Svelte フォームライブラリ選手権

# はじめに
Web開発において、フォームは開発者の悩みのタネの一つです。とくに、ユーザーの入力中にリアルタイムでバリデーションを行い、エラーメッセージを表示するようなフォームは自前で実装するのが大変です。全部HTML標準のバリデーションに任せたいところですが、デザインの都合上、そうも行かないことも多いでしょう。

そういったときに必要になるのがフォームライブラリです。執筆時点(2022年2月)で,
Reactでは [React Hook Form](https://react-hook-form.com/jp/)が覇権を握っているようです。Vueでは[VeeValidate](https://vee-validate.logaretm.com/v3)あたりが有名所でしょうか。
さて、近年その勢いを増しているSvelteですが、フォームライブラリが乱立しています。本記事ではSvelteのフォームライブラリを比較していきます。

# 注意点
- https://bestofsvelte.com/t/form こちらのサイトに乗っているフォームライブラリを比較します。
- 各ライブラリは2022年2月時点での最新バージョンを扱います。
- 筆者のSvelte歴は業務で1年ほど使ったくらいです。
- 10段階評価をしていますが、ざっくりと以下の点を見ています
  - ボイラープレートの少なさ(Svelteの重要な思想だと思ってます)
  - TypeScriptサポート(昨今のフロントエンドにおいて、TypeScriptの重要さは言うまでもありません)
  - テストケースが書かれているか
- なお、GitHubのスター数やnpmのダウンロード数はあまり参考にしません。筆者の経験上、Svelte界隈のライブラリはスター数やダウンロード数とクオリティが一致していないことが多いためです。(Svelteがまだ比較的新しいフレームワークで、フォームライブラリが乱立しているのが原因と考えています。)

# 選手権スタート
## エントリーNo. 1 [Svelte forms lib](https://svelte-forms-lib-sapper-docs.vercel.app/introduction)
**評価：7/10**

ReactのフォームライブラリであるFomikに近いAPIであることをうたっています。yupのスキーマと連携できます。TypeScriptの型定義もありますが、input要素すべてに`on:keyup={handleChange}`と指定する必要があるのはマイナスポイントです。


```ts:サンプルコード
  <script>
    import { createForm } from "svelte-forms-lib";

    const {
      form,
      errors,
      state,
      touched,
      isValid,
      isSubmitting,
      isValidating,
      // handlers
      handleBlur,
      handleChange,
      handleSubmit
    } = createForm({
      initialValues: { name: "", email: "" },
      validationSchema: yup.object().shape({
        name: yup.string().required(),
        email: yup.string().email().required()
      }),
      onSubmit: values => { /** ... */ }
    });
  </script>

  <form class:valid={$isValid} on:submit={handleSubmit}>
    <label>name</label>
    <input name="name" on:keyup={handleChange} />
    {#if $errors.name && $touched.name}
      <small>{$errors.name}</small>
    {/if}

    <label>email</label>
    <input name="email" on:keyup={handleChange} />
    {#if $errors.email && $touched.email}
      <small>{$errors.email}</small>
    {/if}

    <button type="submit" disabled={!$isValid}>
      {#if $isSubmitting}loading...{:else}submit{/if}
    </button>
  </form>
```
(https://svelte-forms-lib-sapper-docs.vercel.app/basic より抜粋)

## エントリーNo. 2 [Svelte Final Form](https://github.com/jetrockets/svelte-final-form)
**評価：6/10**

[Final Form](https://final-form.org/)という、フレームワークに関係なく使えるフォームライブラリをSvelteで使いやすくしたラッパーです。`on:blur`, `on:focus`をいちいち指定する手間がかかるのが難点です。TypeScriptの型定義はあります。リポジトリを見るとまだテストケースがないようです。

```ts:サンプルコード（一部抜粋）
<Form {onSubmit} {validate} {initialValues} let:form let:state>
  <form on:submit|preventDefault={form.submit}>
    <Field name="firstName" let:input let:meta>
      <label for="firstName">First Name</label>
      <input
        name={input.name}
        on:blur={input.onBlur}
        on:focus={input.onFocus}
        on:input={(e) => input.onChange(e.target.value)}
        type="text"
        placeholder="Last Name"
        value={input.value} />
      {#if meta.touched && meta.error}
        <div>{meta.error}</div>
      {/if}
    </Field>
```
(https://github.com/jetrockets/svelte-final-form より抜粋)

## エントリーNo. 3 [Svelte Formly](https://github.com/arabdevelop/svelte-formly)
**評価：3/10**

なんと、フォーム要素をオブジェクトで宣言する設計になっています。scriptとtemplateを分業したい人には全くもってマッチしないでしょう。「フォーム要素の間に何か違う要素を入れたい」という細かい要望に対処できないのがなかなか厳しそう。TypeScriptの型定義がないのもいただけないです。

```ts:サンプルコード（一部抜粋）
<script>
  import { get } from "svelte/store";
  import { valuesForm, Field } from "svelte-formly";

  const fields = [
    {
      type: 'input',
      name: 'firstname',
      value: '',
      attributes: {
        type: 'text',
        label: 'Username',
        id: 'firstname',
        classes: ['form-control'],
        placeholder: 'Tap your first name',
      },
      rules: ['required', 'min:6'],
      messages: {
        required: 'Firstname field is required!',
        min: 'First name field must have more that 6 caracters!',
      },
    },
    {
      type: 'input',
      name: 'email',
      value: '',
      attributes: {
        type: 'email',
        id: 'email',
        placeholder: 'Tap your email',
      },
      rules: ['required', 'email'],
    },
// 略
</script>

<form
  on:submit|preventDefault="{onSubmit}"
  class="custom-form"
  style="--theme-color: {color}"
>
  <Field {fields} />
  <button class="btn btn-primary" type="submit">Submit</button>
</form>
```
(https://github.com/arabdevelop/svelte-formly#usage より)

## エントリーNo. 4 [Svelte Use Form](https://github.com/noahsalvi/svelte-use-form)
**評価：8/10**

圧倒的にシンプルな書き心地が特徴です。formという変数がstore($formと使う)とaction(use:formと使う)の両方の振る舞いをするというのが面白いです。初めて見たときは天才かと思いました。TypeScriptの型定義もあります。
ただ、現時点でテストケースがありません。

```ts:サンプルコード
<script>
  import { useForm, Hint, validators, minLength } from "svelte-use-form";

  const form = useForm();
</script>

<form use:form>
  <input name="title" use:validators={[minLength(5)]} />
  <Hint for="title" on="minLength" let:value>
    The title requires at least {value} characters.
  </Hint>

  <button disabled={!$form.valid}>Submit</button> <br />
</form>
```
(https://github.com/noahsalvi/svelte-use-form#minimal-example-repl より)

## エントリーNo. 5 [Felte](https://felte.dev/)
**評価：10/10**
Svelte Use Formと似た書き心地です。ドキュメントが非常に整備されています。また、動的にフォーム要素が増える場合に対応していたり、yupだけでなくzodやsuperstructといったスキーマに対応していたりと痒いところに手が届く仕様になっています。アクセシビリティについても考慮されていたり、公式が用意しているプラグインによって、エラーの表示形式が変更できたりと至りつくせりです。
TypeScriptで開発されており、型定義も標準装備されています。テストケースもしっかり書かれており、そのカバレッジは99%と非常に高いです。

```ts
<script>
  import { createForm } from 'felte';

  const { form } = createForm({
    onSubmit: (values) => {
      // ...
    },
  })
</script>

<form use:form>
  <input type="text" name="email">
  <input type="password" name="password">
  <input type="submit" value="Sign in">
</form>
```
(https://felte.dev/docs/svelte/getting-started より)

## エントリーNo. 6 [svelte-forms](https://github.com/chainlist/svelte-forms)
**評価9/10**
フィールド1つ1つに対して、`field`メソッドを使ってストアを作る必要があります。変数が増えてしまうというデメリットはあるものの、ボイラープレートが多すぎるというわけではなく、書き味は悪くないです。他のフォームライブラリとの違いは、`bind:value`などsvelteのreactive機能を使う方針をとっていることです。この方法にはUIライブラリと組み合わせやすいというメリットがあります。UIライブラリが提供するフォームコンポーネントは`bind:value`などbindを使う前提のものも多いためです。
TypeScriptの型定義もあり、テストケースも書かれている点も嬉しいです。

```ts:サンプルコード
<script>
  import { form, field } from 'svelte-forms';
  import { required } from 'svelte-forms/validators';

  const name = field('name', '', [required()]);
  const myForm = form(name);
</script>


<section>
  <input type="text" bind:value={$name.value} />

  <button disabled={!$myForm.valid}>Send form</button>
</section>
```
(https://chainlist.github.io/svelte-forms/ より)

# 結果発表
Svelte フォームライブラリ選手権 優勝はFelteです。
抽象化が非常にうまくなされている印象で、ベースが使いやすいだけでなく、細かいユースケースにも対応できる作りになっています。またTypeScriptで開発されていること、テストケースもしっかり書かれていることから、保守性も高いです。
Felteを使ってみて気に入った方はGitHubでスターをつけて開発者を応援しましょう。

# 更新履歴
## 2022/02/14
再調査しました。
エントリーNo. 6 [svelte-forms](https://github.com/chainlist/svelte-forms)を追加しました。
エントリNo. 1~4のライブラリに特に大きな変化はなく、相変わらずfelteが優勝でした。
felteは1.0.0が無事リリースされました。
https://github.com/pablo-abc/felte/blob/main/packages/felte/CHANGELOG.md#100