Componentes

Form

Padrão de formulário composto via Field (sem form.tsx).

Campos e formulárioscomposição

Carregando demonstração…

Código da demo
"use client";

import * as React from "react";
import { Button } from "@/components/ui/button";
import {
  Field,
  FieldDescription,
  FieldError,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSet,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";

export function Demo() {
  const [name, setName] = React.useState("");
  const [bio, setBio] = React.useState("");
  const [saved, setSaved] = React.useState(false);
  const [error, setError] = React.useState<string | null>(null);

  return (
    <form
      className="w-full max-w-md"
      noValidate
      onSubmit={(e) => {
        e.preventDefault();
        if (!name.trim()) {
          setError("Informe um nome.");
          setSaved(false);
          return;
        }
        setError(null);
        setSaved(true);
      }}
    >
      <FieldSet>
        <FieldLegend>Perfil</FieldLegend>
        <FieldDescription>Estado local — sem backend.</FieldDescription>
        <FieldGroup>
          <Field data-invalid={error ? true : undefined}>
            <FieldLabel htmlFor="form-name" required>
              Nome
            </FieldLabel>
            <Input
              id="form-name"
              required
              value={name}
              aria-invalid={error ? true : undefined}
              aria-describedby={error ? "form-name-error" : undefined}
              onChange={(e) => {
                setName(e.target.value);
                setSaved(false);
                if (error) setError(null);
              }}
            />
            {error ? (
              <FieldError id="form-name-error">{error}</FieldError>
            ) : (
              <FieldDescription>Como você quer ser chamado.</FieldDescription>
            )}
          </Field>
          <Field>
            <FieldLabel htmlFor="form-bio">Bio</FieldLabel>
            <Textarea
              id="form-bio"
              value={bio}
              onChange={(e) => {
                setBio(e.target.value);
                setSaved(false);
              }}
            />
          </Field>
          <Button type="submit">Salvar</Button>
          {saved ? (
            <p className="fe-caption text-success">Salvo localmente.</p>
          ) : null}
        </FieldGroup>
      </FieldSet>
    </form>
  );
}

Use

  • FieldSet e FieldLegend para agrupar; uma coluna em tela estreita.
  • Um único botão de envio primário, no fim.
  • Diga o que falta quando o envio não é possível.

Não use

  • Não desabilite o envio sem explicar o motivo.
  • Não peça dado que o produto não usa.
Acessibilidade
Labels, erros e agrupamento semântico com FieldSet.
Importar

Composição do app — não é exportada pelo pacote.

Neste repositório:

import { Field, FieldLabel, FieldGroup } from "@/components/ui/field"