DEV Community

Cover image for Integrating Storybook into an existing next.js project
Lay
Lay

Posted on

Integrating Storybook into an existing next.js project

In this article, you'll learn how to integrate Storybook into an existing Next.js project, including support for different themes.

Introduction

Storybook is an open-source tool that allows developers to build, test, and document UI components in isolation.

Install storybook
To get started, initialize Storybook in your project:

npx storybook@latest init
Enter fullscreen mode Exit fullscreen mode

Configure Your Project
Below is an example configuration for integrating Storybook with a Next.js project:

main.ts

// main.ts

import type { StorybookConfig } from '@storybook/nextjs'

import { join, dirname } from 'path'

function getAbsolutePath(value: string): any {
  return dirname(require.resolve(join(value, 'package.json')))
}

const config: StorybookConfig = {
  stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
  addons: [
    getAbsolutePath('@storybook/addon-onboarding'),
    getAbsolutePath('@storybook/addon-essentials'),
    getAbsolutePath('@chromatic-com/storybook'),
    getAbsolutePath('@storybook/addon-interactions'),
  ],
  framework: {
    name: '@storybook/nextjs',
    options: {},
  },
  staticDirs: ['../public'],
}

export default config

Enter fullscreen mode Exit fullscreen mode

preview.ts

// preview.ts

import { Preview } from '@storybook/react'
import { themesNames } from '../src/theme'

// Contexts providers
import { withThemeProvider } from './themeProvider'

import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime'

export const globalTypes = {
  parameters: {
    nextRouter: {
      Provider: RouterContext.Provider,
    },
  },
  theme: {
    name: 'Theme',
    description: 'Global theme for components',
    defaultValue: 'defaultTheme',
    toolbar: {
      icon: 'paintbrush',
      items: themesNames,
      showName: true,
    },
  }
}

const preview: Preview = {
  decorators: [withThemeProvider],
}

export default preview

Enter fullscreen mode Exit fullscreen mode

Note: When configuring Storybook with custom providers, such as themes or global contexts, it’s essential to mock these contexts properly.

Context Providers Setup

themeProvider.tsx

// themeProvider.tsx

import React from 'react'
import { ThemeProvider } from 'styled-components'
import { Decorator } from '@storybook/react'
import { QueryClient, QueryClientProvider } from 'react-query'
import { AppRouterContext } from 'next/dist/shared/lib/app-router-context.shared-runtime'
import { GlobalStyle } from '../src/styles/Global.styles'

const queryClient = new QueryClient()

const themes = {
  // ... all project themes
  // 'projectName' : { ...settings}
}

// Contexts providers
export const withThemeProvider: Decorator = (Story, context) => {
  const selectedTheme = context.globals.theme ?? 'defaultTheme'

  return (
    <QueryClientProvider client={queryClient}>
      <AppRouterContext.Provider
        // Overriding nextjs router functions
        value={{
          push: async () => true,
          replace: async () => true,
          back: () => {},
          prefetch: async () => {},
          forward: () => {},
          refresh: () => {},
        }}
      >
        <ThemeProvider theme={themes[selectedTheme]}>
          <GlobalStyle />
          <Story />
        </ThemeProvider>
      </AppRouterContext.Provider>
    </QueryClientProvider>
  )
}

Enter fullscreen mode Exit fullscreen mode

Example: Button Component

1. Create the Component

Create a new folder for the component at /src/components/button. Inside this folder, add the following file:

/src/components/button/index.tsx

// index.tsx

export default function Button({children, onClick, disabled}){
  return <button onClick={onClick} disabled={disabled}>{children}</button>
}
Enter fullscreen mode Exit fullscreen mode

2. Add Stories

In the same folder, create a file named Button.stories.ts to define the stories for the Button component.

You can use diferents files [js|jsx|mjs|ts|tsx].

/src/components/button/Button.stories.ts

// Button.stories.ts

import type { Meta, StoryObj } from '@storybook/react'
import { fn } from '@storybook/test'

import Button from './'

const meta = {
  title: 'UI/Primary Button',
  component: Button,
  parameters: {
    layout: 'centered',
  },
  tags: ['autodocs'],
  argTypes: {},
  args: { onClick: fn() },
} satisfies Meta<typeof Button>

export default meta
type Story = StoryObj<typeof meta>

export const Primary: Story = {
  args: {
    children: 'Button',
    disabled: false,
  },
  parameters: {
    nextjs: {
      appDirectory: true,
    },
  },
}

export const Disabled: Story = {
  args: {
    children: 'Button Disabled',
    disabled: true,
  },
  parameters: {
    nextjs: {
      appDirectory: true,
    },
  },
}

Enter fullscreen mode Exit fullscreen mode

Run and Build Storybook

To build and run your Storybook environment, use the following commands:

Build Storybook:

  npm run build-storybook
Enter fullscreen mode Exit fullscreen mode

Run Storybook:

  npm run storybook
Enter fullscreen mode Exit fullscreen mode

Finally, you can view Storybook's UI by opening it in your browser. After running npm run storybook, Storybook will be accessible at:

http://localhost:6006
Enter fullscreen mode Exit fullscreen mode

This interface allows you to browse, test, and document your components interactively. Enjoy exploring your UI components with Storybook! 🎉

You should see something like this:
Image description

Top comments (0)