TypeScript

Since the library is written in TypeScript and we put a lot of emphasis on the development experience, you can expect maximum TypeScript support. Types are automatically inferred from your Valibot schemas, providing type safety throughout your forms.

Type inference

Formisch uses Valibot's type inference to automatically derive TypeScript types from your schemas. You don't need to define separate types—they're inferred automatically.

<script lang="ts">
  import {
    createForm,
    Field,
    Form,
    type SubmitHandler,
  } from '@formisch/svelte';
  import * as v from 'valibot';

  const LoginSchema = v.object({
    email: v.pipe(v.string(), v.email()),
    password: v.pipe(v.string(), v.minLength(8)),
  });

  const loginForm = createForm({
    schema: LoginSchema,
  });

  // TypeScript knows the form structure from the schema
  // loginForm is of type FormStore<typeof LoginSchema>

  const handleSubmit: SubmitHandler<typeof LoginSchema> = (output) => {
    // output is fully typed based on LoginSchema
    console.log(output.email); // ✓ Type-safe
    console.log(output.username); // ✗ TypeScript error
  };
</script>

<Form of={loginForm} onsubmit={handleSubmit}>
  <!-- Form fields -->
</Form>

Input and output types

Valibot schemas can have different input and output types when using transformations. Formisch provides proper typing for both.

<script lang="ts">
  import {
    createForm,
    Field,
    Form,
    type SubmitHandler,
  } from '@formisch/svelte';
  import * as v from 'valibot';

  const ProfileSchema = v.object({
    age: v.pipe(
      v.string(), // Input type: string (from HTML input)
      v.transform((input) => Number(input)), // Output type: number
      v.number()
    ),
    birthDate: v.pipe(
      v.string(), // Input type: string (ISO date string)
      v.transform((input) => new Date(input)), // Output type: Date
      v.date()
    ),
  });

  const profileForm = createForm({
    schema: ProfileSchema,
  });

  const handleSubmit: SubmitHandler<typeof ProfileSchema> = (output) => {
    // output is: { age: number; birthDate: Date }
    console.log(output.age); // number
    console.log(output.birthDate); // Date
  };
</script>

<Form of={profileForm} onsubmit={handleSubmit}>
  <Field of={profileForm} path={['age']}>
    {#snippet children(field)}
      <!-- field.input is string -->
      <input {...field.props} value={field.input ?? ''} type="text" />
    {/snippet}
  </Field>

  <Field of={profileForm} path={['birthDate']}>
    {#snippet children(field)}
      <!-- field.input is string -->
      <input {...field.props} value={field.input ?? ''} type="date" />
    {/snippet}
  </Field>

  <button type="submit">Submit</button>
</Form>

Type-safe paths

Field paths are fully type-checked. TypeScript will provide autocompletion and catch invalid paths at compile time.

const UserSchema = v.object({
  profile: v.object({
    name: v.object({
      first: v.string(),
      last: v.string(),
    }),
    email: v.string(),
  }),
});

const userForm = createForm({ schema: UserSchema });

// ✓ Valid paths - TypeScript provides autocompletion
// <Field of={userForm} path={['profile', 'name', 'first']} />
// <Field of={userForm} path={['profile', 'email']} />

// ✗ Invalid paths - TypeScript error
// <Field of={userForm} path={['profile', 'age']} />
// <Field of={userForm} path={['username']} />

Type-safe props

To pass your form to another component via props, you can use the FormStore type along with your schema type to get full type safety.

<script lang="ts">
  import { createForm, Form, type FormStore } from '@formisch/svelte';
  import * as v from 'valibot';

  const LoginSchema = v.object({
    email: v.pipe(v.string(), v.email()),
    password: v.pipe(v.string(), v.minLength(8)),
  });

  const loginForm = createForm({
    schema: LoginSchema,
  });
</script>

<FormContent of={loginForm} />

<!-- In another file -->
<script lang="ts">
  import { Form, type FormStore, type SubmitHandler } from '@formisch/svelte';
  import * as v from 'valibot';

  const LoginSchema = v.object({
    email: v.pipe(v.string(), v.email()),
    password: v.pipe(v.string(), v.minLength(8)),
  });

  type FormContentProps = {
    of: FormStore<typeof LoginSchema>;
  };

  let { of } = $props<FormContentProps>();

  const handleSubmit: SubmitHandler<typeof LoginSchema> = (output) => console.log(output);
</script>

<Form of={of} onsubmit={handleSubmit}>
  <!-- Form fields -->
</Form>

Generic field components

You can create generic field components with proper TypeScript typing using the FormStore type with Valibot's GenericSchema.

<script lang="ts" generics="T extends v.GenericSchema<{ email: string }>">
  import { type FormStore, useField } from '@formisch/svelte';
  import * as v from 'valibot';

  type EmailInputProps = {
    of: FormStore<T>;
  };

  let { of } = $props<EmailInputProps>();

  const field = useField(of, { path: ['email'] });
</script>

<div>
  <label>
    Email
    <input {...field.props} value={field.input ?? ''} type="email" />
  </label>
  {#if field.errors}
    <div>{field.errors[0]}</div>
  {/if}
</div>

The v.GenericSchema<{ email: string }> type ensures that the form passed to the component must have an email field of type string. TypeScript will catch any type mismatches at compile time.

Available types

Most types you need can be imported from @formisch/svelte. You can find all available types in our API reference.

Contributors

Thanks to all the contributors who helped make this page better!

  • GitHub profile picture of @fabian-hiller

Partners

Thanks to our partners who support the project ideally and financially.

Sponsors

Thanks to our GitHub sponsors who support the project financially.

  • GitHub profile picture of @vasilii-kovalev
  • GitHub profile picture of @saturnonearth
  • GitHub profile picture of @ruiaraujo012
  • GitHub profile picture of @hyunbinseo
  • GitHub profile picture of @nickytonline
  • GitHub profile picture of @KubaJastrz
  • GitHub profile picture of @andrewmd5
  • GitHub profile picture of @Thanaen
  • GitHub profile picture of @caegdeveloper
  • GitHub profile picture of @bmoyroud
  • GitHub profile picture of @dslatkin