Input
Text field with sizes, validation states, a leading icon and a clear button.
Introduction
Inputs collect a single line of text. Most government forms are mostly inputs, so this component carries the four validation states UX4G defines — default, error, success and warning — rather than leaving them to each application.
Installation
npx shadcn@latest add https://indiacn.in/r/input.jsonimport { Input, InputMessage } from '@/components/ui/input';Usage
<Label htmlFor="pan">PAN</Label>
<Input id="pan" placeholder="ABCDE1234F" />
<InputMessage>Ten characters, as printed on your card.</InputMessage>Examples
Default
Twelve digits, as printed on your card.
import { Input, InputMessage } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
export default function Component() {
return (
<div className='w-full max-w-[360px]'>
<Label htmlFor='aadhaar' className='mb-2'>
Aadhaar number
</Label>
<Input id='aadhaar' placeholder='XXXX XXXX XXXX' />
<InputMessage>Twelve digits, as printed on your card.</InputMessage>
</div>
);
}Sizes
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
export default function Component() {
return (
<div className='flex w-full max-w-[360px] flex-col gap-5'>
<div>
<Label htmlFor='size-md' className='mb-2'>
Default — 44px
</Label>
<Input id='size-md' placeholder='Placeholder' />
</div>
<div>
<Label htmlFor='size-lg' className='mb-2'>
Large — 48px
</Label>
<Input id='size-lg' size='lg' placeholder='Placeholder' />
</div>
</div>
);
}States
Enter all twelve digits.
Verified with UIDAI.
This number is already linked to a claim.
import { Input, InputMessage } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
export default function Component() {
return (
<div className='flex w-full max-w-[360px] flex-col gap-5'>
<div>
<Label htmlFor='st-error' className='mb-2'>
Error
</Label>
<Input id='st-error' state='error' defaultValue='1234' />
<InputMessage state='error'>Enter all twelve digits.</InputMessage>
</div>
<div>
<Label htmlFor='st-success' className='mb-2'>
Success
</Label>
<Input id='st-success' state='success' defaultValue='2345 6789 0123' />
<InputMessage state='success'>Verified with UIDAI.</InputMessage>
</div>
<div>
<Label htmlFor='st-warning' className='mb-2'>
Warning
</Label>
<Input id='st-warning' state='warning' defaultValue='2345 6789 0123' />
<InputMessage state='warning'>This number is already linked to a claim.</InputMessage>
</div>
<div>
<Label htmlFor='st-disabled' className='mb-2'>
Disabled
</Label>
<Input id='st-disabled' disabled defaultValue='Locked after submission' />
</div>
</div>
);
}With icons
import { Mail, Search as SearchIcon } from 'lucide-react';
import { ChangeEvent, useCallback, useState } from 'react';
import { Input } from '@/components/ui/input';
export default function Component() {
const [value, setValue] = useState('priya@example.in');
const handleChange = useCallback((e: ChangeEvent<HTMLInputElement>) => setValue(e.target.value), []);
const handleClear = useCallback(() => setValue(''), []);
return (
<div className='flex w-full max-w-[360px] flex-col gap-5'>
<Input prefixIcon={<Mail />} placeholder='Email address' />
<Input
prefixIcon={<SearchIcon />}
value={value}
onChange={handleChange}
onClear={handleClear}
placeholder='Clearable'
/>
</div>
);
}Why there is no small size
UX4G's Figma kit lists three sizes — Small, Default and Large. Rendering the Small and Default symbols and comparing them pixel by pixel gives zero differing pixels across all 32,578: the Small variant is not actually differentiated in the kit.
So this ships the two sizes that exist: md at 44px and lg at 48px. Pass className if you need something smaller, and we will add a real sm if UX4G ever defines one.
Validation colours
Borders come from the -500 step of each semantic scale, measured off the Figma symbols:
| State | Token | Value |
|---|---|---|
| default | neutral-200 | #c6c6c6 |
| hover | primary | #613af5 |
| error | danger-500 | #ec5042 |
| success | success-500 | #389314 |
| warning | warning-500 | #bb772b |
All four state borders clear the 3:1 that WCAG 1.4.11 asks of non-text UI boundaries. The resting neutral-200 border does not — it measures 1.71:1 on white. That is what UX4G specifies, so it is what ships, but it is worth knowing.
Guidelines
- Label every input.
InputMessageis for guidance, not identification. - Say what to do, not what went wrong: "Enter all twelve digits" beats "Invalid".
- Keep the success state for things actually verified against a source, not merely well-formed.
API Reference
Input
| Prop | Type | Default | Description |
|---|---|---|---|
size | md | lg | md | 44px or 48px |
state | default | error | success | warning | default | Border and focus-ring colour |
prefixIcon | ReactNode | — | Leading icon, sized by the container |
suffix | ReactNode | — | Trailing slot, after the clear button |
onClear | () => void | — | Renders a clear button when value is non-empty |
containerClassName | string | — | Classes for the field wrapper rather than the input |
Any other input prop is forwarded.
InputMessage
| Prop | Type | Default | Description |
|---|---|---|---|
state | default | error | success | warning | default | Picks the icon and colour |