Turborepo

Storybook

Storybook is a popular way to build UI components in an isolated environment. By putting Storybook into your Turborepo, you can easily develop your design system right alongside your applications.

Quickstart

If you'd rather use a template, this guide is walking through how to build this Storybook/Turborepo template on Vercel.

Terminal
npx create-turbo@latest -e design-system

Guide

Create a monorepo

If you don't have an existing project, use create-turbo to create a new monorepo:

Terminal
npx create-turbo@latest

Create a directory for the app

You'll need a directory for the Storybook application:

Terminal
mkdir apps/storybook
cd apps/storybook

Add the Storybook application

In the apps/storybook directory, initialize a new Storybook application:

Terminal
npx storybook@latest init

Follow the prompts to create an application. For the rest of this guide, we'll assume React and TypeScript.

Good to know:

After going through Storybook's onboarding, you can uninstall the onboarding addon.

Add your UI kit to Storybook

Now, install your UI package into Storybook.

Terminal
npm install @repo/ui --workspace=storybook

Set up a story for your Button component

Delete the stories and components found in src/stories that were created by the Storybook scaffolding tool. You will be making your own.

As an example, here is a story for the Button component from @repo/ui/button.

./apps/storybook/src/stories/Button.stories.tsx
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from '@repo/ui/button';
 
const meta = {
  title: 'Example/Button',
  component: Button,
  tags: ['autodocs'],
} satisfies Meta<typeof Button>;
 
export default meta;
type Story = StoryObj<typeof meta>;
 
export const Primary: Story = {
  args: {
    appName: 'Button',
    children: 'I am a primary button.',
  },
};

Align scripts to your tasks

Last, integrate the new Storybook application into your Turborepo:

apps/storybook/package.json
{
  "scripts": {
    "dev": "storybook dev -p 6006", 
    "build": "storybook build"
  }
}

These scripts will now run with the turbo dev and turbo build tasks in your turbo.json.

To ensure file outputs are cached when you run build, add storybook-static to the outputs of your turbo.json build task:

turbo.json
{
  "tasks": {
    "build": {
      "outputs": [
        ".next/**",
        "!.next/cache/**"
+       "storybook-static/**"
      ]
    }
  }
}

Add Storybook build outputs to .gitignore

Ensure that the build outputs for Storybook are not committed to source control

.gitignore
+ storybook-static

Verify your configuration

Run turbo build to build the Storybook application alongside the rest of your applications.

You can also run turbo build again to see cache hits for your builds.

More tips

Co-locating stories

If you'd prefer to co-locate your stories to their source code (rather than having them in the Storybook application), you'll need some extra configuration.

Re-configure Storybook sources

In .storybook/main.ts, change the stories paths in config to the directories you'd like to capture. For instance, if you'd like to write stories in the UI package:

./apps/storybook/.storybook/main.ts
 
const config = {
  stories: [
-   "../src/**/*.mdx",
-   "../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"],
+   "../../../packages/ui/src/**/*.stories.@(js|jsx|mjs|ts|tsx)",
};

Move story files to the UI package

Following along with the guide above, move the ./apps/storybook/src/stories/Button.stories.tsx file to ./packages/ui/src/Button.stories.tsx.

Update components imports so that they reference the now co-located modules. For instance, in the story's imports:

./packages/ui/src/Button.stories.tsx
- import { Button } from "@repo/ui/button";
+ import { Button } from "./button";

You'll also need to install any Storybook packages required for writing stories. For example, moving the story from above would require that you install @storybook/react into your @repo/ui package.

Terminal
npm install @storybook/react --workspace=@repo/ui --save-dev

Configure caching

Because stories are now in the UI package, changes to those stories can cause cache misses for any builds that depend on your UI package. However, changing a story doesn't mean your production applications should miss cache.

To prevent this, exclude stories from the inputs to your build task in your root turbo.json. You'll also need to create a build:storybook task, which you'll need in a moment:

./turbo.json
{
  "tasks": {
    "build": {
      "dependsOn": ["^build"],
      "inputs": ["$TURBO_DEFAULT$", "!**/*.stories.{tsx,jsx,mdx}"], 
      "outputs": [".next/**", "!.next/cache/**"]
    },
    "build:storybook": {} 
  }
}

Additionally, create a Package Configuration in the storybook application so stories are accounted for in building the Storybook application, specifically:

./apps/storybook/turbo.json
{
  "extends": ["//"],
  "tasks": {
    "build:storybook": {
      "dependsOn": ["^build:storybook"],
      "outputs": ["storybook-static/**"]
    }
  }
}

Good to know:

If you are using the Compiled Package pattern, you may also need to add ^build to your dependsOn.

Rename the build script

Last, make sure your script to build Storybook uses the configuration we just wrote by renaming it to the name of the task:

apps/storybook/package.json
{
  "scripts": {
    "dev": "storybook dev -p 6006",
    "build:storybook": "storybook build"
  }
}

The script that was once "build" is now "build:storybook" to ensure the stories are included in hashes for caching.

Verify your configuration

To ensure your setup is correct:

  1. Run turbo build:storybook build. You should see cache misses.
  2. Run turbo build:storybook build again. You should see all cache hits.
  3. Make a code change to a story in your @repo/ui package.
  4. Run turbo build:storybook build again. You should only see a cache miss for the Storybook application. All others should hit cache.

Adding CSS

If your UI package exports its own CSS, you'll need to add it to the renders in the Storybook app, similar to how you would add it to your applications. The Storybook documentation recommends you add it to the .storybook/preview.ts file.

hours

Total Compute Saved
Get started with
Remote Caching →

On this page