要約
- 大規模言語モデル(Claude、ChatGPTなど)にReactコンポーネントを作ってもらう時、shadcn/uiを指定すると良い感じにフォームを作ってもらえる。が、作ってもらったフォーム等を手元で動かそうとすると、色々と設定が必要になる。
- 本記事では
Rspack
とpnpm
を使ってReactプロジェクトを作り、shadcn/uiを導入する手順を解説する。-
Rspack
とはRustで書かれたwebpackの互換性のあるツールで早いので筆者は気に入っている。 - いくつかのプロジェクトで採用しているが、webpackと同様に扱えており不自由していない。
- webpackを使いたいのであれば、webpack.config.jsを同様に書くだけで、あとは同じ手順で設定できると思われるが、本記事では扱わない。
-
- なお、設定しなければいけない手順として多くあるので、一括で行ってくれるツールを用意した。詳細は最後の方に記載しているが、コマンドは以下の通りである。
pnpm create @infodb/myproj@latest
補足
-
next.js
やvite
を使うのであればshadcn/uiの公式ページを参照して設定するか、またはv0
やbolt
といったサービスを利用することでもプロジェクト等の設定まで行えるはず。但し、本記事では扱わない。 - また、文中は
pnpm
を使っているが、npm
やyarn
でも同じことができるはずである。(当方では未確認)
紹介動画
このツールの紹介動画。Claudeで作ったフォームを開発環境に持ってきて動かすまでを解説している。
1. Rspackのプロジェクト作成からTailwind CSSの導入まで
- Tailwind CSSの公式ページに記載されているが、それの転記となる。
- また後半にも同じファイルを修正する手順があるが、混乱を避けるために元の手順をそのまま採用している。
1-1. プロジェクトの作成
- 以下のコマンドを実行してプロジェクトを作成する。
pnpm create rspack@latest
- プロジェクト名やフレームワーク、言語を選択する画面が表示されるので、適宜入力する。
- ここではTypeScriptを選択したケースで記載している。
1-2. Tailwind CSSの導入
- 以下のコマンドを実行してTailwind CSSを導入する。
pnpm add -D tailwindcss postcss postcss-loader autoprefixer
pnpx tailwindcss init -p
1-3. rspack.config.tsの修正
- rulesの下にCSSの設定を追加する。
module.exports = {
// ...
module: {
rules: [
{
test: /\.css$/,
use: ["postcss-loader"],
type: "css",
},
// ...
1-4. tailwind.config.jsの修正
- contentに
./src/**/*.{js,ts,jsx,tsx}
を追加する。
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./src/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
1-5. index.cssの修正
-
index.css
を以下のように書き換える。 - Rspackでデフォルトで生成される
index.css
は、色々書かれているので全て消して構わない。
@tailwind base;
@tailwind components;
@tailwind utilities;
1-6. devサーバーの起動
- 以下のコマンドを実行してdevサーバーを起動する。
- ブラウザで
http://localhost:8080
にアクセスして、画面が表示されれば成功。 - また、
src/App.tsx
を編集して、tailwindcssのクラスが適用されることを確認する。
pnpm dev
-
src/App.tsx
の例
export default function App() {
return (
<h1 className="text-3xl font-bold underline">
Hello world!
</h1>
)
}
2. Shadcn/UIの導入
-
shadcn/ui
の公式ページでマニュアルインストールに記載されているが、それの転記となる。 - 但し一部パス等の修正は行っている。
- また、公式ページの記載では足りていない部分があるので、それは次章で記載している。
2-1. 依存関係の導入
- 依存関係のあるパッケージ、アイコンライブラリ、@radix-ui/react-iconsをインストールする。
pnpm add tailwindcss-animate class-variance-authority clsx tailwind-merge
pnpm add lucide-react @radix-ui/react-icons
2-2. path aliasの設定
-
tsconfig.json
にbaseUrl
とpaths
を追加する。
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
...
}
}
2-3. tailwind.config.jsの設定
-
tailwind.config.js
を以下のように修正する。(全て書き換えで構わない。)
const { fontFamily } = require("tailwindcss/defaultTheme")
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: ["./src/app/**/*.{ts,tsx}", "./src/components/**/*.{ts,tsx}"],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
fontFamily: {
sans: ["var(--font-sans)", ...fontFamily.sans],
},
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" },
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" },
},
},
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
},
},
},
plugins: [require("tailwindcss-animate")],
}
2-4. global.cssの作成
- ./src/global.cssを作成し、以下の内容を記述する。
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 47.4% 11.2%;
--muted: 210 40% 96.1%;
--muted-foreground: 215.4 16.3% 46.9%;
--popover: 0 0% 100%;
--popover-foreground: 222.2 47.4% 11.2%;
--border: 214.3 31.8% 91.4%;
--input: 214.3 31.8% 91.4%;
--card: 0 0% 100%;
--card-foreground: 222.2 47.4% 11.2%;
--primary: 222.2 47.4% 11.2%;
--primary-foreground: 210 40% 98%;
--secondary: 210 40% 96.1%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
--accent-foreground: 222.2 47.4% 11.2%;
--destructive: 0 100% 50%;
--destructive-foreground: 210 40% 98%;
--ring: 215 20.2% 65.1%;
--radius: 0.5rem;
}
.dark {
--background: 224 71% 4%;
--foreground: 213 31% 91%;
--muted: 223 47% 11%;
--muted-foreground: 215.4 16.3% 56.9%;
--accent: 216 34% 17%;
--accent-foreground: 210 40% 98%;
--popover: 224 71% 4%;
--popover-foreground: 215 20.2% 65.1%;
--border: 216 34% 17%;
--input: 216 34% 17%;
--card: 224 71% 4%;
--card-foreground: 213 31% 91%;
--primary: 210 40% 98%;
--primary-foreground: 222.2 47.4% 1.2%;
--secondary: 222.2 47.4% 11.2%;
--secondary-foreground: 210 40% 98%;
--destructive: 0 63% 31%;
--destructive-foreground: 210 40% 98%;
--ring: 216 34% 17%;
--radius: 0.5rem;
}
}
@layer base {
* {
@apply border-border;
}
body {
@apply bg-background text-foreground;
font-feature-settings: "rlig" 1, "calt" 1;
}
}
2-5. lib/utils.tsの作成
- ./src/lib/utils.tsを作成し、以下の内容を記述する。
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
3. 最終調整
- 以下の内容は、公式ページ等には書かれていないが、必要な設定である。
3-1. rspack.config.tsの名前修正とAliasの追加
-
rspack.config.ts
のままだとpath.resolve
でTypeError: Cannot read properties of undefined (reading 'resolve')
のエラーとなってしまってaliasが設定できないので、rspack.config.js
にリネームする。- 原因は不明。将来的なバージョンで修正される可能性があるし、
pnpm
を使っている副作用かもしれない。 - jsファイルにすることで
path
が無事にインポートされて回避できるので、一旦これで対応する。
- 原因は不明。将来的なバージョンで修正される可能性があるし、
const rspack = require("@rspack/core");
const RefreshPlugin = require("@rspack/plugin-react-refresh");
const path = require("node:path");
const isDev = process.env.NODE_ENV === "development";
// Target browsers, see: https://github.com/browserslist/browserslist
const targets = ["chrome >= 87", "edge >= 88", "firefox >= 78", "safari >= 14"];
module.exports = {
context: __dirname,
entry: {
main: "./src/main.tsx",
},
resolve: {
alias: {
"@": path.resolve(__dirname, "./src"),
},
extensions: ["...", ".ts", ".tsx", ".jsx"],
},
module: {
rules: [
{
test: /\.svg$/,
type: "asset",
},
{
test: /\.(jsx?|tsx?)$/,
use: [
{
loader: "builtin:swc-loader",
options: {
jsc: {
parser: {
syntax: "typescript",
tsx: true,
},
transform: {
react: {
runtime: "automatic",
development: isDev,
refresh: isDev,
},
},
},
env: { targets },
},
},
],
},
{
test: /\.css$/,
use: ["postcss-loader"],
type: "css",
},
],
},
plugins: [
new rspack.HtmlRspackPlugin({
template: "./index.html",
}),
isDev ? new RefreshPlugin() : null,
].filter(Boolean),
optimization: {
minimizer: [
new rspack.SwcJsMinimizerRspackPlugin(),
new rspack.LightningCssMinimizerRspackPlugin({
minimizerOptions: { targets },
}),
],
},
experiments: {
css: true,
},
};
3-2. src以下のファイルを修正
-
app
、components
フォルダを作成する。 -
src/App.tsx
をsrc/app/App.tsx
に移動する。 -
src/index.css
、src/App.css
を削除する。 - main.tsxを以下のように修正する。
import React from "react";
import ReactDOM from "react-dom/client";
import App from "@/app/App";
import "./global.css";
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
<React.StrictMode>
<App />
</React.StrictMode>,
);
3-3. components.jsonの作成
-
./components.json
を作成し、以下の内容を記述する。
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/globals.css",
"baseColor": "zinc",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
3-4. shadcn/uiのコンポーネントの追加
pnpx shadcn@latest add button
-
src/app/App.tsx
を以下のように修正する。
import { Button } from "@/components/ui/button";
export default function App() {
return (
<div className="p-5">
<h1 className="text-3xl font-bold underline text-red-400 mb-3">
Hello world!
</h1>
<Button>Click me</Button>
</div>
);
}
3-5. devサーバーの起動
- 動作確認をする。以下のコマンドを実行してdevサーバーを起動する。
pnpm dev
- Hello world!とClick meボタンが表示されれば成功。
- あとはAIにshadcn/uiを使ったコンポーネントの作成指示を出すことで、コピペでフォームが表示できるようになる。(コンポーネントやライブラリなどは適宜入れる。)
フォルダ構成と解説、課題など
- 最終的なフォルダ構成は以下のようになる。
|--node_modules/* (中身は省略)
|--src
| |--app
| | |--App.tsx
| |--components
| | |--ui
| | | |--button.tsx
| |--lib
| | |--utils.ts
| |--global.css
| |--main.tsx
|--.gitignore
|--components.json
|--index.html
|--package.json
|--pnpm-lock.yaml
|--postcss.config.js
|--README.md
|--rspack.config.js
|--tailwind.config.js
|--tsconfig.json
- src直下にglobal.cssとmain.tsxを配置している。また、index.htmlはプロジェクト直下となっているので、好みに合わせて修正が必要。(筆者はこの構成を当面使用する予定)
-
pnpx shadcn@latest add <<component>>
を実行するために必要な最小限のファイルは、components.json
、package.json
、tsconfig.json
の3ファイルである。(フォルダは無ければ作られる)- 依存関係がインストールされていないと、shadcnがnpmでインストールしてくれるようなので、事前にインストールしておく必要がある。
- tsconfig.jsonはaliasとなっているフォルダパスを取得するために参照していると思われる。
- 既存のプロジェクトなどに組み込む場合には、それらファイルを用意するだけでshadcn/uiを使えるようになると思われる。(ライブラリ等は要調整)
ツール
- 手順が多いので3-3までの手順を行うツールを用意した。
pnpm create @infodb/myproj@latest