Input
freeInput provides consistent text entry for forms, search, and editable notes.
Preview
Dark
import { PiMagnifyingGlass } from 'react-icons/pi'
import Input from '@/components/ui/Input';
export default function UsageDemo() {
return (
<div className="mx-auto w-full max-w-md">
<Input
aria-label="Search projects"
placeholder="Search projects"
prefix={<PiMagnifyingGlass className="text-base text-muted-foreground" />}
/>
</div>
);
}Installation
Add this component with the NateUI CLI.
npx nateui@latest add InputExamples
Basic
Preview
Dark
import Input from '@/components/ui/Input';
export default function BasicDemo() {
return (
<div className="mx-auto w-full max-w-md">
<Input placeholder="Workspace name" />
</div>
);
}Size
Preview
Dark
import Input from '@/components/ui/Input';
export default function SizeDemo() {
return (
<div className="mx-auto w-full max-w-md space-y-4">
<Input size="sm" placeholder="Small input" />
<Input placeholder="Medium input" />
<Input size="lg" placeholder="Large input" />
</div>
);
}Disabled
Preview
Dark
import Input from '@/components/ui/Input';
export default function DisabledDemo() {
return (
<div className="mx-auto grid w-full max-w-md gap-4 sm:grid-cols-2">
<Input disabled defaultValue="Disabled value" />
<Input readOnly defaultValue="Read only value" />
</div>
);
}Affix
Preview
Dark
$USD
import { PiMagnifyingGlass, PiQuestion } from 'react-icons/pi'
import Input from '@/components/ui/Input';
import Tooltip from '@/components/ui/Tooltip';
export default function AffixDemo() {
return (
<div className="mx-auto w-full max-w-md space-y-4">
<Input
aria-label="Search customers"
placeholder="Search customers"
prefix={<PiMagnifyingGlass className="text-base text-muted-foreground" />}
/>
<Input
aria-label="Monthly budget"
defaultValue="12000"
prefix="$"
suffix="USD"
/>
<Input
aria-label="Workspace slug"
placeholder="northwind-sales"
suffix={
<Tooltip
title="Use lowercase letters, numbers, and dashes."
tabIndex={0}
aria-label="Workspace slug help"
>
<PiQuestion className="text-base text-muted-foreground cursor-help" />
</Tooltip>
}
/>
</div>
);
}Password Visibility
Preview
Dark
import { useState } from 'react';
import { PiEye, PiEyeSlash } from 'react-icons/pi'
import Input from '@/components/ui/Input';
export default function PasswordVisibilityDemo() {
const [visible, setVisible] = useState(false);
return (
<div className="mx-auto w-full max-w-md">
<Input
type={visible ? 'text' : 'password'}
placeholder="Password"
suffix={
<button
type="button"
aria-label={visible ? 'Hide password' : 'Show password'}
className="inline-flex cursor-pointer items-center text-muted-foreground transition-colors hover:text-foreground"
onClick={() => setVisible((current) => !current)}
>
{visible ? (
<PiEyeSlash className="text-base" />
) : (
<PiEye className="text-base" />
)}
</button>
}
/>
</div>
);
}Textarea
Preview
Dark
import Input from '@/components/ui/Input';
export default function TextareaDemo() {
return (
<div className="mx-auto w-full max-w-md">
<Input
textArea
rows={4}
placeholder="Write an internal note"
defaultValue="Follow up after the onboarding call."
/>
</div>
);
}Invalid
Preview
Dark
import { useState } from 'react';
import { PiWarningCircle } from 'react-icons/pi'
import Button from '@/components/ui/Button';
import Form from '@/components/ui/Form';
import Input from '@/components/ui/Input';
export default function InvalidDemo() {
const [invalid, setInvalid] = useState(true);
return (
<Form
className="mx-auto w-full max-w-md"
onSubmit={(event) => event.preventDefault()}
>
<Form.Field
label="Invite email"
htmlFor="invalid-email"
invalid={invalid}
errorMessage={invalid ? 'Enter a valid email address.' : undefined}
>
<Input
id="invalid-email"
name="email"
type="email"
invalid={invalid}
defaultValue="alex@"
suffix={invalid ? <PiWarningCircle className="text-base" /> : null}
/>
</Form.Field>
<div className="flex justify-end gap-2">
<Button type="button" onClick={() => setInvalid((current) => !current)}>
Set {invalid ? 'valid' : 'invalid'}
</Button>
</div>
</Form>
);
}Controlled
Preview
Dark
Current value: northwind-sales
import { useState } from 'react';
import Input from '@/components/ui/Input';
import type { ChangeEvent } from 'react';
export default function ControlledDemo() {
const [value, setValue] = useState('northwind-sales');
const handleChange = (event: ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value);
};
return (
<div className="mx-auto w-full max-w-md space-y-2">
<Input
value={value}
placeholder="Workspace slug"
onChange={handleChange}
/>
<div className="text-xs text-muted-foreground">
Current value: {value || 'empty'}
</div>
</div>
);
}API
Input renders either a native input, a textarea, or an affixed input wrapped by InputWrapper.
Input
Input accepts the props below plus native input and textarea props, except the native size and prefix attributes.
| Prop | Description | Type | Default |
|---|---|---|---|
className | Class names applied to the input root, or to InputWrapper when prefix or suffix is present. | string | - |
defaultValue | Initial value for uncontrolled usage. | string | number | readonly string[] | - |
disabled | Prevents editing and applies disabled control styling. | boolean | - |
invalid | Applies invalid styling; also inherits invalid state from Form.Field. | boolean | - |
onChange | Called when the input value changes. | InputChangeHandler | - |
placeholder | Placeholder text rendered by the native input or textarea. | string | - |
prefix | Content rendered before the editable value inside an affixed input. | InputAffix | - |
readOnly | Prevents editing while keeping the field focusable. | boolean | - |
ref | Ref forwarded to the rendered input or textarea element. | InputRef | - |
rows | Number of visible text rows when textArea is true. | number | - |
size | Control size. Falls back to InputGroup size, then Form size, then ConfigProvider control size. | ControlSize | ConfigProvider controlSize |
suffix | Content rendered after the editable value inside an affixed input. | InputAffix | - |
textArea | Renders a textarea instead of an input. | boolean | false |
type | Native input type when textArea is false. | HTMLInputTypeAttribute | 'text' |
value | Controlled value. undefined and null are normalized to an empty string. | string | number | readonly string[] | - |
...nativeInputProps | Native props for the rendered input or textarea, except size and prefix. | NativeInputProps | - |
InputWrapper
InputWrapper is the affix container used by Input when prefix or suffix is provided.
| Prop | Description | Type | Default |
|---|---|---|---|
children | Editable control rendered between the affix slots. | ReactNode | - |
className | Class names applied to the wrapper root. | string | - |
disabled | Applies disabled wrapper and affix styling. | boolean | - |
invalid | Applies invalid wrapper and affix styling; also inherits invalid state from Form.Field. | boolean | - |
prefix | Content rendered before children. | ReactNode | - |
readOnly | Removes focus ring styling when the wrapped control is read-only. | boolean | - |
ref | Ref forwarded to the wrapper span. | Ref<HTMLSpanElement> | - |
size | Control size. Falls back to InputGroup size, then Form size, then ConfigProvider control size. | ControlSize | ConfigProvider controlSize |
suffix | Content rendered after children. | ReactNode | - |
...nativeSpanProps | Native props applied to the wrapper span, except prefix. | NativeInputWrapperProps | - |
Types
type ControlSize = 'sm' | 'md' | 'lg';
type InputAffix = string | ReactNode;
type InputChangeHandler = ChangeEventHandler<
HTMLInputElement | HTMLTextAreaElement
>;
type InputRef = Ref<ElementType | HTMLInputElement | HTMLTextAreaElement>;
type NativeInputProps = Omit<
InputHTMLAttributes<HTMLInputElement | HTMLTextAreaElement>,
'size' | 'prefix'
>;
type NativeInputWrapperProps = Omit<ComponentProps<'span'>, 'prefix'>;