Starting your winter arc? 🥶 Get 25% off every plan and template with

Field

A composable field shell carrying labels, help text, required markers, and error, success, and validation states.

Loading preview

Examples

Practical examples and common states for the same installable component.

Error

Validation errors with clear messaging and alert role.

Requires: buttoninput

examples/field-error.tsx
import { IconExclamationCircle, IconMail } from "@tabler/icons-react";
import { Button } from "@/components/wensity/button";
import { Field, FieldControl } from "@/components/wensity/field";
import { Input } from "@/components/wensity/input";
export function ErrorFields() {
return (
<div className="space-y-4">
<Field label="Email" required error="Please enter a valid email address.">
<FieldControl asChild>
<Input type="email" defaultValue="not-an-email" leftIcon={<IconMail stroke={1.75} />} />
</FieldControl>
</Field>
<Field label="Password" required error="Must be at least 8 characters.">
<FieldControl asChild>
<Input type="password" defaultValue="123" leftIcon={<IconExclamationCircle stroke={1.75} />} />
</FieldControl>
</Field>
<Button className="w-full" disabled>Fix errors to continue</Button>
</div>
);
}

Success

Confirm valid input with green success feedback.

Requires: buttoninput

examples/field-success.tsx
import { IconCheck, IconMail } from "@tabler/icons-react";
import { Button } from "@/components/wensity/button";
import { Field, FieldControl } from "@/components/wensity/field";
import { Input } from "@/components/wensity/input";
export function SuccessFields() {
return (
<div className="space-y-4">
<Field label="Username" success="This username is available.">
<FieldControl asChild>
<Input defaultValue="ksparth12" leftIcon={<IconCheck stroke={1.75} />} />
</FieldControl>
</Field>
<Field label="Email" success="Verification link sent.">
<FieldControl asChild>
<Input type="email" defaultValue="parth@wensity.com" leftIcon={<IconMail stroke={1.75} />} />
</FieldControl>
</Field>
<Button className="w-full">Save</Button>
</div>
);
}

Horizontal

Labels and controls aligned side by side.

Requires: inputselect

examples/field-horizontal.tsx
import { IconMail } from "@tabler/icons-react";
import { Field, FieldControl } from "@/components/wensity/field";
import { Input } from "@/components/wensity/input";
import { Select } from "@/components/wensity/select";
const roleOptions = [
{ label: "Workspace", options: [{ value: "admin", label: "Admin" }, { value: "developer", label: "Developer" }] },
];
export function HorizontalFields() {
return (
<div className="space-y-5">
<Field label="Display name" layout="horizontal">
<FieldControl asChild>
<Input defaultValue="Parth Sharma" />
</FieldControl>
</Field>
<Field label="Email" layout="horizontal" description="Primary contact address.">
<FieldControl asChild>
<Input type="email" defaultValue="parth@wensity.com" leftIcon={<IconMail stroke={1.75} />} />
</FieldControl>
</Field>
<Field label="Role" layout="horizontal" optional>
<FieldControl asChild>
<Select defaultValue="developer" options={roleOptions} />
</FieldControl>
</Field>
</div>
);
}

All States

Required, optional, error, and success together.

Requires: buttoninput

examples/field-combined.tsx
import { IconCheck, IconMail, IconUser } from "@tabler/icons-react";
import { Button } from "@/components/wensity/button";
import { Field, FieldControl } from "@/components/wensity/field";
import { Input } from "@/components/wensity/input";
export function AllStates() {
return (
<div className="space-y-4">
<Field label="Full name" required>
<FieldControl asChild>
<Input placeholder="Your name" leftIcon={<IconUser stroke={1.75} />} />
</FieldControl>
</Field>
<Field label="Team" optional description="Leave blank for a personal account.">
<FieldControl asChild>
<Input placeholder="Team name" />
</FieldControl>
</Field>
<Field label="Email" required error="This email is already registered.">
<FieldControl asChild>
<Input type="email" defaultValue="taken@wensity.com" leftIcon={<IconMail stroke={1.75} />} />
</FieldControl>
</Field>
<Field label="Username" success="Great choice, this username is yours.">
<FieldControl asChild>
<Input defaultValue="ksparth12" leftIcon={<IconCheck stroke={1.75} />} />
</FieldControl>
</Field>
<Button className="w-full">Create account</Button>
</div>
);
}

Installation

Run the following command

~/your-project
pnpm dlx wensity@latest add field

Usage

Import Field from @/components/wensity/field and render it anywhere in your React or Next.js app. The source lands in your own repo, so every prop, class and animation below is yours to edit.

app/example.tsx
import {
Field,
FieldControl,
} from "@/components/wensity/field";
import { Input } from "@/components/wensity/input";
export function FieldDemo() {
return (
<Field label="Email" required>
<FieldControl asChild>
<Input type="email" placeholder="you@company.com" />
</FieldControl>
</Field>
);
}

Props

PropTypeDefaultDescription
labelReact.ReactNode-Field label, wired to the control it wraps.
descriptionReact.ReactNode-Supporting copy under the label.
errorReact.ReactNode-Error message, with invalid styling applied.
successReact.ReactNode-Success message, with valid styling applied.
requiredboolean-Marks the field required and updates the label.
optionalboolean-Marks the field optional with a muted label suffix.
layout"stack" | "horizontal""stack"Whether the label sits above the control or beside it.
childrenReact.ReactNode-The control this field wraps.