Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
7
Help us understand the problem. What is going on with this article?
@kiyoshiro

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

はじめに

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

そういったときに必要になるのがフォームライブラリです。執筆時点(2021年5月)で,
Reactでは React Hook Formが覇権を握っているようです。VueではVeeValidateあたりが有名所でしょうか。
さて、近年その勢いを増しているSvelteですが、フォームライブラリが乱立しています。本記事ではSvelteのフォームライブラリを比較していきます。

注意点

  • https://bestofsvelte.com/t/form こちらのサイトに乗っているフォームライブラリを比較します。
  • 各ライブラリは2021年5月時点での最新バージョンを扱います。
  • 筆者のSvelte歴は業務で半年ほど使ったくらいです。
  • 主に実装のしやすさ(ボイラープレートの少なさ)を重視します
  • 筆者がTypeScript好きであるため、型定義が同梱されているかを割と重要視します。
  • サンプルコードはすべてライブラリの公式ページからお借りしています。
  • GitHubのスター数やnpmのダウンロード数はあまり参考にしません。筆者の経験上、Svelte界隈のライブラリはスター数とクオリティが一致していないことが多いためです。(Svelteがまだ比較的新しいフレームワークなのが原因かも)
  • 10段階評価をしていますが、とくに決まった指標はありません。筆者の感覚です。

選手権スタート

エントリーNo.1 Svelte forms lib

評価:7/10

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

サンプルコード
  <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>

エントリーNo.2 Svelte Final Form

評価:6/10

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

サンプルコード(一部抜粋)
<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>

エントリーNo. 3 Svelte Formly

評価:3/10

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

サンプルコード(一部抜粋)
<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>

エントリーNo. 4 Svelte Use Form

評価:8/10

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

サンプルコード
<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>

エントリーNo.5 Felte

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

<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>

結果発表

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

7
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
kiyoshiro
バックエンド寄りのフロントエンジニア。Svelteを推している。

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
7
Help us understand the problem. What is going on with this article?