Table of Contents
This article is a collection of notes stemming from my Build React UI Youtube Series. These notes aren’t meant to serve as a standalone solution but rather as notes to use while following along with the series – or to refer back to.
⚠️ These notes are very much Work In Progress
Setup UI
For the first video, we setup the React UI library by using vite library mode
.
Steps 1, install Vite:
npm create vite@latest
React + TypeScript
cd [into project]
npm install && npm run dev # runs the project
Step 2, setup library mode with config:
import { defineConfig } from 'vite'
import { resolve } from 'path'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
build: {
// library entry (src/main.tsx)
lib: {
entry: resolve(__dirname, 'lib/main.tsx'),
name: 'react-ui-library',
fileName: 'react-ui-library' // this will add extensions automatically
},
// import react and react-dom as external
rollupOptions: {
external: ['react', 'react-dom', 'react/jsx-runtime'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
'react/jsx-runtime': 'react/jsx-runtime'
}
}
}
}
})
Follow rest of guide https://vitejs.dev/guide/build:
- create lib/main
- create lib/components/Button
- update package.json and make sure to use the correct lib name
- test build
Package.json:
{
"name": "react-ui-library",
"private": true,
"version": "0.0.0",
"type": "module",
"files": ["dist"],
"main": "dist/react-ui-library.cjs",
"module": "dist/react-ui-library.js",
"exports": {
".": {
"import": "./dist/react-ui-library.js",
"require": "./dist/react-ui-library.umd.cjs"
}
},
"scripts": {
"dev": "vite",
"prebuild": "rm -rf build",
"build": "tsc && vite build",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"prepublish": "npm run build"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.2.55",
"@types/react-dom": "^18.2.19",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@vitejs/plugin-react": "^4.2.1",
"eslint": "^8.56.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"typescript": "^5.2.2",
"vite": "^5.1.0"
}
}
Step 3, ts config paths:
https://www.npmjs.com/package/vite-tsconfig-paths
install:
npm install vite-tsconfig-paths
Update Vite and TS configs:
TSConfig should reference the paths – directly path to main.tsx
https://www.typescriptlang.org/tsconfig#paths
Step 4, test it!
- import a component
- render it!
- preview it using
npm run dev
Tailwind and Storybook
Step 1: install tailwind
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
/** @type {import('tailwindcss').Config} */
export default {
content: [
// reference the library _only_
"./lib/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Step 2: create ./lib/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Import it in main.tsx
Step 3: test Tailwind!
- add a color to the button
- publish it
- install it and test it in a separate project
Step 4: Storybook
https://storybook.js.org/blog/storybook-for-vite/
Initialize storybook for vite
npx sb init --builder @storybook/builder-vite
Run it for the first time:
npm run storybook
Step 5: Setup storybook for our usecase
Delete ./src/stories
Adjust the storybook/main.ts
file
import type { StorybookConfig } from "@storybook/react-vite";
const config: StorybookConfig = {
// our lib paths
stories: ["../lib/**/*.mdx", "../lib/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-onboarding",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/react-vite",
options: {},
},
docs: {
autodocs: "tag",
},
};
export default config;
Start storybook
npm run storybook
Step 6: Add First Story
import type { Meta, StoryObj } from '@storybook/react'
import { Button } from './Button'
const meta: Meta<typeof Button> = {
component: Button,
}
export default meta
type Story = StoryObj<typeof Button>
export const Default: Story = () => <Button>Click me</Button>
It should work! :D
Step 7: Add Tailwind to Storybook
I think this is where the complexity starts to get ridiculous and it’ll get even more ridiculous. We’re going to add Tailwind to Storybook.
This steps feels like crossing wires but I don’t know a better solution yet. Import tailwind.css
in .storybook/preview.js
import '../lib/tailwind.css'
Export Styles And Types
In this video, we will export CSS and Types for our React UI Library project for target applications to use when importing Cottage UI.
Resources:
Styles and types aren’t getting exported yet which hampers our ability to use this UI library. Let’s go ahead and add that.
Reinstall command
Add this command to your package.json for easy reinstall:
"reinstall": "rm -rf node_modules && npm install"
Styles
Add the style export and mark side effects as false in package.json
.
"exports": {
"./dist/style.css": "./dist/style.css"
},
"sideEffects": false
Target app needs to import it:
import 'cottage-ui/dist/style.css'
Why?
Could not get things to work correctly with other solutions and this one seemed to make the most sense – it imports TailwindCSS. This really depends on how you want to structure your CSS.
Since this is supposed to be an internal library, I prefer the simplicity of the solution over having to import an additional file
Types
https://www.npmjs.com/package/vite-plugin-dts
npm install --save-dev vite-plugin-dts
add plugin:
import dts from 'vite-plugin-dts'
plugins: [dts({ rollupTypes: true })]
add include into typescript:
includes: ['lib']
Run it!
How about tree-shaking?
We’ll handle that in the future.