如何使用 Next.js 和 Tailwind CSS 设置 Storybook

2025-06-04

如何使用 Next.js 和 Tailwind CSS 设置 Storybook

介绍

Storybook 是维护和预览独立组件的绝佳方式。我通常将其添加为一个“锦上添花”的功能。在设置过程中,我发现关于使用 Next.js 和 Tailwind CSS 设置 Storybook 的资源非常稀缺。它存在大量 bug,寻找解决方案如同大海捞针。希望本教程能帮助您顺利完成设置。

版本

本分步教程是使用Storybook v6.4.9 和 TypeScript编写的,如果您发现本教程在一些小更新后无法使用,请在下面发表评论。

初始化 Storybook

此命令将把 storybook 安装到你的 Next.js 存储库中,将会有一个提示询问你是否要另外安装 eslint 插件,我建议你接受。

npx -y sb init --builder webpack5
Enter fullscreen mode Exit fullscreen mode

安装 PostCSS 插件

Tailwind CSS 需要 PostCSS 才能工作,因此我们需要使用其预先构建的附加组件之一将其与 Storybook 集成。

yarn add -D @storybook/addon-postcss
Enter fullscreen mode Exit fullscreen mode

添加 Webpack 作为解析依赖项

我们需要这个来确保 webpack 作为依赖项安装,如果我们不安装它,这将导致错误

将其附加到你的 package.json

// package.json

"resolutions": {
    "webpack": "^5"
}
Enter fullscreen mode Exit fullscreen mode

然后使用以下命令安装 webpack 分辨率

yarn
Enter fullscreen mode Exit fullscreen mode

代替.storybook/main.js

这是您可以使用的自定义 main.js 配置

// .storybook/main.js

const path = require('path');

module.exports = {
  stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
  staticDirs: ['../public'],
  addons: [
    '@storybook/addon-links',
    '@storybook/addon-essentials',
    {
      /**
       * NOTE: fix Storybook issue with PostCSS@8
       * @see https://github.com/storybookjs/storybook/issues/12668#issuecomment-773958085
       */
      name: '@storybook/addon-postcss',
      options: {
        postcssLoaderOptions: {
          implementation: require('postcss'),
        },
      },
    },
  ],
  core: {
    builder: 'webpack5',
  },
  webpackFinal: (config) => {
    /**
     * Add support for alias-imports
     * @see https://github.com/storybookjs/storybook/issues/11989#issuecomment-715524391
     */
    config.resolve.alias = {
      ...config.resolve?.alias,
      '@': [path.resolve(__dirname, '../src/'), path.resolve(__dirname, '../')],
    };

    /**
     * Fixes font import with /
     * @see https://github.com/storybookjs/storybook/issues/12844#issuecomment-867544160
     */
    config.resolve.roots = [
      path.resolve(__dirname, '../public'),
      'node_modules',
    ];

    return config;
  },
};
Enter fullscreen mode Exit fullscreen mode

我已经通过链接提供了一些有关错误修复的评论。

确认stories它们staticDirs是否与您的文件夹结构匹配。通过指定,staticDirs我们可以使用公共文件夹中的资产。

代替.storybook/preview.js

// .storybook/preview.js

import '../src/styles/globals.css';
import * as NextImage from 'next/image';

const OriginalNextImage = NextImage.default;

Object.defineProperty(NextImage, 'default', {
  configurable: true,
  value: (props) => <OriginalNextImage {...props} unoptimized />,
});

export const parameters = {
  actions: { argTypesRegex: '^on[A-Z].*' },
  controls: {
    matchers: {
      color: /(background|color)$/i,
      date: /Date$/,
    },
  },
  previewTabs: {
    'storybook/docs/panel': { index: -1 },
  },
};
Enter fullscreen mode Exit fullscreen mode

我喜欢将文档面板设为默认,所以我添加了这个previewTabs配置。您可以根据需要将其移除。

该文件将加载 Tailwind CSSglobals.css并模拟NextImage与 Storybook 协同工作。

运行故事书

运行yarn storybook以启动开发服务器。

然后你就可以开始添加一些故事,这里有一个例子

故事示例

// src/components/buttons/__stories__/Button.stories.tsx

import { ComponentMeta, ComponentStory } from '@storybook/react';
import * as React from 'react';
import { HiSearch } from 'react-icons/hi';

import Button from '@/components/buttons/Button';

export default {
  title: 'Components/Buttons/Button',
  component: Button,
  argTypes: {
    children: {
      control: { type: 'text' },
    },
  },
} as ComponentMeta<typeof Button>;

const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;

export const Default = Template.bind({});
Default.args = {
  children: 'Button',
  variants: 'primary',
};

export const WithIcon = Template.bind({});
WithIcon.args = {
  children: (
    <div className='flex gap-2 items-center'>
      <HiSearch />
      <span>Search</span>
    </div>
  ),
};
Enter fullscreen mode Exit fullscreen mode

类型定义

type ButtonProps = {
  /** Button children element */
  children: React.ReactNode;
  /** Show loading spinner and disable button */
  isLoading?: boolean;
  /** Button color variant */
  variants?: 'primary' | 'secondary';
  /** Disable the button and add not-allowed cursor */
  disabled?: boolean;
} & React.ComponentPropsWithoutRef<'button'>;
Enter fullscreen mode Exit fullscreen mode

如果您使用的是 TypeScript,则可以直接将 JSDoc 添加到类型定义中,它将自动生成 props 描述🤯

截屏

图片描述

惊人的。

在 Vercel 上部署

要在 Vercel 上部署,您需要使用 Next.js 页面创建单独的部署,然后添加此自定义配置

图片描述

讨厌配置?使用我的启动器和扩展器

这是一个电池启动器,只需使用终端中的单个命令即可安装扩展。

图片描述

查看ts-nextjs-tailwind-starter

故事书扩展

我创建这个扩展是为了方便配置项目,它可以完成以上所有教程,适合与我的入门版一起使用,你可以将它用在现有项目中。不保证✌

curl -s https://raw.githubusercontent.com/theodorusclarence/expansion-pack/main/storybook/trigger.sh | bash -s
Enter fullscreen mode Exit fullscreen mode

扩展包存储库

噗噗声发生器

扩展包还配备了一个plop生成器,可轻松为您的组件创建故事书文件


最初发布在我的个人网站上,在我的网站上可以找到更多博客文章代码片段库,以便于访问🚀

喜欢这篇文章吗?订阅我的新闻通讯,每次发布新文章时都会收到通知!

文章来源:https://dev.to/theodorusclarence/step-by-step-how-to-setup-storybook-with-nextjs-and-tailwind-css-lf
PREV
为什么 JavaScript 框架中的高效 Hydration 如此具有挑战性
NEXT
Spring Boot - 速成课程