Field
Form field wrapper with label, hint, and error.
Preview
Used as the prefix for issue IDs. Cannot be changed later.
Code
import { Field, FieldLabel, FieldDescription } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
export function Example() {
return (
<Field>
<FieldLabel>Project key</FieldLabel>
<Input placeholder="PRIZM" />
<FieldDescription>
Used as the prefix for issue IDs. Cannot be changed later.
</FieldDescription>
</Field>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| name | string | — | Field name, used for form submission. |
| validate | (value: unknown) => string | null | — | Sync validator returning an error message or null. |
| validationMode | "onBlur" | "onChange" | "onBlur" | When validation runs. |
| className | string | — | Additional Tailwind classes, merged with the component's defaults via cn(). |
Sub-components
FieldLabel— Label associated automatically via Field context.FieldControl— Slot for the actual control (Input, Textarea).FieldDescription— Helper / hint text.FieldError— Validation error message — auto-shown when invalid.FieldValidity— Render-prop access to validity state for custom error UIs.Built on Base UI Field. Wires `aria-describedby` automatically between label, control, hint, and error.
All components also accept standard HTML attributes for their root element (e.g. `id`, `aria-*`, `data-*`, event handlers) and forward `ref` where applicable.
Source
The full implementation of components/ui/field.tsx is below — copy it into your project and own it. Some components import the cn helper from lib/utils or other primitives; copy those too.
"use client";
import { cn } from "@/lib/utils";
import { Field as BaseField } from "@base-ui-components/react/field";
import type { ComponentPropsWithoutRef } from "react";
export function Field({ className, ...props }: ComponentPropsWithoutRef<typeof BaseField.Root>) {
// Bake the label/control/hint/error rhythm in (matches PrizmField's setSpacing(6)),
// so a field has vertical spacing without the consumer adding space-y-*.
return <BaseField.Root className={cn("grid gap-1.5", className)} {...props} />;
}
export function FieldLabel({
className,
...props
}: ComponentPropsWithoutRef<typeof BaseField.Label>) {
return (
<BaseField.Label
className={cn("text-sm font-medium leading-tight text-fg", className)}
{...props}
/>
);
}
export function FieldDescription({
className,
...props
}: ComponentPropsWithoutRef<typeof BaseField.Description>) {
return (
<BaseField.Description
className={cn("text-xs leading-relaxed text-fg-muted", className)}
{...props}
/>
);
}
export function FieldError({
className,
...props
}: ComponentPropsWithoutRef<typeof BaseField.Error>) {
return (
<BaseField.Error className={cn("text-xs leading-relaxed text-danger", className)} {...props} />
);
}
export const FieldControl = BaseField.Control;
export const FieldValidity = BaseField.Validity;JavaFX
Ships in the PRIZM JavaFX library for thick-client C3 apps as PrizmField (extends VBox). Run the gallery to see it natively.
import design.prizm.fx.controls.PrizmField;
PrizmField()
PrizmField(String labelText)
PrizmField(String labelText, Node control)| Prop | Type | Default | Description |
|---|---|---|---|
| setLabel | (String) → void | — | Set the field label text. |
| setControl | (Node) → void | — | Set/replace the wrapped control; pairs it with the label (labelFor). |
| setDescription | (String) → void | — | Helper / hint text below the control; null or "" hides it. |
| setError | (String) → void | — | Validation error below the field; null or "" clears it. |
| labelNode | () → PrizmLabel | — | The label, for further styling. |
A composite over VBox (no stock 1:1 control), styled by the .prizm-field rules in prizm.css. Carries no validation engine (a web concern) — drive the error line with setError. Mirrors Field + FieldLabel + FieldControl + FieldDescription + FieldError in components/ui/field.tsx.