FormField

A small form composition primitive for real app forms. It connects labels, descriptions, errors, required state, and accessible control ids while supporting stacked, inline, split, card, section, and action layouts around existing inputs, selects, textareas, switches, and checkboxes.

Loading preview...

Examples

Practical examples and common states for the same installable component.

Sign In

Compact account form with labels, descriptions, actions, and remembered state.

examples/form-field-sign-in.tsx
import { IconKey, IconMail } from "@tabler/icons-react";
import { Button } from "@/components/wensity/button";
import { Checkbox } from "@/components/wensity/checkbox";
import {
FormActions,
FormField,
FormFieldControl,
} from "@/components/wensity/form-field";
import { Input } from "@/components/wensity/input";
export function SignInForm() {
return (
<div className="space-y-4">
<FormField label="Email" required>
<FormFieldControl asChild>
<Input
type="email"
autoComplete="email"
placeholder="you@company.com"
leftIcon={<IconMail stroke={1.75} />}
/>
</FormFieldControl>
</FormField>
<FormField label="Password" description="Use at least 12 characters." required>
<FormFieldControl asChild>
<Input
type="password"
autoComplete="current-password"
placeholder="Enter password"
leftIcon={<IconKey stroke={1.75} />}
/>
</FormFieldControl>
</FormField>
<FormActions align="between">
<Checkbox label="Remember device" defaultChecked fullWidth={false} />
<Button size="sm">Continue</Button>
</FormActions>
</div>
);
}

Split Profile

Two-column label and control rhythm for settings pages with longer helper text.

examples/form-field-split-profile.tsx
import { IconBuildingSkyscraper, IconUser } from "@tabler/icons-react";
import {
FormField,
FormFieldControl,
} from "@/components/wensity/form-field";
import { Input } from "@/components/wensity/input";
import { Textarea } from "@/components/wensity/textarea";
export function ProfileForm() {
return (
<div className="space-y-5">
<FormField
layout="split"
label="Display name"
description="Shown in comments, billing, and shared workspaces."
required
>
<FormFieldControl asChild>
<Input defaultValue="Parth Sharma" leftIcon={<IconUser stroke={1.75} />} />
</FormFieldControl>
</FormField>
<FormField
layout="split"
label="Company"
description="Used for invoices and team discovery."
optional
>
<FormFieldControl asChild>
<Input placeholder="Acme Inc." leftIcon={<IconBuildingSkyscraper stroke={1.75} />} />
</FormFieldControl>
</FormField>
<FormField
layout="split"
label="Bio"
description="Short internal context for collaborators."
>
<FormFieldControl asChild>
<Textarea minRows={3} resize="none" placeholder="Design systems and component infrastructure." />
</FormFieldControl>
</FormField>
</div>
);
}

Settings Section

Section wrapper for admin preferences with divided rows and mixed controls.

examples/form-field-settings-section.tsx
import { Checkbox } from "@/components/wensity/checkbox";
import {
FormField,
FormFieldControl,
FormSection,
} from "@/components/wensity/form-field";
import { Select } from "@/components/wensity/select";
import { Switch } from "@/components/wensity/switch";
export function WorkspaceControls() {
return (
<FormSection
title="Workspace controls"
description="A section wrapper for dense admin settings."
divided
>
<FormField
layout="inline"
label="Public profile"
description="Allow anyone with the link to view the workspace overview."
>
<FormFieldControl asChild>
<Switch defaultChecked label="Enabled" fullWidth={false} />
</FormFieldControl>
</FormField>
<FormField
layout="inline"
label="Invite permission"
description="Choose who can invite new members."
>
<FormFieldControl asChild>
<Select
defaultValue="admin"
variant="filled"
options={[
{ value: "owner", label: "Owners only" },
{ value: "admin", label: "Admins and owners" },
{ value: "member", label: "All members" },
]}
/>
</FormFieldControl>
</FormField>
<FormField
layout="inline"
label="Digest"
description="Send weekly activity reports to workspace admins."
>
<FormFieldControl asChild>
<Checkbox variant="toggle" defaultChecked label="Weekly" />
</FormFieldControl>
</FormField>
</FormSection>
);
}

