shadcn.io is not affiliated with official shadcn/ui
Shadcn Field for React and Tailwind
Container for inputs, labels, and helper text.
Choose a unique username for your account.
Installation
bunx --bun shadcn@latest add https://kit.dev/r/field.jsonpnpm dlx shadcn@latest add https://kit.dev/r/field.jsonnpx shadcn@latest add https://kit.dev/r/field.jsonyarn shadcn@latest add https://kit.dev/r/field.jsonThis component depends on Separator. Install it first if you haven't already.
Install the following dependencies:
bun add @ark-ui/react tailwind-variantspnpm add @ark-ui/react tailwind-variantsnpm install @ark-ui/react tailwind-variantsyarn add @ark-ui/react tailwind-variantsCopy and paste the following code into your project.
"use client";
import { ark } from "@ark-ui/react/factory";
import {
Field as ArkField,
useFieldContext as useArkFieldContext,
} from "@ark-ui/react/field";
import { Fieldset as ArkFieldset } from "@ark-ui/react/fieldset";
import type React from "react";
import { tv, type VariantProps } from "tailwind-variants";
import { cn } from "@/lib/utils";
import { Separator } from "@/components/ui/separator";
export const useField = useArkFieldContext;
const fieldVariants = tv({
base: [
"group/field",
"w-full",
"flex gap-2",
"data-invalid:text-destructive",
"dark:data-invalid:text-destructive-foreground",
],
variants: {
orientation: {
vertical: ["flex-col *:w-full [&>.sr-only]:w-auto"],
horizontal: [
"flex-row items-center",
"*:data-[slot=field-label]:flex-auto",
"has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
],
responsive: [
"flex-col *:w-full [&>.sr-only]:w-auto",
"@md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto",
"@md/field-group:*:data-[slot=field-label]:flex-auto",
"@md/field-group:has-[>[data-slot=field-content]]:items-start",
"@md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
],
},
reverse: {
true: [
"data-[orientation=horizontal]:flex-row-reverse",
"data-[orientation=vertical]:flex-col-reverse",
"data-[orientation=responsive]:flex-col-reverse",
"data-[orientation=responsive]:@md/field-group:flex-row-reverse",
],
},
},
defaultVariants: {
orientation: "vertical",
reverse: false,
},
});
interface FieldProps
extends React.ComponentProps<typeof ArkField.Root>,
VariantProps<typeof fieldVariants> {}
export const Field = (props: FieldProps) => {
const {
orientation = "vertical",
reverse = false,
className,
...rest
} = props;
return (
<ArkField.Root
className={cn(fieldVariants({ orientation, reverse }), className)}
data-orientation={orientation}
data-slot="field"
{...rest}
/>
);
};
export const FieldSet = (
props: React.ComponentProps<typeof ArkFieldset.Root>
) => {
const { className, ...rest } = props;
return (
<ArkFieldset.Root
className={cn(
"flex flex-col gap-6",
"has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
className
)}
data-slot="field-set"
{...rest}
/>
);
};
interface FieldLegendProps
extends React.ComponentProps<typeof ArkFieldset.Legend> {
/**
* The variant of the legend.
*/
variant?: "legend" | "label";
}
export const FieldLegend = (props: FieldLegendProps) => {
const { variant = "legend", className, ...rest } = props;
return (
<ArkFieldset.Legend
className={cn(
"mb-3 font-medium",
"data-[variant=legend]:text-base",
"data-[variant=label]:text-sm",
className
)}
data-slot="field-legend"
data-variant={variant}
{...rest}
/>
);
};
export const FieldGroup = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"group/field-group @container/field-group",
"flex w-full flex-col gap-4",
"data-[data-slot=checkbox-group]:gap-3",
"*:data-[slot=field-group]:gap-4",
className
)}
data-slot="field-group"
{...rest}
/>
);
};
export const FieldContent = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"group/field-content",
"flex flex-1 flex-col gap-1.5",
"leading-snug",
className
)}
data-slot="field-content"
{...rest}
/>
);
};
export const FieldLabel = (
props: React.ComponentProps<typeof ArkField.Label>
) => {
const { className, ...rest } = props;
return (
<ArkField.Label
className={cn(
"group/field-label peer/field-label",
"select-none font-medium text-sm leading-snug",
"flex w-fit gap-1",
"has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-xl has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5",
"has-data-[state=checked]:border-primary has-data-[state=checked]:bg-primary/5",
"group-data-disabled/field:opacity-64",
"dark:has-data-[state=checked]:bg-primary/10",
className
)}
data-slot="field-label"
{...rest}
/>
);
};
export const FieldRequiredIndicator = (
props: React.ComponentProps<typeof ark.span>
) => {
const { className, children, ...rest } = props;
return (
<ArkField.RequiredIndicator
aria-hidden
className={cn(
"select-none text-destructive text-sm",
"dark:text-destructive-foreground",
className
)}
data-slot="field-required-indicator"
{...rest}
>
{children ?? "*"}
</ArkField.RequiredIndicator>
);
};
export const FieldTitle = (props: React.ComponentProps<typeof ark.div>) => {
const { className, ...rest } = props;
return (
<ark.div
className={cn(
"w-fit",
"flex items-center gap-2",
"font-medium text-sm leading-snug",
"group-data-[disabled=true]/field:opacity-64",
className
)}
data-slot="field-title"
{...rest}
/>
);
};
export const FieldDescription = (props: React.ComponentProps<typeof ark.p>) => {
const { className, ...rest } = props;
return (
<ark.p
className={cn(
"pointer-events-none",
"font-normal text-muted-foreground text-sm leading-normal",
"group-has-data-[orientation=horizontal]/field:text-balance",
"@md/field-group:group-data-[orientation=responsive]/field:text-balance",
"nth-last-2:-mt-1 last:mt-0 [[data-variant=legend]+&]:-mt-1.5",
"in-[[data-slot=field]:has([data-slot=radio-group-item])]:ms-6 in-[[data-slot=field]:has([data-slot=radio-group-item])]:-mt-1.5!",
"[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
className
)}
data-slot="field-description"
{...rest}
/>
);
};
export const FieldSeparator = (props: React.ComponentProps<typeof ark.div>) => {
const { className, children, ...rest } = props;
return (
<ark.div
className={cn(
"relative",
"h-5",
"-my-2 group-data-[variant=outline]/field-group:-mb-2",
"text-sm",
className
)}
data-content={!!children}
data-slot="field-separator"
{...rest}
>
<Separator className="absolute inset-0 top-1/2" />
{!!children && (
<span
className={cn(
"relative block",
"w-fit",
"mx-auto px-2",
"bg-background",
"text-muted-foreground text-sm"
)}
>
{children}
</span>
)}
</ark.div>
);
};
export const FieldHelper = (
props: React.ComponentProps<typeof ArkField.HelperText>
) => {
const { className, ...rest } = props;
return (
<ArkField.HelperText
className={cn("text-muted-foreground text-sm", className)}
data-slot="field-helper"
{...rest}
/>
);
};
export const FieldError = (
props: React.ComponentProps<typeof ArkField.ErrorText>
) => {
const { className, ...rest } = props;
return (
<ArkField.ErrorText
className={cn(
"font-normal text-destructive text-sm",
"dark:text-destructive-foreground",
className
)}
data-slot="field-error"
{...rest}
/>
);
};Update the import paths to match your project setup.
Usage
import {
Field,
FieldDescription,
FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";<Field className="w-full max-w-xs">
<FieldLabel>Username</FieldLabel>
<Input placeholder="John Doe" />
<FieldDescription>
Choose a unique username for your account.
</FieldDescription>
</Field>Demos
Autocomplete Field
Checkbox Field
Checkbox Group Field
Combobox Field
Combobox Multiple Field
Disabled Field
Field Group
Input Group
Number Input
Orientation Horizontal
Orientation Vertical
Radio Group Field
Required Field
Select Field
Slider Field
Switch Field
Textarea Field
With Error
API Reference
Field
div
PropType
AttributeType
FieldSet
div
PropType
AttributeType
FieldLegend
PropType
AttributeType
FieldGroup
div
PropType
AttributeType
FieldContent
div
PropType
AttributeType
FieldLabel
label
PropType
AttributeType
FieldRequiredIndicator
span
PropType
AttributeType
FieldTitle
div
PropType
AttributeType
FieldDescription
p
PropType
AttributeType
FieldSeparator
div
PropType
AttributeType
FieldHelper
span
PropType
AttributeType
FieldError
span
PropType
AttributeType
useField
PropType