ARTICLES

Next.js + MDX な環境でも Storybook を選んでみる

2019-05-27

Storybook の画面

そこそこ記事が溜まってきたこのサイトをそろそろ改善していこうと思い、ここらで Storybook を使って UI コンポーネントカタログを作ってみようと思いました。

理由としては、少し見直したい UI コンポーネントがあるのですが、このサイト、Next.js + TypeScript + Bulma + MDX で作ったものの、結構一枚岩みたいな感じで出来ていたりするのです。これはささっと立ち上げたいという要望からそうしているので、後悔はないものの、Storybook を使うことで UI コンポーネント単体での作成をしやすくしていきたいという思いがありました。

さて、Next.js で出来ているサイトは、Next.js がビルド環境も兼ねているため、Next.js とは別に Storybook 環境を作る必要があります。以下には、そのヒントがあります。

webpack-extensions ルールのようなものを作って共有化をするということがヒントとして記載されていますが、細かな設定項目の共有はともかく、Storybook と Next.js のビルド環境についてはわけて構築する必要がありそうです。

以下のサイトも環境構築の参考になりました。

その前に、一つ目の選択肢として、そもそも Storybook を使わないという考え方があります。というのも、このサイトは、MDX によるコンテンツ記述をサポートしており、Storybook のように、JSX にてデザインドキュメントを記載していくことが可能だからです。

同様の考え方に基づいて作られているツールに docz というツールがあります。こちらも MDX を使ってデザインドキュメントを記述することが出来るため、Storybook の競合ツールと言えるでしょう。MDX は、現状の Storybook が持っているドキュメント的な記述面を上回っている (と僕は思っている) ので、このまま自分のサイトにて MDX でデザインドキュメントを書いていくのも手です。

迷った挙句、MDX によるデザインドキュメントに踏み込まず、Storybook を使うことにしたのは、いくつか理由があり以下が主な理由となります。

  • Storybook は、React に限らず広く使われており、汎用性が高い
  • Storybook の AddOn には様々なものがあり、これらを組み合わせることで、MDX ベースのドキュメントよりも機能性が高まる
  • 仕事でも使うので、自分のサイトでも使うことで、より深く見れる

ただ、環境構築が煩雑であるなどのデメリットもあるのですが、とはいえ導入してみようと。Storybook を使う場合、TypeScript を使っていない状態であれば、導入は非常に楽です。TypeScript を使っていて、様々な AddOn を入れ、いろいろな最適化を行なっていこうとするとはまったりします。

現時点の構成

.storybook 以下は、こんな風に各種設定ファイルを置いています。

  • .storybook/
    • addons.js
    • config.js
    • webpack.config.js
    • preview-head.html

やっていることは、外部 CSS などの読み込みとして、preview-head.html を入れていること、

そして、AddOns は以下を設定していること、

import '@storybook/addon-actions/register';
import '@storybook/addon-links/register';
import '@storybook/addon-knobs/register';
import '@storybook/addon-viewport/register';
import '@storybook/addon-storysource/register';
import '@storybook/addon-console';
import '@storybook/addon-notes/register';
import 'storybook-addon-react-docgen/register';

あと、webpack.config.js はこんな感じの設定にしました。

const path = require('path')
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin')

module.exports = ({config, mode}) => {
  config.module.rules.push({
    test: /\.(ts|tsx)$/,
    exclude: /node_modules/,
    include: [/components/, /stories/],
    // 
    use: [
      {
        loader: require.resolve('babel-loader'),
        options: {
          presets: [require.resolve('@babel/preset-react'), require.resolve('@babel/preset-typescript')],
        },
      },
      {
        loader: require.resolve('@storybook/addon-storysource/loader'),
        options: { parser: 'typescript' }
      },
      {
        loader: require.resolve("react-docgen-typescript-loader"),
      },
    ],
  })

  config.module.rules.push({
    test: /\.scss$/,
    use: ['style-loader', 'css-loader', 'sass-loader'],
    include: path.resolve(__dirname, '../'),
  });

  config.resolve.extensions.push('.ts', '.tsx')

  // config.plugins.push(
  //   new ForkTsCheckerWebpackPlugin({
  //     async: false,
  //     checkSyntacticErrors: true,
  //     formatter: require('react-dev-utils/typescriptFormatter'),
  //     memoryLimit: 8192,
  //     workers: 1,
  //     useTypescriptIncrementalApi: true
  //   })
  // )

  return config
}

fork-ts-checker-webpack-plugin については、普通に導入してみると Out of memory エラーとなりました。いろいろなバランスを調節した結果、memoryLimit8192 にすることで自分の環境では問題がなくなりました。この memoryLimit は、サービスプロセスのメモリ制限を決めるもので、単位は MB となります。デフォルトは 2048 です。

このエラーは、結構引っかかる人がいるようで、以下のような issue もありました。

こうすることで問題はなくなったものの、動作が遅いので、現状は Storybook 上ではコメントアウトして、確認しています。おいおい、最適化していきたいです。


ARTICLES


    AUTHOR

    原 一浩 の顔写真

    (はら) 一浩(かずひろ)

    カンソクインダストリーズ代表 / グレーティブ合同会社代表

    1998年に独立し、同年、ウェブデザイン専門のメールメディア DesignWedgeの発行を開始。Webデザイン業の傍ら、海外のWebデザインに関する情報発信を行う。
    雑誌への寄稿多数。主な著書に『はじめてのフロントエンド開発』『プロセスオブウェブデザイン』、『Play framework徹底入門』、『ウェブデザインコーディネートカタログ』など。自社製のWebデザインのクロール&キャプチャシステムvaqumをベースに、様々なリサーチを行っている。Web 検定プロジェクトメンバー

    著作一覧はこちら