Superforms v2 with Formsnap and Valibot
はじめに
こんにちは、株式会社Liquitousのエンジニアのかずうみ(@Kazuumi_n)です。
SvelteKitでいい感じのフォームを実装するために、これらのライブラリについて紹介していきます。
ハンズオン
なお、今回は紹介しながらハンズオンしていきます。
環境は、以下の操作を実施済みと想定しています。
- SvelteKit初期化
- TailwindCSSインストール
質問などあれば遠慮なくコメントでもSvelte Japan Discordの#helpチャンネルでもご質問ください!
Superformsについて
SuperformsはSvelteKitに最適化されたフォームライブラリです。値を検証してエラーを処理して、、という面倒なフォーム実装を楽にしてくれます。
今回は詳しくは先人の資料に丸投げするのでぜひご覧ください。
2024年2月に、Superformsのv2がリリースされました。旧バージョンでは値のバリデーション検証にZodしか使えなかったのに対し、v2からは他のバリデーションライブラリも使えるようになりました。
この記事ではValibotというバリデーションライブラリを使っていきます。
Valibotについて
ValibotはJavaScriptのバリデーションライブラリです。入力した値が、事前に定義したスキーマに一致するかどうかを検証できます。
Valibotは比較的新しいライブラリであるため、先行するバリデーションライブラリの利点を参考にしながら、バンドルサイズを小さくするための工夫が多くなされています。
これから実際に使っていきます。
SuperformsとValibotを組み合わせてフォームを作る
今回は、新規アカウント作成とアンケートが組み合わさったUIを作ってみます。
- まず、必要なパッケージをインストールします。
pnpm i -D sveltekit-superforms valibot
- 次に、スキーマを定義します。
import * as v from 'valibot';
export const schema = object({
// メールアドレスの形式であることを検証
email: v.pipe(v.string(), v.email('メールアドレスの形式で入力してください')),
// 8文字以上の文字列であることを検証
password: v.pipe(v.string(), v.minLength(8, '8文字以上で入力してください'))
});
- load関数でフォームを初期化する
import { superValidate } from 'sveltekit-superforms';
import { valibot } from 'sveltekit-superforms/adapters';
import { schema } from '$lib/schema';
export const load = (async () => {
const form = await superValidate(valibot(schema));
return { form };
});
- フォームを表示する
<script lang="ts">
import { superForm } from 'sveltekit-superforms';
export let data;
const { form, errors, constraints, message } = superForm(data.form);
</script>
{#if $message}<h3>{$message}</h3>{/if}
<form method="POST">
<label for="email">メールアドレス</label>
<input
type="email"
name="email"
aria-invalid={$errors.email ? 'true' : undefined}
bind:value={$form.email}
{...$constraints.email}
/>
<!-- emailフィールドにエラーがあれば表示する -->
{#if $errors.email}<span class="text-red-500">{$errors.email}</span>{/if}
<label for="password">パスワード</label>
<input
type="password"
name="password"
aria-invalid={$errors.password ? 'true' : undefined}
bind:value={$form.password}
{...$constraints.password}
/>
<!-- passwordフィールドにエラーがあれば表示する -->
{#if $errors.password}<span class="text-red-500">{$errors.password}</span>{/if}
<div><button>送信する</button></div>
</form>
- フォームで送信された値を検証する
import { message } from 'sveltekit-superforms';
import { fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request }) => {
const form = await superValidate(request, valibot(schema));
if (!form.valid) {
// ここでformを返すだけでok
return fail(400, { form });
}
console.log(form.data)
// Display a success status message
return message(form, '送信成功しました');
}
};
これで、フォームを実装することができました。pnpm dev
で開発サーバーを実行し、localhost:5173/superformsにアクセスすると実際に値が検証されて、スキーマに一致しないフィールドは画面上へ、スキーマに一致すれば開発サーバーのコンソールに入力した情報が表示されます。
Formsnap
Formsnapは、Superformsをベースに、より扱いやすくしたラッパーのようなライブラリです。
先ほど+page.svelteを書いただけでもかなりの量の共通ボイラープレートを書くことになりましたが、本来アクセシビリティを考慮するとさらに記述量が増えます。
Formsnapを使うことで、その記述量を劇的に減らすことができます。
Formsnapを組み合わせる
- Formsnapをインストールする
pnpm install formsnap
- Formsnapでフロント側を実装する
<script lang="ts">
import { superForm } from 'sveltekit-superforms';
import { Field, Control, Label, Description, FieldErrors } from 'formsnap';
import { valibotClient } from 'sveltekit-superforms/adapters';
import { schema } from '$lib/schema';
export let data;
const form = superForm(data.form, {
validators: valibotClient(schema)
});
const { form: formData, message, enhance } = form;
</script>
{#if $message}<h3>{$message}</h3>{/if}
<form use:enhance class="mx-auto flex max-w-md flex-col" method="POST">
<Field {form} name="email">
<Control let:attrs>
<Label>メールアドレス</Label>
<input {...attrs} type="email" bind:value={$formData.email} />
</Control>
<Description>メールアドレスを入力してください</Description>
<FieldErrors />
</Field>
<Field {form} name="password">
<Control let:attrs>
<Label>パスワード</Label>
<input {...attrs} type="password" bind:value={$formData.password} />
</Control>
<Description>パスワードを入力してください</Description>
<FieldErrors />
</Field>
<button>送信</button>
</form>
- サーバーサイドは、先ほどの工程で使ったコードと同様
同様のため省略
import { superValidate } from 'sveltekit-superforms';
import { valibot } from 'sveltekit-superforms/adapters';
import { schema } from '$lib/schema';
export const load = (async () => {
const form = await superValidate(valibot(schema));
return { form };
});
import { message } from 'sveltekit-superforms';
import { fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request }) => {
const form = await superValidate(request, valibot(schema));
if (!form.valid) {
// ここでformを返すだけでok
return fail(400, { form });
}
console.log(form.data)
// Display a success status message
return message(form, '送信成功しました');
}
};
いかがでしょうか。似通ったコードを書く必要がかなり減ったと思います。
個人的には、次のshadcn-svelteも合わせるとより恩恵を得られると感じます。
shadcn-svelte
shadcn-svelteは、洗練されてカスタマイズ性も高いSvelte用のUIコンポーネント集です。
Bits UIというヘッドレスUIコンポーネントをベースに開発されています。
shadcn-svelteを組み合わせる
-
shadcn-svelteの導入
ここでは詳細を書きません、以下の公式ページに従ってください。
https://www.shadcn-svelte.com/docs/installation/sveltekit#setup-path-aliases -
shadcn-svelteでformを追加する
npx shadcn-svelte add form
- shadcn-svelteでフロント側を実装する
(Formsnap版をコピーした上で差分を表示します。)
+ import { Field, Control, Label, Description, FieldErrors, Button } from '$components/ui/form';
- import { Field, Control, Label, Description, FieldErrors } from 'formsnap';
/// 略
+ <Button>送信</Button>
- <button>送信</button>
- サーバーサイドは、先ほどの工程で使ったコードと同様
同様のため省略
import { superValidate } from 'sveltekit-superforms';
import { valibot } from 'sveltekit-superforms/adapters';
import { schema } from '$lib/schema';
export const load = (async () => {
const form = await superValidate(valibot(schema));
return { form };
});
import { message } from 'sveltekit-superforms';
import { fail } from '@sveltejs/kit';
export const actions = {
default: async ({ request }) => {
const form = await superValidate(request, valibot(schema));
if (!form.valid) {
// ここでformを返すだけでok
return fail(400, { form });
}
console.log(form.data)
// Display a success status message
return message(form, '送信成功しました');
}
};
項目2を見ていただくと分かるように、Formsnapからshadcn-svelteへの変更はとてもスムーズです。それもそのはず、shadcn-svelteの作者とFormsnapの作者は同じ人で、shadcn-svelteのformコンポーネントはFormsnapをベースに作られています。
src/lib/components/ui/form/
配下のsvelteコンポーネントを直接確認・編集することもできます。
終わりに
この内容はSvelte Japan Online Meetup #3で話した内容を記事にしたものでした(アーカイブはこちら)!
ぜひ皆さんもSvelteKitのSuperformsに、Formsnapやshadcn-svelteを試してみてください!
記事に関連してわからないことがあれば、コメントでもSvelte Japan Discordの#helpチャンネルでもご質問いただけると幸いです。
Discussion