Basic form controls including buttons, inputs, checkboxes, switches, and form organization components with built-in accessibility and interaction states.
A basic button component with interaction states and polymorphic rendering.
/**
* Basic button component with interaction states
* @param props - Button properties including standard HTML button attributes
*/
function Button<TTag extends keyof JSX.IntrinsicElements = 'button'>(
props: ButtonProps<TTag>
): JSX.Element;
interface ButtonProps<TTag extends keyof JSX.IntrinsicElements = 'button'>
extends PolymorphicProps<TTag> {
/** Whether the button is disabled */
disabled?: boolean;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Button type for form submission */
type?: 'button' | 'submit' | 'reset';
/** Render prop providing interaction state */
children?: React.ReactNode | ((props: ButtonRenderProps) => React.ReactNode);
}
interface ButtonRenderProps {
/** Whether button is disabled */
disabled: boolean;
/** Whether button is being hovered */
hover: boolean;
/** Whether button has focus */
focus: boolean;
/** Whether button is being pressed */
active: boolean;
/** Whether button has autofocus */
autofocus: boolean;
}Usage Examples:
import { Button } from "@headlessui/react";
// Basic button
<Button className="px-4 py-2 bg-blue-500 text-white rounded">
Click me
</Button>
// Button with render props for dynamic styling
<Button>
{({ hover, focus, active, disabled }) => (
<span className={`
px-4 py-2 rounded transition-colors
${disabled ? 'bg-gray-300 cursor-not-allowed' : 'bg-blue-500'}
${hover && !disabled ? 'bg-blue-600' : ''}
${focus ? 'ring-2 ring-blue-300' : ''}
${active ? 'bg-blue-700' : ''}
`}>
Click me
</span>
)}
</Button>
// Polymorphic button as link
<Button as="a" href="/profile" className="text-blue-500 underline">
Go to Profile
</Button>A checkbox input component with form integration and indeterminate state support.
/**
* Checkbox input component with form integration
* @param props - Checkbox properties including form attributes
*/
function Checkbox<TType = string>(
props: CheckboxProps<TType>
): JSX.Element;
interface CheckboxProps<TType = string> extends PolymorphicProps<'input'> {
/** Value for form submission */
value?: TType;
/** Whether checkbox is disabled */
disabled?: boolean;
/** Whether checkbox is in indeterminate state */
indeterminate?: boolean;
/** Controlled checked state */
checked?: boolean;
/** Default checked state for uncontrolled usage */
defaultChecked?: boolean;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Associated form ID */
form?: string;
/** Form field name */
name?: string;
/** Change handler receiving checked state */
onChange?: (checked: boolean) => void;
/** Tab index */
tabIndex?: number;
/** Render prop providing interaction and state */
children?: React.ReactNode | ((props: CheckboxRenderProps) => React.ReactNode);
}
interface CheckboxRenderProps {
/** Current checked state */
checked: boolean;
/** Whether currently changing state */
changing: boolean;
/** Whether has focus */
focus: boolean;
/** Whether being pressed */
active: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has autofocus */
autofocus: boolean;
/** Whether disabled */
disabled: boolean;
/** Whether in indeterminate state */
indeterminate: boolean;
}Usage Examples:
import { useState } from "react";
import { Checkbox } from "@headlessui/react";
// Controlled checkbox
function ControlledExample() {
const [enabled, setEnabled] = useState(false);
return (
<Checkbox checked={enabled} onChange={setEnabled}>
{({ checked }) => (
<span className={`${checked ? 'bg-blue-500' : 'bg-gray-200'}
relative inline-block w-6 h-6 rounded border-2`}>
{checked && (
<svg className="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
)}
</span>
)}
</Checkbox>
);
}
// Uncontrolled checkbox with form integration
<form>
<Checkbox name="newsletter" value="yes" defaultChecked>
Subscribe to newsletter
</Checkbox>
</form>
// Indeterminate checkbox (for "select all" scenarios)
<Checkbox indeterminate={someSelected && !allSelected}>
Select All
</Checkbox>A text input component with form integration and validation states.
/**
* Text input component with form integration
* @param props - Input properties including standard HTML input attributes
*/
function Input<TTag extends keyof JSX.IntrinsicElements = 'input'>(
props: InputProps<TTag>
): JSX.Element;
interface InputProps<TTag extends keyof JSX.IntrinsicElements = 'input'>
extends PolymorphicProps<TTag> {
/** Whether input is disabled */
disabled?: boolean;
/** Whether input is in invalid state */
invalid?: boolean;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Render prop providing interaction state */
children?: React.ReactNode | ((props: InputRenderProps) => React.ReactNode);
}
interface InputRenderProps {
/** Whether disabled */
disabled: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has focus */
focus: boolean;
/** Whether has autofocus */
autofocus: boolean;
/** Whether in invalid state */
invalid: boolean;
}Usage Examples:
import { Input, Field, Label, Description } from "@headlessui/react";
// Basic input with styling
<Input
type="email"
placeholder="Enter your email"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500"
/>
// Input with render props for dynamic styling
<Input>
{({ focus, invalid, hover }) => (
<input
className={`
w-full px-3 py-2 border rounded-md transition-colors
${invalid ? 'border-red-500' : 'border-gray-300'}
${focus ? 'ring-2 ring-blue-500' : ''}
${hover && !focus ? 'border-gray-400' : ''}
`}
placeholder="Username"
/>
)}
</Input>
// Input in form context
<Field>
<Label>Email address</Label>
<Input
type="email"
name="email"
required
className="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md"
/>
<Description>We'll never share your email</Description>
</Field>A toggle switch component with form integration.
/**
* Toggle switch component
* @param props - Switch properties including form attributes
*/
function Switch<TTag extends keyof JSX.IntrinsicElements = 'button'>(
props: SwitchProps<TTag>
): JSX.Element;
interface SwitchProps<TTag extends keyof JSX.IntrinsicElements = 'button'>
extends PolymorphicProps<TTag> {
/** Current checked state */
checked?: boolean;
/** Default checked state for uncontrolled usage */
defaultChecked?: boolean;
/** Change handler receiving checked state */
onChange?: (checked: boolean) => void;
/** Form field name */
name?: string;
/** Form value when checked */
value?: string;
/** Associated form ID */
form?: string;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Whether switch is disabled */
disabled?: boolean;
/** Tab index */
tabIndex?: number;
/** Render prop providing interaction and state */
children?: React.ReactNode | ((props: SwitchRenderProps) => React.ReactNode);
}
interface SwitchRenderProps {
/** Whether switch is checked */
checked: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has focus */
focus: boolean;
/** Whether being pressed */
active: boolean;
/** Whether has autofocus */
autofocus: boolean;
/** Whether currently changing state */
changing: boolean;
/** Whether disabled */
disabled: boolean;
}Usage Examples:
import { useState } from "react";
import { Switch } from "@headlessui/react";
// Toggle switch with custom styling
function ToggleExample() {
const [enabled, setEnabled] = useState(false);
return (
<Switch checked={enabled} onChange={setEnabled}>
{({ checked }) => (
<span className={`${checked ? 'bg-blue-600' : 'bg-gray-200'}
relative inline-flex h-6 w-11 items-center rounded-full
transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500`}>
<span className={`${checked ? 'translate-x-6' : 'translate-x-1'}
inline-block h-4 w-4 transform rounded-full bg-white transition`} />
</span>
)}
</Switch>
);
}
// Switch with form integration
<form>
<Switch name="notifications" value="enabled" defaultChecked>
Enable notifications
</Switch>
</form>A native select element wrapper with enhanced styling capabilities.
/**
* Native select element wrapper
* @param props - Select properties including standard HTML select attributes
*/
function Select<TTag extends keyof JSX.IntrinsicElements = 'select'>(
props: SelectProps<TTag>
): JSX.Element;
interface SelectProps<TTag extends keyof JSX.IntrinsicElements = 'select'>
extends PolymorphicProps<TTag> {
/** Whether select is disabled */
disabled?: boolean;
/** Whether select is in invalid state */
invalid?: boolean;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Render prop providing interaction state */
children?: React.ReactNode | ((props: SelectRenderProps) => React.ReactNode);
}
interface SelectRenderProps {
/** Whether disabled */
disabled: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has focus */
focus: boolean;
/** Whether being pressed */
active: boolean;
/** Whether has autofocus */
autofocus: boolean;
/** Whether in invalid state */
invalid: boolean;
}A textarea element wrapper with enhanced styling capabilities.
/**
* Textarea element wrapper
* @param props - Textarea properties including standard HTML textarea attributes
*/
function Textarea<TTag extends keyof JSX.IntrinsicElements = 'textarea'>(
props: TextareaProps<TTag>
): JSX.Element;
interface TextareaProps<TTag extends keyof JSX.IntrinsicElements = 'textarea'>
extends PolymorphicProps<TTag> {
/** Whether textarea is disabled */
disabled?: boolean;
/** Whether textarea is in invalid state */
invalid?: boolean;
/** Whether to auto-focus on mount */
autoFocus?: boolean;
/** Render prop providing interaction state */
children?: React.ReactNode | ((props: TextareaRenderProps) => React.ReactNode);
}
interface TextareaRenderProps {
/** Whether disabled */
disabled: boolean;
/** Whether being hovered */
hover: boolean;
/** Whether has focus */
focus: boolean;
/** Whether has autofocus */
autofocus: boolean;
/** Whether in invalid state */
invalid: boolean;
}A field wrapper that provides form field context and disabled state propagation.
/**
* Field wrapper that provides form field context
* @param props - Field properties
*/
function Field<TTag extends keyof JSX.IntrinsicElements = 'div'>(
props: FieldProps<TTag>
): JSX.Element;
interface FieldProps<TTag extends keyof JSX.IntrinsicElements = 'div'>
extends PolymorphicProps<TTag> {
/** Whether field is disabled (propagates to children) */
disabled?: boolean;
/** Render prop providing field state */
children?: React.ReactNode | ((props: FieldRenderProps) => React.ReactNode);
}
interface FieldRenderProps {
/** Whether field is disabled */
disabled: boolean;
}A fieldset wrapper for grouping related form fields.
/**
* Fieldset wrapper for grouping form fields
* @param props - Fieldset properties including standard HTML fieldset attributes
*/
function Fieldset<TTag extends keyof JSX.IntrinsicElements = 'fieldset'>(
props: FieldsetProps<TTag>
): JSX.Element;
interface FieldsetProps<TTag extends keyof JSX.IntrinsicElements = 'fieldset'>
extends PolymorphicProps<TTag> {
/** Whether fieldset is disabled (affects all contained fields) */
disabled?: boolean;
/** Render prop providing fieldset state */
children?: React.ReactNode | ((props: FieldsetRenderProps) => React.ReactNode);
}
interface FieldsetRenderProps {
/** Whether fieldset is disabled */
disabled: boolean;
}A legend component for fieldsets (wrapper around Label).
/**
* Legend component for fieldsets
* @param props - Legend properties
*/
function Legend<TTag extends keyof JSX.IntrinsicElements = 'legend'>(
props: LegendProps<TTag>
): JSX.Element;
interface LegendProps<TTag extends keyof JSX.IntrinsicElements = 'legend'>
extends PolymorphicProps<TTag> {
/** Standard legend props inherit from Label */
}Usage Examples:
import { Field, Fieldset, Legend, Label, Input, Checkbox, Description } from "@headlessui/react";
// Form with field organization
<form>
<Fieldset>
<Legend>Personal Information</Legend>
<Field>
<Label>First Name</Label>
<Input name="firstName" required />
<Description>Enter your first name</Description>
</Field>
<Field>
<Label>Last Name</Label>
<Input name="lastName" required />
</Field>
</Fieldset>
<Fieldset disabled={isSubmitting}>
<Legend>Preferences</Legend>
<Field>
<Checkbox name="newsletter">
Subscribe to newsletter
</Checkbox>
</Field>
</Fieldset>
</form>A specialized button that closes its containing component (Dialog, Popover, etc.).
/**
* Button that closes its containing component
* @param props - CloseButton properties (inherits all ButtonProps)
*/
function CloseButton<TTag extends keyof JSX.IntrinsicElements = 'button'>(
props: CloseButtonProps<TTag>
): JSX.Element;
interface CloseButtonProps<TTag extends keyof JSX.IntrinsicElements = 'button'>
extends ButtonProps<TTag> {
/** All props inherited from Button component */
}Usage Examples:
import { Dialog, DialogPanel, CloseButton } from "@headlessui/react";
<Dialog open={isOpen} onClose={() => setIsOpen(false)}>
<DialogPanel>
<h2>Dialog Title</h2>
<p>Dialog content...</p>
{/* Automatically closes the dialog when clicked */}
<CloseButton className="absolute top-2 right-2 p-1">
<XMarkIcon className="w-5 h-5" />
</CloseButton>
</DialogPanel>
</Dialog>// Base polymorphic props for all components
interface PolymorphicProps<TTag extends keyof JSX.IntrinsicElements = 'div'> {
/** Element or component to render as */
as?: TTag;
/** Children content or render prop function */
children?: React.ReactNode | ((props: any) => React.ReactNode);
}
// Common interaction states
interface BaseInteractionState {
disabled: boolean;
hover: boolean;
focus: boolean;
active: boolean;
autofocus: boolean;
}