Next.js+Tailwind CSS+Storybookでフロントエンド開発①

はじめに

概要

Next.js+Tailwind CSS+Storybookを使って、コンポーネントベースのフロントエンド開発に挑戦しました。
下記の順番で進めていきます。

  1. 環境構築
  2. Storybookで管理しながらコンポーネントを作成
  3. APIの呼び出し
  4. コンポーネントを組み合わせてレイアウト構築

完成図

宝くじ抽選アプリを作っていきます。

開発環境

私はWSL2で作業しました。
node.jsとnpmのバージョンは以下のとおりです。

$ node -v
v23.2.0
$ npm -v
10.9.0

環境構築

Next.js+TypeScript+Tailwind CSSの用意

Next.jsアプリを作る

・アプリを作りたい場所で下記を実行
・今回はTypeScriptを使いたいので--tsを指定

$ npx create-next-app@latest my-next-app --ts

・コンソールで下記を質問されるのでそれぞれ選択+Enter
・2番目の「Tailwind CSS?」は必ずYes

✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like your code inside a `src/` directory? …  Yes
✔ Would you like to use App Router? (recommended) …  Yes
✔ Would you like to use Turbopack for next dev? … No
✔ Would you like to customize the import alias (@/* by default)? … No

・追加で質問される場合もYesを選択

Need to install the following packages:
create-next-app@15.0.3
Ok to proceed? (y) y

・pacakgeのインストールなど辛抱強く待ちます…

Next.jsアプリを起動、動作確認

・完了後、プロジェクト名のフォルダへ移動(package.jsonがあるところ)
・下記を実行して起動

$ npm run dev

http://localhost:3000 へアクセス
・page.tsxを適当に修正し、リアルタイム更新されることを確認
・今回は下記のようにTailwind CSSのクラス名を追加してみました

<li className="text-red-500">Save and see your changes instantly.</li>

・変更が反映されたのでNext.jsとTailwind CSSの用意完了!

リアルタイム更新されないとき

・package.jsonにあるscriptのdevに「WATCHPACK_POLLING=true」を追記
・Next.jsのファイル変更を検知が、WSL2では期待通りに動作しないことがあるようです
・ポーリング方式(一定間隔でファイル変更をチェックする)にすることで、確実に変更検知するようになりました

"dev": "WATCHPACK_POLLING=true next dev",

Storybookの用意

Storybookをインストール

・package.jsonがある場所に移動
・Storybookをインストール

$ npx storybook init

・下記のように質問されたらYes

Need to install the following packages:
storybook@8.5.1
Ok to proceed? (y)

・こちらもmpacakgeのインストールなど辛抱強く待つ…

Storybookを起動、動作確認

・初回は自動起動しました
・次回以降は下記で起動させればOK

$ yarn storybook

・UIを適当に操作してみます
・Buttonコンポーネントが用意してありました

・Storybookの用意完了!

リアルタイム更新されないとき

・package.jsonにあるscriptのstorybookに「WATCHPACK_POLLING=true」を追記

"storybook": "WATCHPACK_POLLING=true storybook dev -p 6006"

フォルダ構成を整える

CSS置き場

・Next.jsアプリを作成すると、デフォルトで/src/app/globals.cssが生成されます
・今回は/src/cssを用意し、ここにglobals.cssを置くことにします

image置き場

・Next.jsアプリを作成すると、デフォルトで/publicに画像が直接置かれています
・今回は/public/imagesを作成し、ここに画像を置くことにします
・フォントを置く場合も、/public/fonts を用意すれば良さそうです

コンポーネント置き場

・Storybookをインストールすると、デフォルトで/src/storiesにサンプルのコンポーネントが生成されます
・今回は /src/componentsを用意し、ここに作成したコンポーネントを置くことにします
・コンポーネントごとに更にフォルダを作成し、.tsx.stories.tsxを配置します

ページの用意

Next.jsだとApp Routerを使う場合が多いので、宝くじ抽選画面として/src/app/lotteryを作成しました。

上記を踏まえて、以下の構成になりました。

my-next-app
├── public
|   └── images
|       └── logo.svg
├── src
|   ├── app
|   |   ├── lottery
|   |   |   └── page.tsx
|   |   ├── layout.tsx
|   |   └── page.tsx
|   ├── css
|   |   └── global.css
|   └── components
|       └── button
|           ├── button.css (これは後で消す)
|           ├── Button.tsx
|           └── Button.stories.tsx
└── package.json

StorybookにTailwind CSSを適用させる

適用確認のための準備

・サンプルのButtonコンポーネントを↑のように配置しておきます
・その他のサンプルはすべて削除します
・ボタンラベル部分を<span>で囲み、Tailwind CSSのクラス名を記載

<span className='text-red-500'>{label}</span>

/src/app/lottery/page.tsxを作成し、宝くじ抽選画面を用意
・先ほど用意したButtonコンポーネントを呼び出します

"use client";
import { Button } from "../../comopnents/button/Button";

export default function Home() {
    return (
        <div className="p-5">
            <Button primary={false} label="宝くじ抽選ページで呼び出した" />
        </div>
    );
}

適用確認

npm run devでアプリを起動
http://localhost:3000/lotteryへアクセスすると、Buttonコンポーネントのラベルが赤色になっていました↓

しかし、Storybookでは赤色になっていません…!↓

適用させる

global.cssの先頭に「Tailwindを使う」宣言があるので、Storybook側でもこれを読み込む必要があるようです。

@tailwind base;
@tailwind components;
@tailwind utilities;

/.storybook/preview.tsに下記を追記

import "../src/css/globals.css";

・Tailwindsに適用範囲を追加(今回のようにsrc/componentsにコンポーネントを作成する場合は不要)

import type { Config } from "tailwindcss";

export default {
  content: [
    "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
    "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
  ],

・Storybookを起動しなおして確認すると、アプリ側と同じく赤色になっている=Tailwind CSSが適用されました!

Tailwind CSSが反映されない場合

/postcss.config.mjs/postcss.config.jsに変更、中身を下記に変更

module.exports = {
    plugins: {
      tailwindcss: {},
    },
};

おわりに

環境構築は以上です。
次回はコンポーネントを作っていきます。