| Next.Js / Storybook / I18N / Typescript

Configuring Storybook for Next.Js, I18n, SCSS & TypeScript

Storybook is great. It lets you design, tweak and test your components in complete isolation. Integrating it is usually not that big of a deal, but there are some hoops when it comes to integrating into an existing framework, such as NextJs.

Quick start

In order to add Storybook to your project, as the official quick start guide suggests, simply run:

npx -p @storybook/cli sb init

This will take care of the initial setup. It’ll:

  • add the packages
  • add the package scripts
  • add the config files under .storybook

Environment variables

Chances are, you’re storing sensitive information (and probably not so sensitive as well) in a .env file. Next knows about it, via next.config.js, and takes care of exposing this information to the client code. However, since next isn’t running side-by-side with Storybook, this task falls onto us. Fortunately, we don’t have to do much. We can make the publicRuntimeConfig available by navigating to your storybook configuration in .storybook/config.js and adding this piece of code:

import env from '../env'
import { setConfig } from 'next/config'

setConfig(env)

Header links

Using custom fonts? CDN linked stylesheets? Not a problem. All you need to do is to create a preview-head.html file inside your .storybook folder and add all your head contents there.

TypeScript

Your stories are going to be built with Webpack, so all you need here is a standard webpack configuration for TypeScript. First, create webpack.config.js file in your .storybook folder. Then, add the webpack config, like so:

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')
  return config
}

SCSS

SCSS configuration could not be easier with this handy package. Setup is very straightforward, so I’ll just link it:

https://www.npmjs.com/package/@storybook/preset-scss

I18n

Translations! Gotta love ’em. There are a number of flavors of i18n, but since we’re talking Next, I’ll assume you’re using Next-I18Next. You obliously won’t need the SSR capabilities the package provides, but you probably don’t want to install another translation package just for Storybook. That’s alright, we can make it work. First, if you’re using module.exports in your i18n configuration, change it to ES6 style. This is necessary since you’ll need to export individual parts of your NextI18Next instance, and the two export styles should not be mixed. Once you have:

export default NextI18NextInstance

Also export:

export const {
  appWithTranslation,
  Link,
  Trans,
  Router,
  config,
  withTranslation,
  i18n,
} = NextI18NextInstance

Currently next-i18next depends on a middleware to load namespaces. Since that runs on server side, we don’t have access to said middleware. So, what do we do? Well, what we can do, is that we can invoke the translation service client side. First, we have to expose the static directory containing your translations publicly (as Next does). That’s easily done, just modify your storybook script in package.json to:

"storybook": "start-storybook -s ./static/ -p 6006"

Then, in order to invoke the translation service, you’ll need to wrap your component with appWithTranslation once you write your stories, like so:

import { appWithTranslation } from 'lib/i18n'

const ComponentWithTranslation = appWithTranslation(Component)

storiesOf('Component', module).add('withSampleData', () => (
  <ComponentWithTranslation data={sampleData} />
))

Material UI

If you have a project that heavily uses Material-UI, you’ll want your stories to have access to it’s predefined themes. This can be done on the story level, or globally as well. You could leverage storybook-material-ui however, at the time of writing, it only handles palette definitions and custom typography, but not component level overrides. If you need that, like I did, you’ll need to add the provider decorator manually. Create a MuiDecorator file somewhere in your project:

import { ThemeProvider } from '@material-ui/core/styles'
import muiTheme from 'theme'

const MuiDecorator = storyFn => <ThemeProvider theme={muiTheme}>{storyFn()}</ThemeProvider>

export default MuiDecorator

Then, in your storybook config file, import addDecorator from @storybook/react, and call:

addDecorator(MuiDecorator)

before your configure.

Hope this quick guide helps you getting Storybook up and running!

Akos Radler

Akos Radler

Full Stack .Net / React developer

Read More