Field
Server-safeLayout wrapper that groups a label, control, description, and error text.
Live preview
Rendered from the installed VEXA source — the same files you receive.
Account notifications only.
Lowercase letters and dashes only.
Installation
Adds the component source to your project. Registry dependencies are pulled in automatically.
npx shadcn@latest add https://vexa.valfiguer.com/r/field.jsonInstalls to components/vexa/field.tsx
npm dependencies
No external npm dependencies.
Registry dependencies
- @vexa/utils
- @vexa/label
Other VEXA items this component needs. Install them first — they are resolved automatically by the CLI.
Source
Copy this into its target path, or install it with the command above.
components/vexa/field.tsx
import type * as React from "react";
import { cn } from "@/lib/utils";
/**
* Field — a vertical layout wrapper that groups a `Label`, a control, and its
* description / error text with consistent spacing.
*
* RSC-safe by design: it uses no React context or hooks, so the accessible
* wiring stays explicit and server-renderable. Associate parts by id:
*
* ```tsx
* <Field>
* <Label htmlFor="email">Email</Label>
* <Input id="email" aria-describedby="email-hint" aria-invalid={hasError} />
* <FieldDescription id="email-hint">Work address preferred.</FieldDescription>
* {hasError ? <FieldError id="email-error">Enter a valid email.</FieldError> : null}
* </Field>
* ```
*/
export function Field({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="field"
className={cn("flex flex-col gap-2", className)}
{...props}
/>
);
}
/**
* FieldDescription — supplementary help text. Reference its `id` from the
* control's `aria-describedby`.
*/
export function FieldDescription({
className,
...props
}: React.ComponentProps<"p">) {
return (
<p
data-slot="field-description"
className={cn("text-sm text-vexa-muted-foreground", className)}
{...props}
/>
);
}
/**
* FieldError — an error message. Reference its `id` from the control's
* `aria-describedby` (or `aria-errormessage`) and set `aria-invalid` on the
* control. Errors should name both the problem and the recovery action.
* A leading icon or `"Error:"` prefix keeps meaning from relying on color.
*/
export function FieldError({
className,
children,
...props
}: React.ComponentProps<"p">) {
if (children == null || children === false) return null;
return (
<p
data-slot="field-error"
className={cn(
"flex items-center gap-1.5 text-sm font-medium text-vexa-danger",
"[&_svg]:size-4 [&_svg]:shrink-0",
className,
)}
{...props}
>
{children}
</p>
);
}
Details
Example
tsx
<Field><Label htmlFor="n">Name</Label><Input id="n" /></Field>Accessibility
No context/hooks — accessible wiring stays explicit (id + aria-describedby). Errors name the problem and recovery; not color-only.