Skip to content
UI
Language:en Author:AtomForge

Forms

Recipe: edit a display name

Complete page component, rendered beneath the application's Root. This example validates locally and records a local snapshot; it does not claim to save to a server.

<script lang="ts">
  import { Button, Field, Input } from '@atom-forge/ui';

  const id = $props.id();
  let name = $state('');
  let submitted = $state(false);
  let savedName = $state<string | undefined>();
  const error = $derived(submitted && !name.trim() ? 'Enter a display name.' : undefined);

  function submit(event: SubmitEvent) {
    event.preventDefault();
    submitted = true;
    if (!name.trim()) return;
    savedName = name.trim();
  }
</script>

<form onsubmit={submit} novalidate class="flex flex-col gap-4 max-w-md">
  <Field label="Display name" for={id} required error={error}>
    <Input
      id={id}
      name="displayName"
      bind:value={name}
      required
      invalid={!!error}
      aria-invalid={!!error}
      aria-describedby={error ? `${id}-error` : undefined}
    />
    {#snippet errorSnippet()}
      {#if error}<span id={`${id}-error`}>{error}</span>{/if}
    {/snippet}
  </Field>
  <Button type="submit" label="Save locally"/>
  <p role="status">{savedName === undefined ? '' : `Local snapshot: ${savedName}`}</p>
</form>

Field.for and the input ID establish the label association. The error snippet supplies an ID because Field does not generate aria-describedby connections. Always supplying errorSnippet suppresses Field hints even when the snippet is empty; this recipe intentionally has no hint. novalidate lets the demonstrated application rule run instead of native required-field blocking.

Application responsibilities

  • Implement full validation, server-side validation, serialization, persistence, permissions, and submission error handling. Field's required marker and Input's invalid styling do not implement those rules.
  • For async submission, maintain pending/error state, prevent duplicate requests, and use Button loading/disabled; only announce success after persistence succeeds.
  • Convert numeric strings explicitly. Keep Select ID types consistent (string and number are not interchangeable); Select has no public onchange prop.
  • Own reset/dirty state, accessible help/error associations, focus on invalid fields, and native form integration for controls that do not forward form attributes.
  • For async options, implement throttling, failure handling, and stale-response protection in the source; these are not a form engine feature.