IndiaCN UI

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.json
import { 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.

InputDefault
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

InputSizes
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.

InputStates
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

InputWithIcons
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:

StateTokenValue
defaultneutral-200#c6c6c6
hoverprimary#613af5
errordanger-500#ec5042
successsuccess-500#389314
warningwarning-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. InputMessage is 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

PropTypeDefaultDescription
sizemd | lgmd44px or 48px
statedefault | error | success | warningdefaultBorder and focus-ring colour
prefixIconReactNodeLeading icon, sized by the container
suffixReactNodeTrailing slot, after the clear button
onClear() => voidRenders a clear button when value is non-empty
containerClassNamestringClasses for the field wrapper rather than the input

Any other input prop is forwarded.

InputMessage

PropTypeDefaultDescription
statedefault | error | success | warningdefaultPicks the icon and colour

On this page