フォーム実装、めんどくさくないですか?
フロントエンド開発において、フォームの実装は避けて通れない作業のひとつです。
テキスト入力・セレクトボックス・バリデーション・エラーメッセージ…これらをひとつひとつ丁寧に実装するのはかなりの手間がかかります。
さらにバックエンドの仕様変更のたびにフロントエンド側も修正が必要になり、二重管理のコストが地味に積み重なっていきます。
バックエンド寄りの僕にとって、フロントエンドのフォーム実装は特に苦手な領域でした。
そこで出会ったのが react-jsonschema-form(RJSF) です。JSON Schema を定義するだけでフォームが自動生成されるので、フロントエンド実装をほぼゼロにできます。
この記事では、RJSF の基本から uiSchema による機能拡張まで、実際のコードを交えて紹介します。
react-jsonschema-form とは
react-jsonschema-form(RJSF) は、JSON Schema の定義をもとに React のフォームコンポーネントを自動生成するライブラリです。
結構前からあり、現在も活発にメンテナンスされています。
主な特徴は次のとおりです。
- JSON Schema からフォームを自動生成: スキーマを定義するだけで入力フィールドが揃う
-
バリデーション自動化: JSON Schema のルール(
required・minLength・patternなど)を自動で適用 - uiSchema によるUI拡張: スキーマとは別にUIのカスタマイズができる
- テーマ対応: MUI・Ant Design・Bootstrap など主要 UI ライブラリのテーマが公式提供されている
RJSF v5 以降は @rjsf/core と各テーマパッケージが分離されています。
本記事では @rjsf/core をベースに説明します。
インストール
npm install @rjsf/core @rjsf/utils @rjsf/validator-ajv8
MUI テーマを使いたい場合は以下も追加します。
npm install @rjsf/mui @mui/material @emotion/react @emotion/styled
基本的な使い方
最小構成のサンプルです。schema を渡すだけでフォームが生成されます。
import Form from "@rjsf/core";
import validator from "@rjsf/validator-ajv8";
const schema = {
title: "ユーザー登録",
type: "object",
required: ["name", "email"],
properties: {
name: {
type: "string",
title: "名前",
minLength: 1,
},
email: {
type: "string",
title: "メールアドレス",
format: "email",
},
age: {
type: "integer",
title: "年齢",
minimum: 0,
},
},
};
export default function App() {
const handleSubmit = ({ formData }: { formData: unknown }) => {
console.log("送信データ:", formData);
};
return (
<Form
schema={schema}
validator={validator}
onSubmit={handleSubmit}
/>
);
}
schema を変えるだけでフォームの構成が変わります。
バックエンドからスキーマを API で返すようにすれば、フロントエンドのコードを一切変えずにフォームを更新できます。
uiSchema でUIをカスタマイズする
uiSchema は JSON Schema とは独立したオブジェクトで、各フィールドの見た目や振る舞いを制御します。
型定義や必須チェックはスキーマ側・UIの詳細は uiSchema 側と役割を分けられるのがポイントです。
テキストエリアへの変更・ヘルプテキストの追加
const uiSchema = {
name: {
"ui:placeholder": "例:山田 太郎",
"ui:help": "フルネームを入力してください",
},
email: {
"ui:widget": "email",
},
age: {
"ui:widget": "updown", // スピナー入力に変更
},
};
<Form schema={schema} uiSchema={uiSchema} validator={validator} onSubmit={handleSubmit} />
パスワード入力・非表示フィールド
const uiSchema = {
password: {
"ui:widget": "password",
},
internalId: {
"ui:widget": "hidden",
},
};
カスタムウィジェットで機能を拡張する
デフォルトのウィジェットでは対応できないケース(日付ピッカー・ファイルアップロードなど)は、カスタムウィジェットを作成して登録できます。
以下は外部 API からデータを取得して動的にセレクトボックスの選択肢を生成するカスタムウィジェットの例です。
import { WidgetProps } from "@rjsf/utils";
import { useEffect, useState } from "react";
function DynamicSelectWidget(props: WidgetProps) {
const { value, onChange } = props;
const [options, setOptions] = useState<{ label: string; value: string }[]>([]);
useEffect(() => {
// 外部 API から選択肢を取得
fetch("/api/options")
.then((res) => res.json())
.then((data) => setOptions(data));
}, []);
return (
<select
value={value ?? ""}
onChange={(e) => onChange(e.target.value)}
>
<option value="">-- 選択してください --</option>
{options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
);
}
このウィジェットを uiSchema で指定します。
const uiSchema = {
category: {
"ui:widget": DynamicSelectWidget,
},
};
カスタムウィジェットのレンダリング中にエラーが発生すると、フォーム全体がクラッシュする可能性があります。
エラーハンドリングは必ず実装してください。
フォームのデータフロー全体像
バックエンドからスキーマを配信するアーキテクチャのイメージは次のとおりです。
このように、スキーマを API で管理することでフロントエンドのコード変更なしにフォームを更新できます。
まとめ
react-jsonschema-form を使うことで得られるメリットをまとめます。
| 項目 | 内容 |
|---|---|
| 実装コスト削減 | JSON Schema を定義するだけでフォームが生成される |
| バリデーション自動化 | スキーマのルールがそのまま入力チェックになる |
| フロント・バック間の一貫性 | スキーマが単一の真実の源(Single Source of Truth)になる |
| 拡張性 |
uiSchema とカスタムウィジェットで柔軟にカスタマイズ可能 |
バックエンドエンジニアがフォーム機能を追加するたびにフロントエンドへの依頼が発生する、という状況を解消できるのが最大の魅力です。
僕自身、RJSF を導入してからフロントエンドの実装工数を大きく削減でき、機能開発に集中できるようになりました。
まずはシンプルなフォームから試してみてください!
参考資料