Claude CodeでWeb UIを作ってみる話
こんにちは、まるとです。
最近ちょっとしたWebアプリを思いついたものの、フロントエンドの実装があまり得意ではない自分です。
そこで、図を簡単に書いて生成AIに読み込ませたら、どこまで作り込んでくれるのか試してみました。
ツール/使いたい技術
- Claude Code (Proプラン)
- React+Next.js
やってみる
まず、簡単に以下の図を作りました。
このUIにした目的としては「1人用または4人用の予約不要会議室が空いているかをエリアごとに直感的に知りたい」から、シンプルですが上の図を作ってみました。
今回はReact+Next.jsを使いたいので、「npx create-next-app@latest」を実行し、ほぼ空のプロジェクトを作成しました。
次にClaude Codeで以下の指示を出してみます。
npx create-next-app@latestを実行して基本的なプロジェクトは作りました。
WebUIで部屋の空き状況を見れるようにしたいです。
UIのイメージを以下に配置しました。
project/path/to/ui.png
バックエンドの実装は不要なので、まずはフロントだけ作成してください。
できたのがこちらです。
かなり再現度が高いのではないでしょうか。
この時点での軽くファイル構造や実装を見てみましょう。
構造
src/
└── app/
├── globals.css
├── layout.tsx
├── page.tsx
└── page.module.css
Claude Codeへの指示の仕方にもよりますが、基本的にpage.tsxに全機能があるファイル構造でした。
実装
実装も見てみます。
'use client';
import { useState, useEffect } from 'react';
import styles from "./page.module.css";
interface Room {
id: string;
name: string;
status: 'Available' | 'In use';
capacity: number;
}
export default function Home() {
const [currentTime, setCurrentTime] = useState('00:00');
const rooms: Room[] = [
{ id: 'dev-1', name: 'Dev-1', status: 'Available', capacity: 1 },
{ id: 'dev-2', name: 'Dev-2', status: 'In use', capacity: 4 }
];
useEffect(() => {
const updateTime = () => {
const now = new Date();
const hours = now.getHours().toString().padStart(2, '0');
const minutes = now.getMinutes().toString().padStart(2, '0');
setCurrentTime(`${hours}:${minutes}`);
};
updateTime();
const interval = setInterval(updateTime, 1000);
return () => clearInterval(interval);
}, []);
return (
<div className={styles.container}>
<header className={styles.header}>
<h1>Box Status</h1>
<div className={styles.time}>{currentTime}</div>
</header>
<div className={styles.main}>
<aside className={styles.sidebar}>
<h2>Area</h2>
<div className={styles.areaList}>
<button
className={`${styles.areaButton} ${styles.active}`}
>
Dev
</button>
</div>
</aside>
<main className={styles.roomsContainer}>
<div className={styles.rooms}>
{rooms.map(room => (
<div
key={room.id}
className={`${styles.roomCard} ${room.status === 'Available' ? styles.available : styles.inUse}`}
>
<h3>{room.name}</h3>
<div className={styles.roomInfo}>
<div className={styles.capacity}>
<span className={styles.personIcon}>👤</span>
<span>{room.capacity}</span>
</div>
<p className={styles.status}>{room.status}</p>
</div>
</div>
))}
</div>
</main>
</div>
</div>
);
}
現時点では部屋数も少なくシンプルですが、将来的に拡張するのであれば、ヘッダーやサイドバー、部屋などをコンポーネントに分けた方が単一ファイルに役割が集中せず、個別にテストや保守がしやすくなります。
その他、生成されたコードではコンポーネント内で部屋のリストを毎回生成しています。ただ、この場合、再レンダリング毎に配列オブジェクトが作成されます。
初期データ・デフォルト値はコンポーネント外に配置することで再レンダリング時に再生成が行われないため、少しでも効率が良くなります。
// 生成時(Home()内で定義されている)
export default function Home() {
const rooms: Room[] = [
{ id: 'dev-1', name: 'Dev-1', status: 'Available', capacity: 1 },
{ id: 'dev-2', name: 'Dev-2', status: 'In use', capacity: 4 }
];
// 変更案(Home()外で初期値を定義)
const INITIAL_ROOMS: Room[] = [
{ id: 'dev-1', name: 'Dev-1', status: 'Available', capacity: 1 },
{ id: 'dev-2', name: 'Dev-2', status: 'In use', capacity: 4 }
];
// stateなどで動的に状態を管理(setXxxxxでデータ更新ができる)
export default function Home() {
const [rooms, setRooms] = useState<Room[]>(DEFAULT_ROOMS);
簡単にですが、実装はいくつか改善できそうなところがありそうです。
機能を増やしてみる
一旦実装は見れたので、フロントエンドを作る話に戻します。
今度は作成されたUIに対して、図を提示せずに機能を追加してみます。
開発用画面では部屋が2部屋しかありませんが、将来的に部屋数が増えたときに空いている部屋だけに絞ったり、各部屋を収容人数でフィルタして見やすくしたいとします。
では、Claude Codeに文章だけで以下の指示を出してみます。
空き部屋のみ表示、使用中のみ表示のフィルタボタンを実装して
また、人数毎にフィルタしたい
作成された画面がこちらです。
特にデザインは指定していませんが、綺麗なボタンが出来上がっています。
続いてエリアを増やしつつ、全エリアを表示できる機能を追加します。
エリアは参考までにクラスメソッド 日比谷オフィスのエリア「Meet」「Cafe」「Hub」「Lab」を増やしつつ、いくつか部屋をつくります。
エリアの参考
以下の指示を出します。
全エリアを表示できるAll、次のエリアを追加して。Meet、Cafe、Hub、Lab
各エリアには次の部屋を割り当て
Meet: 13-16(4人)
Cafe: 17-24(1人)
Hub: 25-32(4人)
Lab: 33-34(1人)、38-39(4人)、40-51(1人)
完成した画面が以下となります。
機能含めかなり綺麗に出来上がっており、ほぼコードを書くことなくプロトタイプを作ることができました。
伝えたいこと
図を書けばある程度完成したものがすぐに利用できる状態で出力され、更に自然言語で機能を追加できるので、デモやプロトタイプの作成にはかなり有用なのではないでしょうか。
一方で、本番運用や長期的な保守を考えた場合は、生成されたコードをそのまま使うのではなく、人の手を入れることも重要だと実感しました。
ただ、0から1を作るまで短時間でできる(今回のデモは10分もかからず)ので、プロトタイプを早い段階で作成し、認識合わせや要件整理で活用できるのではないでしょうか。
フロントエンドが苦手な方でもアイデアがあれば、短時間で形ができるのでぜひ一度試してみてください。
以上、まるとでした!