Next.js の ServerAction で使えるフォームライブラリ、Conform を使ってみた


こんにちは、Gaji-Labo でフロントエンドをやっている中嶋です。
Gaji-Labo は Next.js や Remix(React Router) などのモダンなフロントエンドの技術を使い、スタートアップ支援を行っている会社です。
今回は Next.js の ServerAction の使用を前提に作られたフォームライブラリの Conform を個人開発で使ってみたので、ご紹介も兼ねて感想などを伝えられたらなと思います。

Conform とは??

React のフォームライブラリの一つです。私も公私でよく使っているもので、react-hook-form がありますが、この Conform も同じようにフォームの処理やバリデーション周りの実装を楽にしてくれるものです。特徴としては、以下のような点があるようです。

公式サイト引用

概要

Conform は、Web 標準に基づいて HTML フォームを段階的に強化し、 Remix や Next.js のようなサーバーフレームワークを完全にサポートする、型安全なフォームバリデーションライブラリです。

特徴

  • プログレッシブエンハンスメント・ファーストな API
  • 型安全なフィールド推論
  • きめ細やかなサブスクリプション
  • 組み込みのアクセシビリティ・ヘルパー
  • Zod による自動型強制

細かいところまで使用できていないため、今回詳細な説明は省略させて頂こうと思いますが、私がいいなと感じたところを上げていくと以下のようなところです。

  • Web 標準に基づいていて、アクセシビリティやSEOの観点も考慮しやすい
  • 型安全なフィールド推論がある
  • Zod が標準でサポートされている

ServerAction がサポートされている

そして私が使用してみたいと思った一番の理由が Next.js の ServerAction がサポートされている点です。
Next.js の最新の機能を使っていこうとすると、どうしてもライブラリが制限されてしまったり、独自の実装になってしまうことも少なくないです。
フォーム系のライブラリに関しても例外なく、その制限が入ったりします。そんな中、Conform は初めから ServerAction をサポートしている点でかなり魅力的でした。

実際に使ってみる

今回は Next.js の環境をつくって、その中に Conform 導入していく想定で実装したいと思いますので、Next.js の環境構築などは行いません。

インストール

npm install @conform-to/react @conform-to/zod --save 

スキーマの定義

import { z } from 'zod';

export const userSchema = z.object({
  firstName: z.string(),
  lastName: z.string(),
  email: z.string().email(),
  greeting: z.string()
});

今回は簡単なユーザープロフィールを想定して実装したいと思います。

ServerAction の実装

'use server';

import { parseWithZod } from '@conform-to/zod';
import { userSchema } from '@/app/schema';
import { sendUserProfile } from '@/app/your-dir'

export async function userAction (prevState: unknown, formData: FormData) {
  const submission = parseWithZod(formData, {
    schema: userSchema,
  });

  if (submission.status !== 'success') {
    return submission.reply();
  }

  return await sendUserProfile(submission.value)
}

userAction という関数名で ServerAction を作成しました。関数としては parseWithZod を使用して formData として渡ってきた入力値を検証し、検証結果に問題がなければ sendUserProfile 関数に値を渡してバックエンドにリクエストする想定です。

フォームコンポーネントの実装

'use client'

import { useForm } from '@conform-to/react';
import { parseWithZod } from '@conform-to/zod';
import { UserSchema } from './schema';
import { userAction } from './action';
import { useActionState } from "react";

const UserProfile = () => {
  const [lastResult, action] = useActionState(userAction, undefined);
  const [form, fields] = useForm({
    // 前回の送信結果を同期
    lastResult,

    // バリデーション・ロジック
    onValidate({ formData }) {
      return parseWithZod(formData, { schema: UserSchema });
    },

    // バリデーションタイミングの指定
    shouldValidate: 'onBlur', // バリデーションを行うタイミング
    shouldRevalidate: 'onInput', // 再検証時のタイミング
  });
  
  return (
    <form id={form.id} onSubmit={form.onSubmit} action={action}>
      <div>
        <label>First Name</label>
        <input
          type="text"
          key={fields.firstName.key}
          name={fields.firstName.name}
          defaultValue={fields.firstName.initialValue}
        />
        <div>{fields.name.errors?.[0]}</div>
      </div>
      
      // ...
      
      <button>submit</button>
    </form>
  );
}

コンポーネント内で Conform を使用するためには useForm を使用します。
useActionState でフォームの 状態を管理して、lastResult として useForm に渡しています。これによって serverAction によって更新された値を useForm でも同期することが可能になります。
また、useForm はバリデーションも細かく設定することができ、shouldValidateはバリデーションのタイミングを変更することができます。
shouldRevalidate は バリデーション状態(入力値が不正だった場合)にどのタイミングで再検証を行うかを指定することができます。
バリデーションのタイミングを細かく設定できるのは個人的にかなり嬉しいポイントでした。

使用してみて

ここまでコードベースで Conform について解説させていただきましたが、react-hook-form など他のフォーム系のライブラリを使用したことのある方なら割とすんなり入れそうだなと思いました。
使用感も特に難しいこともなく、シンプルに実装できてとても良いライブラリという印象を受けました。
また、今回は Next.js に絞った内容での説明になってしまいましたが、Remix(React Router) やUIライブラリについても対応しているので、今後伸びてきそうなライブラリだと思っています。
Next.js の AppRouter や Remix などでフォームの実装に悩んでいる方や、ServerAction を使ってフォームを実装したいと考えている方にはぴったりなライブラリだと思うので、ぜひご自身でも試してみてください。
今後もチーム開発に活かせるような技術発信をしていけたらと思っているので、引き続きよろしくお願いいたします。

Gaji-Labo は新規事業やサービス開発に取り組む、事業会社・スタートアップへの支援を行っています。

弊社では、Next.js を用いた Web アプリケーションのフロントエンド開発をリードするフロントエンドエンジニアを募集しています!さまざまなプロダクトやチームに関わりながら、一緒に成長を体験しませんか?

もちろん、一緒にお仕事をしてくださるパートナーさんも随時募集中です。まずはお気軽に声をかけてください!

求人応募してみる!


投稿者 Shota Nakashima

フロントエンドエンジニア。 業界未経験から受託開発、toBサービスの開発経験を経てGaji-Laboに参加。 フロントエンド、バックエンド問わずモダンな技術に興味・関心があります。将来はフロントエンドのエキスパートになりたいと思っています。 プライベートでは5児の父で、イクメンエンジニアを目指して日々奮闘中です。