Nextjs Typescript Storybook Setup
In this post I will show you how to setup a basic next.js app that uses TypeScript and also Storybook.
- Create a next.js app
- Add TypeScript
- Add Storybook
Create a next.js app
mkdir my-app
cd my-app
npm init -y
npm i next react react-dom
Add the following scripts into the package.json:
{
...
"scripts": {
"dev": "next",
"build": "next build",
"start": "next start"
}
}
Add a page to the nextjs app:
// pages/index.js
export default () => <div>Welcome to next.js!</div>
Test the nextjs app by running:
npm run dev
Add Typescript
First install the module:
npm i -D @zeit/next-typescript @types/next
Then create a next.config.js in the root folder:
// next.config.js
const withTypescript = require('@zeit/next-typescript')
module.exports = withTypescript()
Now you can rename the pages/index.js
to pages/index.tsx
. When you run npm run dev
you will notice it still works. Now you can use TypeScript in your pages and components.
Add Storybook
The first thing to do is to simply add the base dependencies for storybook:
npm i -D @storybook/react
npm i -D @babel/core babel-loader babel-preset-react-app
Also add some @types:
npm i -D @types/storybook__react @types/node
No you can add the npm script to start storybook:
{
...
"scripts": {
...
"storybook": "start-storybook -p 6006 -c .storybook"
}
}
Running storybook will result in an error since we have not yet created a storybook config file. We add this file within a new folder called .storybook
(note the '.' in the folder name):
// .storybook/config.js
import { configure } from '@storybook/react'
const req = require.context('../components', true, /.stories.tsx$/)
function loadStories() {
req.keys().forEach(filename => req(filename))
}
configure(loadStories, module)
This way we specify to have files with the *.stories.tsx
file ending inside a components
folder. Of course you can specify the location of your story files however you like.
Let's create a simple component and a story next.
// components/Button.tsx
import * as React from 'react'
type Props = {
buttonText: string
}
export default (props: Props) => <button>{props.buttonText}</button>
The story looks like this:
// components/Button.stories.tsx
import * as React from 'react'
import { storiesOf } from '@storybook/react'
import Button from './Button'
storiesOf('Button', module).add('with text', () => {
return <Button buttonText="Hello World" />
})
When we start our storybook now, we will see an issue saying that we need an appropriate loader to handle this file type. In order to solve this issue we need to create a custom webpack config for storybook. Add this webpack.config.js
file inside the .storybook
folder:
const path = require('path')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')
module.exports = ({ config }) => {
config.module.rules.push({
test: /\.(ts|tsx)$/,
loader: require.resolve('babel-loader'),
options: {
presets: [require.resolve('babel-preset-react-app')],
},
})
config.resolve.extensions.push('.ts', '.tsx')
config.plugins.push(
new ForkTsCheckerWebpackPlugin({
async: false,
checkSyntacticErrors: true,
formatter: require('react-dev-utils/typescriptFormatter'),
})
)
return config
}
Make sure to also install this package:
npm i -D fork-ts-checker-webpack-plugin
Story book will now be able to use TypeScript, but it cannot use it from the TypeScript package installed by next.js. So we need to add it:
npm i -D typescript
You will also need a TypeScript config. You can add this one to the root folder. Make sure its named tsconfig.json
:
// tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"allowSyntheticDefaultImports": true,
"baseUrl": ".",
"jsx": "preserve",
"lib": ["dom", "es2017"],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"preserveConstEnums": true,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"target": "esnext"
}
}
Finally to make next.js run without TypeScript issues, you need to setup a .babelrc
file like this:
// .babelrc
{
"presets": ["next/babel", "@zeit/next-typescript/babel"]
}
And that's it. When you start storybook you should see it up and running.
Also the next.js app should still be working fine.