Input
Migration guide for Input from HeroUI v2 to v3
Refer to the v3 Input documentation for complete API reference, styling guide, and advanced examples. This guide only focuses on migrating from HeroUI v2.
Overview
The Input component in HeroUI v3 has been simplified to a primitive input element. For form fields with labels, validation, and error messages, use the TextField compound component instead.
Key Change: Input → TextField
v2: Input was a full-featured component with built-in label, description, error messages, validation, variants, colors, sizes, etc.
v3: Input is now a primitive component (just the input element). For form fields, use TextField which wraps Input with Label, Description, and FieldError components.
When to Use Input vs TextField
Use TextField (Most Cases)
Use TextField when you need:
- Labels
- Descriptions
- Error messages
- Validation
- Form integration
Use Input (Primitive Only)
Use Input when you need:
- Just a basic input element
- Custom label/error handling
- Integration with custom form components
Key Changes
1. Component Split
v2: Single Input component with all features
v3: Split into Input (primitive) and TextField (compound component)
2. Removed Props from Input
The following props are no longer available on Input in v3:
label- UseLabelcomponent insideTextFielddescription- UseDescriptioncomponent insideTextFielderrorMessage- UseFieldErrorcomponent insideTextFieldvariant- Removed (use Tailwind CSS classes)color- Removed (use Tailwind CSS classes)size- Removed (use Tailwind CSS classes)radius- Removed (use Tailwind CSS classes)labelPlacement- Use layout withLabelcomponentstartContent- Use as first child inTextFieldendContent- Use as last child inTextFieldisClearable- Implement manually with buttonisRequired- UseisRequiredonTextFieldisInvalid- UseisInvalidonTextFieldvalidate- UsevalidateonTextFieldclassNames- UseclassNameprops on individual componentsonValueChange- UseonChangeevent handler
3. New Props on Input
isOnSurface- Whether input is on a surface background (affects styling)
Migration Examples
Basic Usage
import { Input } from "@heroui/react";
export default function App() {
return (
<Input
label="Email"
placeholder="Enter your email"
type="email"
/>
);
}import { TextField, Label, Input, FieldError } from "@heroui/react";
export default function App() {
return (
<TextField name="email" type="email">
<Label>Email</Label>
<Input placeholder="Enter your email" />
<FieldError />
</TextField>
);
}Input with Description
<Input
description="We'll never share your email"
label="Email"
type="email"
/>import { Description } from "@heroui/react";
<TextField name="email" type="email">
<Label>Email</Label>
<Input />
<Description>We'll never share your email</Description>
<FieldError />
</TextField>Input with Error Message
<Input
errorMessage="Please enter a valid email"
isInvalid
label="Email"
type="email"
/><TextField
isInvalid
name="email"
type="email"
validate={(value) => {
if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(value)) {
return "Please enter a valid email";
}
return null;
}}
>
<Label>Email</Label>
<Input />
<FieldError />
</TextField>Input with Start/End Content
<Input
label="Price"
startContent={<span>$</span>}
type="number"
/><TextField name="price" type="number">
<Label>Price</Label>
<span>$</span>
<Input />
<FieldError />
</TextField>Input with Clear Button
<Input
isClearable
label="Email"
onClear={() => console.log("cleared")}
type="email"
/>import { useState } from "react";
import { CloseButton } from "@heroui/react";
const [value, setValue] = useState("");
<TextField name="email" type="email">
<Label>Email</Label>
<div className="flex items-center">
<Input value={value} onChange={(e) => setValue(e.target.value)} />
{value && (
<CloseButton
aria-label="Clear"
onPress={() => setValue("")}
/>
)}
</div>
<FieldError />
</TextField>Required Input
<Input
isRequired
label="Email"
type="email"
/><TextField isRequired name="email" type="email">
<Label>Email</Label>
<Input />
<FieldError />
</TextField>Controlled Input
import { useState } from "react";
const [value, setValue] = useState("");
<Input
label="Email"
onValueChange={setValue}
type="email"
value={value}
/>import { useState } from "react";
const [value, setValue] = useState("");
<TextField name="email" type="email">
<Label>Email</Label>
<Input
onChange={(e) => setValue(e.target.value)}
value={value}
/>
<FieldError />
</TextField>Simple Input (No Label/Validation)
<Input placeholder="Search..." type="search" />{/* v3 Input can be used directly for simple cases */}
<Input aria-label="Search" placeholder="Search..." type="search" />Input Variants
<Input label="Email" type="email" variant="bordered" />
<Input label="Email" type="email" variant="flat" />
<Input label="Email" type="email" variant="underlined" />{/* Use Tailwind CSS classes for styling */}
<TextField name="email" type="email">
<Label>Email</Label>
<Input className="border border-border" />
<FieldError />
</TextField>Input Sizes
<Input label="Email" size="sm" type="email" />
<Input label="Email" size="md" type="email" />
<Input label="Email" size="lg" type="email" />{/* Use Tailwind CSS classes for sizes */}
<TextField name="email" type="email">
<Label>Email</Label>
<Input className="h-8 text-sm" />
<FieldError />
</TextField>Input Colors
<Input color="primary" label="Email" type="email" />
<Input color="danger" label="Email" type="email" />{/* Use Tailwind CSS classes for colors */}
<TextField name="email" type="email">
<Label>Email</Label>
<Input className="border-primary focus-visible:border-primary" />
<FieldError />
</TextField>Breaking Changes Summary
- Component Split:
Input→TextFieldfor form fields,Inputfor primitive input - Label Required: Must use
Labelcomponent instead oflabelprop - Error Display: Must use
FieldErrorcomponent instead oferrorMessageprop - Description: Must use
Descriptioncomponent instead ofdescriptionprop - Validation: Move
validatefunction fromInputtoTextField - Start/End Content: Use as children instead of props
- Clear Button: Implement manually with
CloseButton - Variants Removed: Use Tailwind CSS classes
- Colors Removed: Use Tailwind CSS classes
- Sizes Removed: Use Tailwind CSS classes
- Radius Removed: Use Tailwind CSS classes
- onValueChange Removed: Use
onChangeevent handler - ClassNames Removed: Use
classNameprops on individual components
Tips for Migration
- Most cases: Convert
InputtoTextFieldcompound component - Simple inputs: Use
Inputdirectly if you don't need labels/validation - Add TextField wrapper: Wrap
InputwithTextFieldfor form fields - Add Label: Use
Labelcomponent for labels - Add FieldError: Always include
FieldErrorfor error display - Move validation: Move
validatefunction toTextField - Use onChange: Replace
onValueChangewithonChangeevent handler - Custom styling: Use Tailwind CSS classes for variants, colors, sizes
Need Help?
For v3 Input features and API:
- See the API Reference
- Check interactive examples
For community support: