Manuel Schoebel
Wednesday, 21. November 2018

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 = (baseConfig, env, 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.

Storybook with TypeScript

Also the next.js app should still be working fine.

Teile den Artikel, wenn er dir gefallen hat:

Du willst wissen wenn es etwas Neues gibt?
Dann melde dich kurz bei meinem Newsletter an!

Durch Anmeldung bei meinem Newsletter willigst du zur Erfolgsmessung, dem Einsatz des Versanddienstleisters MailChimp und Protokollierung der Anmeldung zu. Mehr Informationen dazu und zu deinen Widerrufsrechten findest du hier in der Datenschutzerklärung.