FELTE

An extensible form library for Svelte and Solid

Use any value you want, this is just a demo.

Validation!

Use your favorite validation library (or create your own strategy). Officially supporting Yup, Zod and Superstruct!

Error reporting!

With Felte's built-in reporting capabilities you'll need minimal set up to handle your errors! Use any of our reporter packages or build your own to suit your needs.

Easy styling!

No fighting with custom components or CSS variables. Felte works with plain HTML tags. Write your form as you would any HTML5 form!

Using Tippy.js

<script>
  import { createForm } from 'felte';
  import reporter from '@felte/reporter-tippy';
  import { validator } from '@felte/validator-yup';
  import * as yup from 'yup';

  const schema = yup.object({
    email: yup.string().email().required(),
    password: yup.string().required(),
  });

  const { form } = createForm({
    onSubmit: () => {
      throw {
        password: 'This password already exists',
      };
    },
    onError: error => error,
    extend: [validator, reporter()],
    validateSchema: schema,
  });
</script>

<form use:form>
  <label>
    <span>Email:</span>
    <input name="email" type="email">
  </label>
  <label>
    <span>Password:</span>
    <input name="password" type="password">
  </label>
  <button type="submit">Fail to sign in</button>
</form>

Using the DOM

<script>
  import { createForm } from 'felte';
  import reporter from '@felte/reporter-dom';
  import { validator } from '@felte/validator-yup';
  import * as yup from 'yup';

  const schema = yup.object({
    email: yup.string().email().required(),
    password: yup.string().required(),
  });

  const { form } = createForm({
    onSubmit: () => {
      throw {
        password: 'This password already exists',
      };
    },
    onError: error => error,
    extend: [validator, reporter({ single: true })],
    validateSchema: schema,
  });
</script>

<form use:form>
  <label>
    <span>Email:</span>
    <input name="email" type="email" aria-describedby="email-validation">
  </label>
  <div id="email-validation" data-felte-reporter-dom-for="email" />
  <label>
    <span>Password:</span>
    <input name="password" type="password" aria-describedby="password-validation">
  </label>
  <div id="password-validation" data-felte-reporter-dom-for="password" />
  <button type="submit">Fail to sign in</button>
</form>

Using a Svelte component

<script>
  import { createForm } from 'felte';
  import { svelteReporter as reporter, ValidationMessage } from '@felte/reporter-svelte';
  import { validator } from '@felte/validator-yup';
  import * as yup from 'yup';

  const schema = yup.object({
    email: yup.string().email().required(),
    password: yup.string().required(),
  });

  const { form } = createForm({
    onSubmit: () => {
      throw {
        password: 'This password already exists',
      };
    },
    onError: error => error,
    extend: [validator, reporter],
    validateSchema: schema,
  });
</script>

<form use:form>
  <label>
    <span>Email:</span>
    <input id="email" name="email" type="email" aria-describedby="email-validation">
  </label>
  <!-- You can use a placeholder slot -->
  <ValidationMessage for="email" let:messages={message}>
    <span id="email-validation" class="validation-message" aria-live="polite">
      {message}
    </span>
    <span slot="placeholder" id="email-validation" class="validation-message" />
  </ValidationMessage>
  <label>
    <span>Password:</span>
    <input id="password" name="password" type="password" aria-describedby="password-validation">
  </label>
  <!-- Or handle any undefined/falsy message yourself -->
  <ValidationMessage for="password" let:messages={message}>
    <span id="password-validation" class="validation-message" aria-live="polite">
      {message || ''}
    </span>
  </ValidationMessage>
  <button type="submit">Fail to sign in</button>
</form>