Team Invite

Grid form with grouped select options, required fields, optional note, and actions.

examples/form-field-invite.tsx
import { IconMail } from "@tabler/icons-react";
import { Button } from "@/components/wensity/button";
import {
FormActions,
FormField,
FormFieldControl,
} from "@/components/wensity/form-field";
import { Input } from "@/components/wensity/input";
import { Select } from "@/components/wensity/select";
import { Textarea } from "@/components/wensity/textarea";
export function TeamInviteForm() {
return (
<div className="grid gap-4 sm:grid-cols-2">
<FormField label="Email" required>
<FormFieldControl asChild>
<Input type="email" placeholder="alex@company.com" leftIcon={<IconMail stroke={1.75} />} />
</FormFieldControl>
</FormField>
<FormField label="Role" required>
<FormFieldControl asChild>
<Select
defaultValue="developer"
options={[
{ value: "admin", label: "Admin" },
{ value: "developer", label: "Developer" },
{ value: "viewer", label: "Viewer" },
]}
/>
</FormFieldControl>
</FormField>
<FormField
className="sm:col-span-2"
label="Message"
description="Optional context included in the invite email."
optional
>
<FormFieldControl asChild>
<Textarea minRows={3} resize="none" placeholder="I added you to help review the new component set." />
</FormFieldControl>
</FormField>
<FormActions className="sm:col-span-2">
<Button variant="secondary">Save draft</Button>
<Button>Send invite</Button>
</FormActions>
</div>
);
}

Billing Error

Validation styling keeps required copy and error text tied to the right control.

examples/form-field-billing-error.tsx
import { IconCreditCard, IconMail } from "@tabler/icons-react";
import { Button } from "@/components/wensity/button";
import {
FormActions,
FormField,
FormFieldControl,
} from "@/components/wensity/form-field";
import { Input } from "@/components/wensity/input";
export function BillingContactForm() {
return (
<div className="space-y-4">
<FormField
label="Billing email"
error="Use a complete company email address."
required
>
<FormFieldControl asChild>
<Input type="email" defaultValue="billing@" leftIcon={<IconMail stroke={1.75} />} />
</FormFieldControl>
</FormField>
<FormField label="Payment method" description="Cards are encrypted before storage.">
<FormFieldControl asChild>
<Input placeholder="4242 4242 4242 4242" leftIcon={<IconCreditCard stroke={1.75} />} />
</FormFieldControl>
</FormField>
<FormActions>
<Button variant="destructive">Review errors</Button>
</FormActions>
</div>
);
}

Card Fields

Compact field cards for token creation, billing panels, and dense admin tools.

examples/form-field-card-fields.tsx
import {
FormField,
FormFieldControl,
} from "@/components/wensity/form-field";
import { Input } from "@/components/wensity/input";
import { Select } from "@/components/wensity/select";
import { Switch } from "@/components/wensity/switch";
export function TokenFields() {
return (
<div className="grid gap-3 sm:grid-cols-3">
<FormField layout="card" label="API token name" description="Short and searchable.">
<FormFieldControl asChild>
<Input placeholder="Production deploy" inputSize="sm" />
</FormFieldControl>
</FormField>
<FormField layout="card" label="Scope" description="Least privilege by default.">
<FormFieldControl asChild>
<Select
selectSize="sm"
defaultValue="read"
options={[
{ value: "read", label: "Read" },
{ value: "write", label: "Write" },
{ value: "admin", label: "Admin", disabled: true },
]}
/>
</FormFieldControl>
</FormField>
<FormField layout="card" label="Rotation" description="Keep token age low.">
<FormFieldControl asChild>
<Switch defaultChecked label="90 days" switchSize="sm" />
</FormFieldControl>
</FormField>
</div>
);
}