Github

Input

A text input component for forms and user data entry with built-in styling and accessibility features.

import { Input } from "@/components/ui/input";

export function InputDemo() {
	return (
		<div className="w-full max-w-sm">
			<Input type="email" placeholder="Email" />
		</div>
	);
}

Installation

pnpm dlx shadcn@latest add https://herocn.dev/r/input.json

Usage

import { Input } from "@/components/ui/input"
<Input placeholder="Email" type="email" />

Examples

Variants

Use the variant prop for a softer background on the secondary style.

import { Input } from "@/components/ui/input";

export function InputVariants() {
	return (
		<div className="flex w-full max-w-sm flex-col gap-3">
			<Input placeholder="Default" />
			<Input variant="secondary" placeholder="Secondary" />
		</div>
	);
}

States

Disabled inputs are non-interactive. Set aria-invalid for error styling (for example when validation fails).

import { Input } from "@/components/ui/input";

export function InputStates() {
	return (
		<div className="flex w-full max-w-sm flex-col gap-3">
			<Input disabled placeholder="Disabled" />
			<Input aria-invalid placeholder="Invalid" defaultValue="Invalid value" />
		</div>
	);
}

With field

Pair Input with Field, FieldLabel, and FieldDescription for accessible labels and helper text.

Choose a unique username for your account.

import {
	Field,
	FieldDescription,
	FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function InputField() {
	return (
		<div className="w-full max-w-sm">
			<Field>
				<FieldLabel htmlFor="input-field-username">Username</FieldLabel>
				<Input
					id="input-field-username"
					type="text"
					placeholder="Enter your username"
				/>
				<FieldDescription>
					Choose a unique username for your account.
				</FieldDescription>
			</Field>
		</div>
	);
}

Field group

Stack multiple fields and actions with FieldGroup.

We'll send updates to this address.

import { Button } from "@/components/ui/button";
import {
	Field,
	FieldDescription,
	FieldGroup,
	FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function InputFieldgroup() {
	return (
		<div className="w-full max-w-sm">
			<FieldGroup>
				<Field>
					<FieldLabel htmlFor="fieldgroup-name">Name</FieldLabel>
					<Input id="fieldgroup-name" placeholder="Jordan Lee" />
				</Field>
				<Field>
					<FieldLabel htmlFor="fieldgroup-email">Email</FieldLabel>
					<Input
						id="fieldgroup-email"
						type="email"
						placeholder="[email protected]"
					/>
					<FieldDescription>
						We&apos;ll send updates to this address.
					</FieldDescription>
				</Field>
				<Field orientation="horizontal">
					<Button type="reset" variant="outline">
						Reset
					</Button>
					<Button type="submit">Submit</Button>
				</Field>
			</FieldGroup>
		</div>
	);
}

Label with badge

Use a badge in the label row for status or metadata (for example a feature flag).

import { Badge } from "@/components/ui/badge";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function InputBadge() {
	return (
		<div className="w-full max-w-sm">
			<Field>
				<FieldLabel
					htmlFor="input-badge"
					className="flex w-full items-center gap-2"
				>
					Webhook URL
					<Badge variant="default" className="ml-auto">
						Beta
					</Badge>
				</FieldLabel>
				<Input
					id="input-badge"
					type="url"
					placeholder="https://api.example.com/webhook"
				/>
			</Field>
		</div>
	);
}

Input group

Prefix or suffix text and icons with InputGroup, InputGroupAddon, and InputGroupInput.

https://
import { InfoIcon } from "lucide-react";
import { Field, FieldLabel } from "@/components/ui/field";
import {
	InputGroup,
	InputGroupAddon,
	InputGroupInput,
	InputGroupText,
} from "@/components/ui/input-group";

export function InputInputGroup() {
	return (
		<div className="w-full max-w-sm">
			<Field>
				<FieldLabel htmlFor="input-group-url">Website URL</FieldLabel>
				<InputGroup>
					<InputGroupInput id="input-group-url" placeholder="example.com" />
					<InputGroupAddon>
						<InputGroupText>https://</InputGroupText>
					</InputGroupAddon>
					<InputGroupAddon align="inline-end">
						<InfoIcon />
					</InputGroupAddon>
				</InputGroup>
			</Field>
		</div>
	);
}

Button group

Place the input beside actions using ButtonGroup.

import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";

export function InputButtonGroup() {
	return (
		<div className="w-full max-w-sm">
			<Field>
				<FieldLabel htmlFor="input-button-group">Search</FieldLabel>
				<ButtonGroup>
					<Input
						id="input-button-group"
						variant="secondary"
						placeholder="Type to search..."
					/>
					<Button variant="tertiary">Search</Button>
				</ButtonGroup>
			</Field>
		</div>
	);
}

In surface

Use variant="secondary" when placing an Input inside a card or panel surface for a subtler, shadow-free appearance that blends with the surface background.

import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import { Surface } from "@/components/ui/surface";

export function InputInSurface() {
	return (
		<Surface className="flex w-full max-w-sm flex-col gap-3 rounded-3xl p-6">
			<Field>
				<FieldLabel htmlFor="input-in-surface-message">
					What's your name?
				</FieldLabel>
				<Input id="input-in-surface-message" variant="secondary" />
			</Field>
			<Button>Submit</Button>
		</Surface>
	);
}

RTL

لن نشارك بريدك الإلكتروني مع أي شخص.

"use client";

import {
	type Translations,
	useTranslation,
} from "@/components/language-selector";
import {
	Field,
	FieldDescription,
	FieldLabel,
} from "@/components/ui/field";
import { Input } from "@/components/ui/input";

const translations: Translations = {
	en: {
		dir: "ltr",
		values: {
			nameLabel: "Full name",
			namePlaceholder: "John Doe",
			emailLabel: "Email address",
			emailPlaceholder: "[email protected]",
			emailDescription: "We'll never share your email with anyone.",
		},
	},
	ar: {
		dir: "rtl",
		values: {
			nameLabel: "الاسم الكامل",
			namePlaceholder: "محمد علي",
			emailLabel: "البريد الإلكتروني",
			emailPlaceholder: "[email protected]",
			emailDescription: "لن نشارك بريدك الإلكتروني مع أي شخص.",
		},
	},
	he: {
		dir: "rtl",
		values: {
			nameLabel: "שם מלא",
			namePlaceholder: "ישראל ישראלי",
			emailLabel: "כתובת דוא״ל",
			emailPlaceholder: "[email protected]",
			emailDescription: "לעולם לא נשתף את הדוא״ל שלך עם אחרים.",
		},
	},
};

export function InputRtl() {
	const { dir, language, t } = useTranslation(translations, "ar");

	return (
		<div
			className="flex w-full max-w-sm flex-col gap-4"
			lang={language}
			dir={dir}
		>
			<Field>
				<FieldLabel htmlFor="form-input-rtl-name">{t.nameLabel}</FieldLabel>
				<Input
					type="text"
					id="form-input-rtl-name"
					placeholder={t.namePlaceholder}
				/>
			</Field>
			<Field>
				<FieldLabel htmlFor="form-input-rtl-email">{t.emailLabel}</FieldLabel>
				<Input
					type="email"
					id="form-input-rtl-email"
					placeholder={t.emailPlaceholder}
				/>
				<FieldDescription>{t.emailDescription}</FieldDescription>
			</Field>
		</div>
	);
}

API Reference

Input

The Input component accepts all props from a native <input> and @base-ui/react Input plus the following:

PropTypeDefault
variant"default" | "secondary"
"default"