Form composition
Combine native form semantics with Liquidify controls and grouped visual structure.
Use a native <form> for submission semantics and Liquidify's Form for visual grouping. Form renders a grouped layout; it is not an HTML form and holds no values.
import { useState } from "react"
import { Button, Form, Picker, TextField, Toggle } from "@liquidify/react"
import "@liquidify/react/styles.css"
export function AccountSettings() {
const [name, setName] = useState("")
const [plan, setPlan] = useState("free")
const [updates, setUpdates] = useState(true)
const save = () => console.log({ name, plan, updates })
return (
<form onSubmit={(event) => event.preventDefault()}>
<Form>
<Form.Section title="Profile">
<label htmlFor="account-name">Display name</label>
<TextField id="account-name" name="name" value={name} onChange={setName} />
</Form.Section>
<Form.Section title="Preferences" footer="You can change these later.">
<Picker
aria-label="Plan"
items={[
{ value: "free", label: "Free" },
{ value: "pro", label: "Pro" },
]}
value={plan}
onChange={setPlan}
/>
<Toggle aria-label="Product updates" checked={updates} onChange={setUpdates} />
</Form.Section>
</Form>
<Button variant="borderedProminent" onPress={save}>Save</Button>
</form>
)
}Value ownership
TextField can participate in FormData through its native name. Picker, Toggle, Slider, and other composite controls do not emit hidden native values, so keep their values in React state and merge them into the submission payload.
Use either a controlled value plus onChange, or an uncontrolled defaultValue/defaultChecked. Do not switch modes after mount.
Labels and validation
Associate text fields with visible native labels. Give composite controls an aria-label or aria-labelledby. Connect validation copy with the documented aria-describedby, aria-invalid, or aria-errormessage surface rather than relying on color alone.
See the Forms concept for native submission details and the Controlled form example for a smaller recipe.