antuikit's Form is Ant Design's Form, installed into your project as a file you own. Form.Item, Form.List, Form.ErrorList and the hooks all come from the one import.
The kit adds one behavior: feedbackIcons defaults to your icon pack's four validation icons. The label tooltip icon comes from AntuikitProvider, and only items that pass tooltip show it.
Install
npx antuikit@latest init
npx antuikit@latest add form
The first command installs AntuikitProvider and the theme tokens, and the second adds Form.
Import
From @/components/ui/form, never from antd. The file is yours once it is installed, so the import path is a path in your project rather than a package.
import { Form } from "@/components/ui/form"
The instance, and the compound parts
Form.useForm() hands you the store before the form renders, which is how anything outside the JSX reads a value, sets one, or resets. The rest of the namespace is the parts below and the other hooks.
1
const [form] = Form.useForm(); // the form instance
2
const parent = Form.useFormInstance(); // the nearest parent Form's instance
3
const name = Form.useWatch("name", form); // re-renders when this field changes
4
const { status } = Form.Item.useStatus(); // the nearest item's validation status
Form.Item is the field: give it a name and it registers a value, derives an id, points the label at that id and owns the error message. Without a name it is layout only.
Form.List is a field array with a render prop, and renders no element of its own. Unlike Form and Form.Item, it takes no type argument: its values are part of the parent form's type.
Form.ErrorList renders messages that belong to no single field. A Form.List's own "at least one row" rule is the case it exists for.
Form.useWatch over onValuesChange when you only care about one field. It subscribes to that path rather than re-rendering on every edit.
Use Form.useForm() instead of a ref. It gives you the same instance before the form mounts. To type a ref anyway, use FormProps['ref'] rather than antd's FormRef.
The axes
Set these on the Form instead of on each field. variant, size and disabled reach every control inside it.
layout takes horizontal (the default), vertical and inline. An Item can override it with horizontal or vertical only: inline is an arrangement of the whole form.
labelAlign takes left and right (the default), and only means anything under horizontal.
size takes small, medium (the default) and large, and is inherited by every control inside.
variant takes outlined (the default), filled, borderless and underlined, likewise inherited.
requiredMark takes true, optional, false or a render function. Only optional puts real text beside the label.
Flags
Five booleans. The first two decide what the store keeps after a field or the whole form unmounts.
preserve (default true) keeps a field's value in the store after the field unmounts, which means a conditional field submits a stale value after it has left the screen. Set preserve={false} on the Item of a conditional field. Do not set it on a Form.List field: removing a row already removes its values, and the flag logs a warning.
clearOnDestroy (default false) empties the whole store when the Form unmounts. It matters when the instance outlives the form, as in a Modal that keeps its subtree alive between records.
colon (default true) draws a colon after each label under horizontal. A trailing colon you type into the label yourself is stripped, so the two cannot double up.
labelWrap (default false) lets a long label wrap onto a second line instead of being clipped by its column.
disabled (default false) disables every control inside. It also takes the whole form out of the tab order, so a permanently read-only view wants readOnly on the controls instead.
Slots
Form's own slot is children, which also accepts a render function of (values, form). That function re-runs on every store change, so it is the expensive route: an Item with shouldUpdate scopes the same thing to one field. The slots that matter day to day belong to Form.Item.
label is the field's name on screen and, given a name, its accessible name too.
extra is the durable hint under the control. It gets the id <fieldId>_extra and joins the control's aria-describedby, and an error does not replace it.
help takes over the message slot. A static hint written there is replaced when a rule fails, so put lasting hints in extra.
tooltip hangs a help icon off the label. Its trigger is a span with tabIndex={-1}, so only a mouse user can open it. Its icon comes from AntuikitProvider.
hasFeedback shows a status icon from your icon pack inside the control. The object form, hasFeedback={{ icons }}, takes its own icons and wins over the Form's feedbackIcons. On a Select the status icon sits next to the arrow.
Events
onValuesChange
onFieldsChange
onFinish
onFinishFailed
Styling
classNames and styles take the same keys, one per part of every Item inside the Form. Use them before a descendant selector, since each key is part of the API.
1
<Form
2
classNames={{
3
root: "…",
4
label: "…",
5
content: "…",
6
help: "…",
7
helpItem: "…",
8
extra: "…",
9
}}
10
/>
A Tailwind class on one of these keys wins over the default styles without !important. Writing a component token's CSS variable on the element does nothing, so set the token in your theme or style the part instead.
API Reference
Form
Form's props. Rows marked antuikit in the Version column are added by the kit.