Dynamic form
SvForm with conditional fields: visible / disabled derived from other values (hidden fields skip validation and drop from the payload), async field validation, an async onSubmit with a loading button, and a Reset that restores the initial values.
A live, editable Svelte 5 component example from the SvGrid gallery (Layout). Read the SvGrid UI component docs for the full API.
About this example
SvForm with conditional fields in Svelte 5: visible and disabled are functions of the other values, hidden fields skip validation and drop out of the payload, a field can validate asynchronously, an async onSubmit shows a loading button, and showReset adds a Reset that restores the initial values.
SvForm - a DYNAMIC form: fields appear/disable based on other values (visible / disabled as (values) => boolean), an async onSubmit shows a loading state, and a Reset button restores the initial values. Hidden fields are skipped in validation and left out of the submitted payload.
Imports, features and API used
Imports: @svgrid/grid
Frequently asked questions
How do I show a field only when another has a value?
Give the field visible: (values) => values.contact === 'phone'. When it is hidden it is not validated and is left out of the submitted object.
How does async validation work?
Give the field asyncValidate, a function returning a promise of a message or null, debounced by asyncDebounce; the field shows a pending state until it resolves and submit is blocked while any check is outstanding.
What happens during an async submit?
The submit button shows its loading state and the form is disabled until the promise from onSubmit settles; a rejection surfaces as an error.
Related documentation
Related articles
- Avoiding Layout Thrash in Custom Grid Cells - Layout thrash from interleaved DOM reads and writes is the most common cause of scroll jank in grids with custom cells - here is how to find it and design your way out of it.
- Bundle Size of Svelte Data Grids - How to Compare - README bundle numbers are nearly useless. Here is how to measure the real delta a data grid adds to your Svelte app, and why feature-gated architectures change the math entirely.
- Saved Views - Persist Grid Layout and Filters - Give users named, switchable snapshots of column order, sorting, filters, and grouping - persisted to localStorage or a server adapter - using SvGrid's createNamedViews API.
Source code (323-form-dynamic.svelte)
<script lang="ts">
/**
* SvForm - a DYNAMIC form: fields appear/disable based on other values
* (`visible` / `disabled` as `(values) => boolean`), an async `onSubmit` shows
* a loading state, and a Reset button restores the initial values. Hidden
* fields are skipped in validation and left out of the submitted payload.
*/
import { SvForm, rules } from '@svgrid/grid'
import type { FormField } from '@svgrid/grid'
// Pretend server check: these usernames are "taken".
const TAKEN = ['ada', 'grace', 'alan']
async function usernameFree(v: string): Promise<string | null> {
if (!v) return null
await new Promise((r) => setTimeout(r, 600))
return TAKEN.includes(v.trim().toLowerCase()) ? 'That username is taken' : null
}
const fields: FormField[] = [
{ name: 'username', label: 'Username', required: true, full: true,
asyncValidate: usernameFree },
{ name: 'contact', label: 'Preferred contact', type: 'select', required: true, options: [
{ value: 'email', label: 'Email' }, { value: 'phone', label: 'Phone' }, { value: 'none', label: 'Do not contact me' },
] },
// Shown + required only for the matching contact method.
{ name: 'email', label: 'Email address', type: 'email', required: true, full: true,
rules: [rules.email()], visible: (v) => v.contact === 'email' },
{ name: 'phone', label: 'Phone number', type: 'tel', required: true, full: true,
visible: (v) => v.contact === 'phone' },
{ name: 'marketing', label: 'Send me product updates', type: 'switch' },
// Appears only when marketing is on.
{ name: 'topics', label: 'Topics', type: 'select', full: true, visible: (v) => !!v.marketing, options: [
{ value: 'releases', label: 'Releases' }, { value: 'tips', label: 'Tips & tutorials' }, { value: 'events', label: 'Events' },
] },
// Disabled when the user opted out of contact entirely.
{ name: 'notes', label: 'Anything else?', type: 'textarea', full: true,
disabled: (v) => v.contact === 'none' },
]
let result = $state<string>('')
// Async submit: the button shows a loading state until this settles.
async function save(values: Record<string, unknown>) {
result = ''
await new Promise((r) => setTimeout(r, 900))
result = JSON.stringify(values, null, 2)
}
</script>
<div class="wrap">
<header>
<h2>Dynamic form</h2>
<p>
Fields show, hide and disable from other values; <strong>Username</strong> runs an
async "is it taken?" check (try <code>ada</code>); the submit button loads while an
async save runs; Reset restores the start. Hidden fields skip validation and are
left out of the payload - switch the contact method and watch the fields change.
</p>
</header>
<div class="card">
<SvForm
{fields}
columns={2}
submitLabel="Save preferences"
showReset
initial={{ contact: 'email', marketing: false }}
onSubmit={save}
/>
</div>
{#if result}
<pre class="result">{result}</pre>
{/if}
</div>
<style>
.wrap { padding: 20px; max-width: 620px; display: flex; flex-direction: column; gap: 16px; }
header h2 { margin: 0 0 4px; font-size: 20px; font-weight: 700; }
header p { margin: 0; color: var(--sg-muted, #64748b); font-size: 13.5px; line-height: 1.5; }
.card { padding: 20px; border: 1px solid var(--sg-border, #e2e8f0); border-radius: 12px; }
.result { margin: 0; padding: 14px; background: var(--sg-header-bg, #f8fafc); border: 1px solid var(--sg-border, #e2e8f0); border-radius: 10px; font-size: 12px; overflow: auto; }
</style>More Layout examples
- Account & security settings console - A real SaaS settings surface composed from the UI kit: SvMenubar app bar, promise + Undo-action toasts on save, SvPopconfirm on destructive rows, SvHoverCard teammate previews, and frame input adornments (leading icons, prefix affixes, masked API key). Profile / Team / Security tabs over SvCard + SvStat.
- Invoice builder - An adornment-heavy money form: currency prefixes, % suffixes, masked tax IDs (frame adornments), line items with SvPopconfirm delete + Undo action toasts, live SvStat totals, an SvHoverCard tax hint, and a promise toast on Send. Pure UI-kit composition.
- Operations KPI dashboard - A KPI console: SvStat + SvSparkline tiles, an SvGauge SLA dial, SvHoverCard drill-down previews per service, an SvMenubar toolbar (View / Range / Actions) and a promise toast on Refresh. Pure UI-kit composition, no grid dependency.
- Form: rich field types - SvForm reaching the whole input suite from one schema - phone, country, mask, combobox, radio, slider, tags, datetime and file - plus per-field help text, a readonly field, column span, and a promise toast on submit.
- Form: cascading fields - SvForm dependent selects - a child list derives from the parent value via a function `options`, and `dependsOn` clears the child when the parent changes so a stale selection never lingers. Country -> State -> City.