Setting up a Design System in Storybook with React, Styled Components, Tailwind, and Typescript in 2020
Setup a react app
There are different ways to start a React App depending on your end goal. If your intend to start an Application that will contain some components to be used within this specific project, you could start with:
npx create-react-app my-design-system -template=typescript
cd my-design-system
Since my goal is to create an npm package with the design system to use as a component library for other projects, then I am using the following approach:
mkdir my-design-system
cd my-design-system
yarn init -y yarn add -D react @types/react typescript
Note that in this last example, I am installing the packages as development dependencies, since the end-user will already have to react as a dependency in their project in the future.
Setup the tsconfig.json file
// to initialise typescript config file
yarn tsc --init
With the project folder opened in your code editor, uncomment the following:
//tsconfig.json"target": "es6", // change to es6 instead
"lib": ["dom"],
"jsx": "react", // change it to react instead
"declaration": true,
"declarationMap": true,
"outDir": "./dist", // name a distribution folder "rootDirs": ["src", "stories"],
// in case your stories will be placed inside src, than just use
"rootDir": "src", instead."moduleResolution": "node",
Now, with the project folder opened in your code editor, add a /src folder to the root of your project.
Tailwind Setup
To use Tailwind together with Styled-Components, we will need to install now the following dependencies:
yarn add -D tailwindcss tailwind.macro@next @types/styled-component spostcss-cli autoprefixer postcss-importyarn add styled-components
Once the packages have been installed, initialise tailwind.config.js
with the command: npx tailwind init
This command will create a tailwind.config.js on the root directory and you could now customize Tailwind properties within the needs of our project.
Create the file tailwind.css
in .src/assets/css
directory to add the base Tailwind variables according to your project needs, that call the utilities from Tailwinds package.
//.src/assets/css/tailwind.css
@tailwind base;
@tailwind components; // in case you need the components
@tailwind utilities;
On the root directory, add a new file called postcss.config.js
with the following code:
// postcss.config.jsmodule.exports = {
plugins: [
require('postcss-import'),
require("tailwindcss"),
require("autoprefixer")
]
};
Tailwind will be processed with postcss
, and autoprefixer
will parse the CSS and add vendor prefixes for browser support.
Update your package.json
// package.json"scripts": {
"build:css": "postcss src/assets/css/tailwind.css -o src/assets/css/main.css",
"watch:css": "postcss src/assets/css/tailwind.css -o src/assets/css/main.css -w",
"storybook": "start-storybook -p 8080"
}
Run this command on the terminal so the tailwind file will be generated.
yarn build:css
Place the generated tailwind file in your App component (in case you initiated through create-react-app), or generate an index.tsx
in the .src/ folder.
// index.tsx or App.tsximport './assets/css/tailwind.css';
Let's start with Storybook
First, let’s install the dependencies that we need and do some initial storybook config.
yarn add -D @storybook/react react-docgen-typescript-loader @babel/core babel-loader babel-preset-react-app @storybook/addon-info @storybook/addon-centered
Let’s create a new folder at the root of our project called .storybook
and create a main.js
file inside.
On the main.js
file add the following configuration:
//.storybook/main.jsconst path = require('path');module.exports = {
webpackFinal: async config => {
config.module.rules = [
...config.module.rules,
{
test: /\.(ts|tsx)$/,
include: [path.resolve(__dirname, '..')],
use: [
{
loader: require.resolve("babel-loader"),
options: {
presets: [require.resolve("babel-preset-react-app")]
}
},
require.resolve("react-docgen-typescript-loader")
]
}
],config.resolve.extensions.push('.ts', '.tsx');
return config;
},
};
Now add a second file to the same folder nameconfig.js.
In this config file you could add the addons such as addon-info, to your storybook, more references here.
//config.jsimport { configure, addDecorator } from '@storybook/react';
import { withInfo } from '@storybook/addon-info';
import centered from '@storybook/addon-centered/react';addDecorator(withInfo);
addDecorator(centered);// automatically import all files ending in *.stories.tsxconfigure(require.context('../stories', true, /\.story\.tsx$/), module);
Create a Story
We would need to have at least one component written in our .src/components folder, and one story added to our ./stories folder, so we could later on check if our storybook is running correctly.
Let's then assume we have a Button component sample in our ./src/components/Button/index.tsx
and a Button Style in ./src/components/Button/styles.ts
like this:
//./src/components/Button/index.tsximport React, { ButtonHTMLAttributes } from 'react';
import { Container } from './styles';export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
label: string;
}export const Button: React.FC<ButtonProps> = ({ label, ...rest }) => {
return (
<Container type='button' {...rest}>
{label}
</Container>
);
};
Here we are not using 'export default' because we will want to import the component when published by using: import { Button } from 'my-design-system';
Now, go to your ./src/index.tsx
and export the Button component:
export { Button } from './components/Button';
//./src/components/Button/styles.tsimport styled, { css } from 'styled-components';
import tw from 'tailwind.macro';export const Container = styled.button`
${tw`text-white text-base font-bold text-center bg-indigo-700 rounded-full flex py-4 px-10 focus:outline-none cursor-pointer`} &:disabled {
${tw`opacity-75 cursor-not-allowed`}
} &:hover:not(:disabled) {
${tw`g-indigo-800 border-transparent shadow-xs`}
} &:active:not(:disabled) {
${tw`g-indigo-900 border-transparent shadow-xs`}
}
`;
Note* So, here is where things get interesting. To usetailwind.macro
together with styled-components, and extend the powerful capabilities of using Tailwind in the project by adding the 'tw' tagged template literal, we need to have a few extras steps together with typescript.
Now, if you reached until here, you will notice that typescript will give an error on our ./src/components/Button/styles.ts
since this plugin does not have @types support yet, so to turn this around we have to declare the module manually. In the root directory, create a folder namedtypes
and inside this folder, create another folder named tailwind.macro
. Inside this folder create a file named index.d.ts
with the following code:
declare module 'tailwind.macro' {
export default function tw(string: TemplateStringsArray): string;
}
This should now clear out the previous error in our ./src/components/Button/styles.ts
file.
*Note that in the main.js file in our .storybook folder, we already have a babel-loader instead of the ts-loader, because oftailwind.macro.
Create astories
folder at the root directory.
//.stories/Button.story.tsximport React from "react";
import { storiesOf } from "@storybook/react";import { Button } from "../src";storiesOf("Button", module).add("Default", () => <Button label="Continue" />);
Running storybook
And, there you go! Awesome.
Now if we run yarn storybook
we should get something like this.
BAM! Great job!
Thanks for reading.
If you'd like, this same boilerplate is available on Github: https://github.com/danarocha-br/storybook-react-typescript-tailwind-styled, check it out!