Skip to main content

Import

import { Field } from '@tanstack/svelte-form'
// or
const form = createForm(...)
// use form.Field

Props

name
string
required
The name of the field. Must match a key in your form’s data type.
validators
FieldValidators
Validation functions for the field.
defaultValue
TValue
Default value for the field (overrides form defaultValues)
children
Snippet
required
Svelte snippet that receives field and state as parameters

Snippet Parameters

The children snippet receives an object with:
field
FieldApi
The field API instance
state
FieldState
The current field state

Usage

Basic Field

<script lang="ts">
  import { createForm } from '@tanstack/svelte-form'
  
  const form = createForm({
    defaultValues: {
      name: '',
    },
    onSubmit: async ({ value }) => console.log(value),
  })
</script>

<form.Field name="name">
  {#snippet children({ field, state })}
    <label>
      Name:
      <input
        value={state.value}
        oninput={(e) => field.handleChange(e.currentTarget.value)}
        onblur={field.handleBlur}
      />
    </label>
  {/snippet}
</form.Field>

With Validation

<form.Field
  name="email"
  validators={{
    onChange: ({ value }) => {
      if (!value) return 'Email is required'
      if (!value.includes('@')) return 'Invalid email address'
      return undefined
    },
  }}
>
  {#snippet children({ field, state })}
    <div>
      <input
        type="email"
        value={state.value}
        oninput={(e) => field.handleChange(e.currentTarget.value)}
        onblur={field.handleBlur}
        placeholder="Enter your email"
      />
      {#if state.meta.isTouched && state.meta.errors.length > 0}
        <em style="color: red;">{state.meta.errors[0]}</em>
      {/if}
    </div>
  {/snippet}
</form.Field>

Async Validation

<form.Field
  name="username"
  validators={{
    onChangeAsyncDebounceMs: 500,
    onChangeAsync: async ({ value, signal }) => {
      // Check if username is available
      const response = await fetch(`/api/check-username?name=${value}`, {
        signal,
      })
      const available = await response.json()
      return available ? undefined : 'Username is taken'
    },
  }}
>
  {#snippet children({ field, state })}
    <div>
      <input
        value={state.value}
        oninput={(e) => field.handleChange(e.currentTarget.value)}
      />
      {#if state.meta.isValidating}
        <span>Checking...</span>
      {/if}
      {#if state.meta.errors.length > 0}
        <em>{state.meta.errors[0]}</em>
      {/if}
    </div>
  {/snippet}
</form.Field>

Array Fields

<script lang="ts">
  const form = createForm({
    defaultValues: {
      items: [''],
    },
    onSubmit: async ({ value }) => console.log(value),
  })
</script>

<form.Field name="items" mode="array">
  {#snippet children({ field, state })}
    <div>
      {#each state.value as item, i (i)}
        <form.Field name={`items[${i}]`}>
          {#snippet children({ field: itemField, state: itemState })}
            <input
              value={itemState.value}
              oninput={(e) => itemField.handleChange(e.currentTarget.value)}
            />
            <button type="button" onclick={() => field.removeValue(i)}>
              Remove
            </button>
          {/snippet}
        </form.Field>
      {/each}
      
      <button type="button" onclick={() => field.pushValue('')}>
        Add Item
      </button>
    </div>
  {/snippet}
</form.Field>

Standalone Field

You can use the Field component from the package directly:
<script lang="ts">
  import { createForm, Field } from '@tanstack/svelte-form'
  
  const form = createForm({
    defaultValues: { name: '' },
    onSubmit: async ({ value }) => console.log(value),
  })
</script>

<Field form={form} name="name">
  {#snippet children({ field, state })}
    <input
      value={state.value}
      oninput={(e) => field.handleChange(e.currentTarget.value)}
    />
  {/snippet}
</Field>

TypeScript

The Field component is fully typed:
interface FormData {
  name: string
  age: number
  tags: string[]
}

const form = createForm<FormData>({
  defaultValues: {
    name: '',
    age: 0,
    tags: [],
  },
  onSubmit: async ({ value }) => console.log(value),
})

// TypeScript knows 'name' is a string
<form.Field name="name">
  {#snippet children({ field, state })}
    <!-- state.value is typed as string -->
    <input value={state.value} />
  {/snippet}
</form.Field>

// TypeScript knows 'tags' is an array
<form.Field name="tags" mode="array">
  {#snippet children({ field, state })}
    <!-- state.value is typed as string[] -->
    <!-- field.pushValue expects a string -->
    <button onclick={() => field.pushValue('')}>Add</button>
  {/snippet}
</form.Field>

See Also

Build docs developers (and LLMs) love