React Router v7 セットアップ手順 - Biome, Vitest, shadcn/ui, Storybook
私の好きなフレームワークである Remix が React Router に統合されました。
🎉 Remix v2 → React Router v7
Rimix の最新版である react router v7 のセットアップ方法についてメモを残しておきます。
👇ソースはこちら
プロジェクトを作成
フレームワークとしてreact-router
を利用するためのコマンドを実行します。
npx create-react-router@latest react-router-v7-boilerplate
git Initialize a new git repository? Yes
deps Install dependencies with npm? Yes
フォーマッター/リンター設定(Biome)
eslintとprettierは使わず、フォーマッターとリンターを兼ねているBiome(バイオーム)を利用します。
1. パッケージをインストール
npm install --save-dev --save-exact @biomejs/biome
2. 設定ファイルを生成
npx @biomejs/biome init
3. VSCode用の拡張機能を追加
①拡張機能をインストール
②.vscode/settings.json
に設定を追加
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.codeActionsOnSave": {
"quickfix.biome": "always",
"source.organizeImports.biome": "always"
}
}
③biome.json
の設定を修正
{
"$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
"vcs": {
"enabled": true,
"clientKind": "git",
"useIgnoreFile": true
},
"files": {
"ignore": ["**/node_modules/**"]
},
"formatter": {
"indentStyle": "space",
"ignore": []
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"nursery": { "useSortedClasses": "warn" },
"correctness": {
"noUnusedImports": "warn",
"noUnusedVariables": "warn"
}
}
},
"organizeImports": {
"enabled": true
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"semicolons": "always"
}
},
"overrides": [{
"include": ["app/components/shadcn/ui/**"],
"linter": {
"rules": {
"nursery": { "useSortedClasses": "off" }
}
}
}]
}
4. コードチェック & 修正用のスクリプトを追加
"scripts": {
// ...
"--- CHECK SECTION ---": "--- --- --- --- ---",
"biome:check:write": "npx biome check --write",
VSCodeのデバッグ環境構築
VSCode上でフロントエンドとバックエンドを同時にデバッグ可能とするため、.vscode/launch.json
を作成してデバッグ設定を追加します。
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Backend",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev",
"cwd": "${workspaceFolder}"
},
{
"name": "Debug Frontend",
"type": "chrome",
"request": "launch",
"url": "http://localhost:5173/",
"webRoot": "${workspaceFolder}/app",
"sourceMaps": true
},
{
"name": "Debug Backend and Frontend",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev",
"cwd": "${workspaceFolder}",
"serverReadyAction": {
"action": "startDebugging",
"pattern": "Local: +https?://.+",
"name": "Debug Frontend"
}
},
]
}
デバッグと実行で「Debug Backend and Frontend」を実行すれば、フロントエンドとバックエンド両方のブレークポイントが利用可能となります。
参考にした記事はこちらです。
vite.config.tsで環境変数を扱えるように変更
デプロイ時のPORT指定に備えて、PORTを環境変数で指定可能なようにしておきます。
1. .envを作成
PORT=5173
2. .gitに.envが含まれないように設定
# ...既存の設定
# env
.env
.env
をgitに含まないようにすると、必要な環境変数が分からなくなるため、.env
と同じ構造でダミーの値を設定した.env.example
を作成しておきます。
# --- 開発時は、このファイルを `.env` ファイルに名称変更して利用すること --- #
PORT=5173
3. vite.config.tsを修正
.env
の情報は、loadEnv
から取得します。
import { reactRouter } from '@react-router/dev/vite';
import autoprefixer from 'autoprefixer';
import tailwindcss from 'tailwindcss';
import { defineConfig, loadEnv } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig(({ mode }) => {
// Load env file based on `mode` in the current working directory.
// Set the third parameter to '' to load all env regardless of the `VITE_` prefix.
const env = loadEnv(mode, process.cwd(), '');
return {
css: {
postcss: {
plugins: [tailwindcss, autoprefixer],
},
},
plugins: [reactRouter(), tsconfigPaths()],
server: {
port: Number.parseInt(env.PORT || '5173'),
},
};
});
Vitestを導入
1. パッケージをインストール
npm i -D vitest
2. vitestのglobal設定(テストファイルのimport省略)
vite.config.ts
をベースにして、テスト関数のimport
を省略する設定を加えます。
まずは、vitest.config.ts
を作成します。
import { type ConfigEnv, defineConfig, mergeConfig } from 'vitest/config';
import baseViteConfig from './vite.config';
export default defineConfig(async (configEnv: ConfigEnv) => {
// NOTE: 環境変数の読み込み(loadEnv())が非同期的なためawaitを設定
const baseConfig = await baseViteConfig(configEnv);
return mergeConfig(
baseConfig,
defineConfig({
test: {
globals: true,
},
}),
);
});
次に、tsconfig.json
を修正します。
"types": [
"node",
"vite/client",
+ "vitest/globals"
],
3. vitest動作確認用にテストファイルを追加
const sampleFunction = (a: number, b: number) => {
return a + b;
};
test('adds 1 + 2 to equal 3', () => {
expect(sampleFunction(1, 2)).toBe(3);
});
4. テスト実行用のスクリプトを追加
"scripts": {
// ...,
"--- TEST SECTION ---": "--- --- --- --- ---",
"test": "vitest"
},
npm run test
でテストが正常に動作すればOKです。
GitHubアクション設定
プルリク時に、コードチェック・ビルド・テストが通るか確認するためのCI設定を追加しておきます。
まず、CI用のコマンドを追加します。
"scripts": {
// ...
"--- CI SECTION ---": "--- --- --- --- ---",
"ci:check": "npx biome ci",
"ci:build": "npx env-cmd -f .env.example npm run build",
"ci:test": "npm run test"
}
次に、main
ブランチにプルリクした際のアクションを追加します。
name: CI
on:
push:
branches:
- main
pull_request:
jobs:
main:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- run: npm ci # 正確なバージョンのパッケージをインストール
- run: npm run ci:check
- run: npm run ci:build
- run: npm run ci:test
ファイルベースルーティング設定
Remix v2のファイルベースルーティングを利用するように設定を変更します。
1. パッケージをインストール
npm i @react-router/fs-routes
app/routes.ts
にファイベースルーティングの設定を追加する。
import { type RouteConfig } from "@react-router/dev/routes";
import { flatRoutes } from '@react-router/fs-routes';
const routes: RouteConfig = flatRoutes();
export default routes;
上記を設定すると、例えばapp/routes/_app._index/route.tsx
がトップページとして表示される。
shadcn/ui導入
1. パッケージをインストール
npx shadcn@latest init
✔ Which style would you like to use? › Default
✔ Which color would you like to use as the base color? › Zinc
✔ Would you like to use CSS variables for theming? … yes
2. components.jsonを設定
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/app.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "~/components/shadcn",
"utils": "~/lib/utils",
"ui": "~/components/shadcn/ui",
"lib": "~/lib",
"hooks": "~/hooks"
},
"iconLibrary": "lucide"
}
3. コンポーネントを追加
例えばボタンを追加したい場合、下記コマンドで追加できます。
npx shadcn@latest add button
Storybook導入
1. パッケージをインストール
npx storybook@latest init
2. Storybook用のvite-storybook.config.tsを作成
vite-storybook.config.ts
を作成します。
import { defineConfig, loadEnv } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
export default defineConfig(({ mode }) => {
const env = loadEnv(mode, process.cwd(), '');
process.env = { ...process.env, ...env };
return {
plugins: [tsconfigPaths()],
};
});
3. main.tsを修正
- storiesの対象パスを変更(appフォルダ内を対象とする)
- builderオプションに
vite-storybook.config.ts
を設定
import type { StorybookConfig } from '@storybook/react-vite';
const config: StorybookConfig = {
stories: ['../app/**/*.mdx', '../app/**/*.stories.@(js|jsx|mjs|ts|tsx)'],
addons: [
'@storybook/addon-onboarding',
'@storybook/addon-essentials',
'@chromatic-com/storybook',
'@storybook/addon-interactions',
],
framework: {
name: '@storybook/react-vite',
options: {
builder: {
viteConfigPath: 'vite-storybook.config.ts',
},
},
},
};
export default config;
4. StorybookにTailwind CSSを適応する
-
preview.ts
に、Tailwind CSSのimportを追加
import type { Preview } from '@storybook/react';
+ import '../app/app.css';
const preview: Preview = {
// ...
-
postcss.config.js
を追加
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
5. 利用方法
動作確認用に、shadcn/uiボタンのstorybookを追加します。
import type { Meta, StoryObj } from '@storybook/react';
import { Button } from './button';
const meta: Meta<typeof Button> = {
title: 'Components/Button',
component: Button,
argTypes: {
variant: {
control: { type: 'select' },
options: [
'default',
'destructive',
'outline',
'secondary',
'ghost',
'link',
],
},
size: {
control: { type: 'select' },
options: ['default', 'sm', 'lg', 'icon'],
},
asChild: { control: 'boolean' },
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Default: Story = {
args: {
variant: 'default',
size: 'default',
children: 'Button',
},
};
export const Destructive: Story = {
args: {
variant: 'destructive',
size: 'default',
children: 'Button',
},
};
export const Outline: Story = {
args: {
variant: 'outline',
size: 'default',
children: 'Button',
},
};
export const Secondary: Story = {
args: {
variant: 'secondary',
size: 'default',
children: 'Button',
},
};
export const Ghost: Story = {
args: {
variant: 'ghost',
size: 'default',
children: 'Button',
},
};
export const Link: Story = {
args: {
variant: 'link',
size: 'default',
children: 'Button',
},
};
Storybookを起動して、TailWindCSSが適用されていればOKです。
npm run storybook
セットアップは以上となります。
Discussion