Next.js and Tailwind are popular frameworks in modern web development. I’ve used them in some projects and really happy with the result. Storybook is one library to create component documentation which can be integrated to project written in many UI libraries. It provides many functionalities to enhance our documentation. Let’s try to combine these libraries to set up our next web project.
Setup Next.js
First of all, let’s initiate the project using Next.js:
- Initiate project and name it as you want
npx create-next-app@latest --typescript
We will use TypeScript for the rest of article. More setup options visit Next.js documentation.
- Go to the project directory, then install additional ESLint plugins
yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-plugin-simple-import-sort
- Update
.eslintrc.json
1{ 2 "extends": ["next/core-web-vitals", "plugin:@typescript-eslint/recommended"], 3 "plugins": ["@typescript-eslint", "simple-import-sort"], 4 "overrides": [ 5 { 6 "files": ["**/*.js"], 7 "rules": { 8 "@typescript-eslint/no-var-requires": "off" 9 } 10 } 11 ], 12 "rules": { 13 "@typescript-eslint/comma-dangle": ["error", "always-multiline"], 14 "@typescript-eslint/member-delimiter-style": [ 15 "error", 16 { 17 "multiline": { 18 "delimiter": "semi", 19 "requireLast": true 20 }, 21 "singleline": { 22 "delimiter": "semi", 23 "requireLast": false 24 } 25 } 26 ], 27 "@typescript-eslint/no-shadow": "error", 28 "@typescript-eslint/semi": "error", 29 "import/newline-after-import": "error", 30 "import/no-duplicates": "error", 31 "no-console": "warn", 32 "no-multi-spaces": [ 33 "error", 34 { 35 "ignoreEOLComments": true, 36 "exceptions": { 37 "VariableDeclarator": true 38 } 39 } 40 ], 41 "no-multiple-empty-lines": [ 42 "error", 43 { 44 "max": 1, 45 "maxBOF": 0, 46 "maxEOF": 0 47 } 48 ], 49 "no-trailing-spaces": "error", 50 "quotes": ["error", "single", { "avoidEscape": true }], 51 "react/jsx-curly-brace-presence": [ 52 "error", 53 { 54 "props": "never", 55 "children": "never" 56 } 57 ], 58 "react/jsx-sort-props": "error", 59 "simple-import-sort/imports": [ 60 "error", 61 { 62 "groups": [ 63 // Custom grouping: https://github.com/lydell/eslint-plugin-simple-import-sort#custom-grouping 64 // Side effect imports. 65 ["^\\u0000"], 66 // Node.js builtins prefixed with `node:`. 67 ["^node:"], 68 // `react` and `next` related packages. 69 ["^react", "^next"], 70 // Packages 71 // Things that start with a letter (or digit or underscore), or `@` followed by a letter. 72 ["^@?\\w"], 73 // Absolute imports for internal dependencies. 74 ["^components", "^core", "^hooks", "^providers", "^utils"], 75 // Anything not matched in another group. 76 ["^"], 77 // Relative imports. Anything that starts with a dot. 78 ["^\\."] 79 ] 80 } 81 ], 82 "simple-import-sort/exports": "error" 83 } 84}
- Install
prettier
yarn add --dev --exact prettier
- Add
.prettierrc.js
1module.exports = { 2 singleQuote: true, 3 trailingComma: 'all', 4};
- Add
.prettierignore
1# next.js 2/.next/ 3/out/ 4 5# production 6/build
- Add
prettier
script topackage.json
1{ 2 "scripts": { 3 ... 4 "format": "prettier --write", 5 ... 6 } 7}
- Try running the dev server
yarn dev
By default, Next.js runs on
http://localhost:3000
.
Integrate Tailwind into Next.js
Tailwind provides a comprehensive guideline for many frameworks. Here’s to install Tailwind into Next.js.
- Install dependencies
yarn add -D tailwindcss postcss autoprefixer npx tailwindcss init -p
This step generates
tailwind.config.js
andpostcss.config.js
. - Update
tailwind.config.js
with the following content:1/** @type {import('tailwindcss').Config} */ 2module.exports = { 3 content: [ 4 './components/**/*.{js,ts,jsx,tsx}', 5 './pages/**/*.{js,ts,jsx,tsx}', 6 ], 7 theme: { 8 extend: {}, 9 }, 10 plugins: [], 11}
- Replace the content of
styles/global.css
with Tailwind directives1@tailwind base; 2@tailwind components; 3@tailwind utilities;
- Install additional libraries for Tailwind
yarn add -D @tailwindcss/nesting prettier-plugin-tailwindcss
Visit nesting and prettier plugin documentation for more information.
- Update
postcss.config.js
1module.exports = { 2 plugins: { 3 'tailwindcss/nesting': {}, 4 tailwindcss: {}, 5 autoprefixer: {}, 6 }, 7};
- Update
.prettierrc.js
1module.exports = { 2 singleQuote: true, 3 trailingComma: 'all', 4 plugins: [require('prettier-plugin-tailwindcss')], 5};
- Start using Tailwind in
pages/index.tsx
1import type { NextPage } from "next"; 2import Head from "next/head"; 3 4const Home: NextPage = () => { 5 return ( 6 <> 7 <Head> 8 <title>Next.js Tailwind</title> 9 <meta name="description" content="Generated by create next app" /> 10 <link rel="icon" href="/favicon.ico" /> 11 </Head> 12 13 <div className="absolute left-0 top-64 -z-10 h-72 w-72 rounded-full bg-primary-500 opacity-10 blur-3xl"></div> 14 <div className="absolute right-0 top-24 -z-10 h-72 w-72 rounded-full bg-danger-500 opacity-10 blur-3xl"></div> 15 16 <section className="container mx-auto py-16 px-6 text-center sm:py-40"> 17 <h1 className="mx-auto w-3/4 pb-10 text-4xl font-bold sm:text-5xl"> 18 Next.js Tailwind 19 </h1> 20 <p>A great combination of Next.js and Tailwind.</p> 21 </section> 22 </> 23 ); 24}; 25 26export default Home;
- Try
format
andlint
command to verify if ESLint and Prettier are configured correctlyyarn format yarn lint
- Restart dev server to see the result
yarn dev
Install Storybook
When our project grows and we have a lot of components, making sure there’s no duplication becomes harder if we don’t have proper component documentation. Storybook is a library to create component documentation. It can be integrated to project written in many UI libraries and provides useful functionalities.
- Initiate Storybook
npx storybook init
Within this step, Storybook checks project dependencies and decides the best configuration. Also, providing examples of stories.
- Type
y
if prompted with the messageNeed to install the following packages: storybook Ok to proceed?
. - Type
y
if prompted with the messageDo you want to run the 'eslintPlugin' migration on your project?
. Within this step, Storybook installs ESLint plugin needed. - Install additional libraries
yarn add -D storybook-addon-next postcss-loader
- Configure
.storybook/main.js
1const path = require('path'); 2 3module.exports = { 4 // load stories from any directories including MDX files 5 stories: [ 6 './**/*.stories.@(js|jsx|ts|tsx|mdx)',, 7 '../**/*.stories.@(js|jsx|ts|tsx|mdx)', 8 ], 9 addons: [ 10 '@storybook/addon-links', 11 '@storybook/addon-essentials', 12 '@storybook/addon-interactions', 13 'storybook-addon-next', 14 ], 15 framework: '@storybook/react', 16 core: { 17 builder: '@storybook/builder-webpack5', 18 }, 19 webpackFinal: async (config) => { 20 // use tailwind via postcss-loader 21 config.module.rules.push({ 22 test: /\.css$/, 23 use: ['postcss-loader'], 24 include: path.resolve(__dirname, '../'), 25 }); 26 27 // define aliases for each directory not provided by Next.js by default 28 config.resolve.alias = { 29 ...config.resolve.alias, 30 components: path.resolve(__dirname, '../components'), 31 }; 32 33 return config; 34 }, 35};
- Configure
.storybook/preview.js
1import '../styles/globals.css'; 2 3export const parameters = { 4 actions: { argTypesRegex: "^on[A-Z].*" }, 5 controls: { 6 matchers: { 7 color: /(background|color)$/i, 8 date: /Date$/, 9 }, 10 }, 11 viewMode: 'docs', 12}
I personally prefer
docs
mode by default because it looks nice :). If canvas mode is preferable, just removeviewMode: 'docs'
. - Add these items to
.eslintignore
1!.storybook 2.next 3node_modules
- Then, extend
eslint-plugin-storybook
in.eslintrc.json
1{ 2 "extends": [ 3 ... 4 5 "plugin:storybook/recommended" 6 ], 7 ... 8}
- Create component sample
components/Button/Button.tsx
1import NextLink from 'next/link'; 2import { useRouter } from 'next/router'; 3 4interface ButtonProps extends React.HTMLProps<HTMLButtonElement> { 5 href?: string; 6} 7 8export default function Button({ children, href, onClick }: ButtonProps) { 9 const router = useRouter(); 10 const linkActive = router.asPath === href; 11 12 if (href) { 13 return ( 14 <NextLink href={href || '/'}> 15 <a 16 className={`underline decoration-2 underline-offset-2 ${ 17 linkActive 18 ? 'font-bold decoration-red-500' 19 : 'decoration-violet-500' 20 }`} 21 > 22 {children} 23 </a> 24 </NextLink> 25 ); 26 } 27 28 return ( 29 <button 30 className="rounded-md border-0 bg-violet-500 px-12 py-2 text-base text-white shadow-lg shadow-violet-300 transition hover:bg-violet-600 hover:shadow-violet-400" 31 onClick={onClick} 32 > 33 {children} 34 </button> 35 ); 36}
- Create story sample
components/Button/Button.stories.tsx
1import { ComponentMeta, ComponentStory } from '@storybook/react'; 2 3import Button from './Button'; 4 5export default { 6 title: 'Button', 7 component: Button, 8} as ComponentMeta<typeof Button>; 9 10const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />; 11 12export const Playground = Template.bind({}); 13Playground.args = { 14 children: 'Click here', 15}; 16 17export const Link = Template.bind({}); 18Link.args = { 19 children: 'Click here', 20 href: '/my-link', 21}; 22Link.parameters = { 23 nextRouter: { 24 path: '/', 25 }, 26}; 27 28export const ActiveLink = Template.bind({}); 29ActiveLink.args = { 30 children: 'Click here', 31 href: '/my-link', 32}; 33ActiveLink.parameters = { 34 nextRouter: { 35 asPath: '/my-link', 36 }, 37};
In the example above, we are simulating the router. Visit addon documentation for more capabilities.
- Create
.storybook/Introduction.stories.mdx
which add README.md as an introduction in the storybook1import { Description, Meta } from '@storybook/addon-docs'; 2import Readme from '../README.md'; 3 4<Meta title="Introduction" /> 5 6<Description markdown={Readme} />
- Delete examples from the Storybook which are located in
stories
directory. - Try running the storybook
yarn storybook
By default, Storybook runs on http://localhost:6006.
Congrats! Your web project is ready. Let's build a beautiful web app by adding more components and stories. I also created a project starter based on this article which you can use to speed up the project set-up.