<input> element with consistent default styling — height, border, rounded corners, and a focus ring — while forwarding a ref and accepting every standard HTML input attribute.
Import
Basic usage
TypeScript type
Input accepts all props from React.InputHTMLAttributes<HTMLInputElement>. You do not need a custom props interface — any attribute you would put on a native <input> works here.
Default behavior
typedefaults to"text"if you omit it.- A
refis forwarded viaReact.forwardRef, so you can attach a ref directly:
Default styling
The following Tailwind classes are applied by default:| Category | Classes |
|---|---|
| Layout | flex h-9 w-full |
| Shape | rounded-md border |
| Background | bg-white |
| Spacing | px-3 py-1.5 |
| Typography | text-sm |
| Placeholder | placeholder:text-gray-400 |
| Focus | focus:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 |
| Disabled | disabled:cursor-not-allowed disabled:opacity-50 |
Customizing with className
Pass a className to extend or override defaults. Classes are merged with cn() (clsx + tailwind-merge), so Tailwind conflicts are resolved automatically:
Input types
Because all HTML input attributes pass through, you can use anytype value:
Disabled state
Set thedisabled attribute to prevent interaction. The input renders with cursor-not-allowed and opacity-50:
Props
The HTML input type. Accepts any valid
<input type> value — text, email, password, number, file, and so on.Additional Tailwind classes merged via
cn(). Use this to extend or override default styles.A forwarded ref. Attach it to access the underlying
<input> DOM node imperatively.Disables the input and applies
cursor-not-allowed opacity-50.Placeholder text shown when the field is empty. Styled with
text-gray-400.All remaining native input attributes —
value, onChange, onBlur, aria-*, data-*, id, name, required, and so on.Real-world example
Search input with controlled state (from FeaturesPage.tsx)
Accessibility
- Visible label — Pair the input with a
<label>using a matchinghtmlFor/idor usearia-labelwhen a visible label is not present, as shown in the search example above. - Disabled state —
disabled:cursor-not-allowedanddisabled:opacity-50provide both a visual and cursor cue. - Focus ring —
focus-visible:ring-2 focus-visible:ring-indigo-500produces a keyboard-visible focus indicator, satisfying WCAG 2.4.7. The ring only appears on keyboard focus, not on mouse click.
Always provide an accessible label for every input. Use a
<label> element or aria-label when a visible label is not shown.