Input
A single-line text field with icons, prefix/suffix, a clear button and a password toggle.
'use client';
import { Input } from '@akhatmirzo/ui';
import { Search } from 'lucide-react';
export default function InputBasic() {
return (
<div className="w-full max-w-sm">
<Input leftIcon={<Search />} placeholder="Mahsulot qidirish" aria-label="Mahsulot qidirish" />
</div>
);
}
Import
import { Input } from '@akhatmirzo/ui';When to use
- For short, single-line values — names, emails, search, amounts. For multi-line text, use
Textarea. - Always inside a
Field— the label, hint and error get wired up for you. For a field without a visible label (like search), passaria-label. - For strict formats like phone numbers, card numbers and dates,
MaskedInputis a better fit. - A
placeholderis not a label — it disappears as soon as you type. Use it for an example value: "90 123 45 67".
Sizes
32, 40 (default) and 48px — the same as Button, so they line up in a row.
'use client';
import { Button, Input } from '@akhatmirzo/ui';
export default function InputSizes() {
return (
<div className="flex w-full max-w-md flex-col gap-3">
<div className="flex items-center gap-2">
<Input size="sm" placeholder="Kichik · 32" aria-label="Kichik" />
<Button size="sm">Qo'llash</Button>
</div>
<div className="flex items-center gap-2">
<Input size="md" placeholder="O'rta · 40" aria-label="O'rta" />
<Button size="md">Qo'llash</Button>
</div>
<div className="flex items-center gap-2">
<Input size="lg" placeholder="Katta · 48" aria-label="Katta" />
<Button size="lg">Qo'llash</Button>
</div>
</div>
);
}
Icons, prefix and suffix
leftIcon / rightIcon are decorative icons (aria-hidden). prefix / suffix are fixed
text inside the field, set off by a divider: a country code, a currency, a domain.
'use client';
import { Input } from '@akhatmirzo/ui';
import { Mail } from 'lucide-react';
export default function InputAddons() {
return (
<div className="flex w-full max-w-sm flex-col gap-3">
<Input prefix="+998" placeholder="90 123 45 67" inputMode="tel" aria-label="Telefon" />
<Input suffix="so'm" placeholder="1 250 000" inputMode="numeric" aria-label="Narx" />
<Input rightIcon={<Mail />} type="email" placeholder="email@misol.uz" aria-label="Email" />
<Input prefix="https://" suffix=".uz" placeholder="dokon" aria-label="Sayt manzili" />
</div>
);
}
Clearing (clearable)
With clearable, a ✕ button appears whenever the field has a value. Esc clears it too. Clearing
fires a regular input event, so onChange runs in controlled mode, followed by onClear.
Qiymat: Samarqand
'use client';
import { Input } from '@akhatmirzo/ui';
import { Search } from 'lucide-react';
import { useState } from 'react';
export default function InputClearable() {
const [query, setQuery] = useState('Samarqand');
return (
<div className="flex w-full max-w-sm flex-col gap-2">
<Input
leftIcon={<Search />}
clearable
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Shahar qidirish"
aria-label="Shahar qidirish"
/>
<p className="text-xs text-text-muted">Qiymat: {query || '—'}</p>
</div>
);
}
Password
With type="password", a show/hide button appears on the right automatically.
Kamida 8 ta belgi
'use client';
import { Field, Input } from '@akhatmirzo/ui';
export default function InputPassword() {
return (
<div className="w-full max-w-sm">
<Field label="Parol" description="Kamida 8 ta belgi">
<Input type="password" defaultValue="maxfiy123" autoComplete="current-password" />
</Field>
</div>
);
}
States
Error (invalid), disabled (disabled) and read-only (readOnly). Inside a Field, passing
error turns on invalid automatically.
'use client';
import { Input } from '@akhatmirzo/ui';
export default function InputStates() {
return (
<div className="grid w-full max-w-lg grid-cols-1 gap-3 sm:grid-cols-2">
<Input placeholder="Oddiy" aria-label="Oddiy" />
<Input invalid defaultValue="sarvar@mail" aria-label="Xato" />
<Input disabled defaultValue="O'chirilgan" aria-label="O'chirilgan" />
<Input readOnly defaultValue="Faqat o'qish uchun" aria-label="Faqat o'qish" />
</div>
);
}
With Field
Tasdiqlash kodi SMS orqali keladi
Email manzil noto'g'ri
'use client';
import { Field, Input } from '@akhatmirzo/ui';
import { Mail } from 'lucide-react';
export default function InputWithField() {
return (
<div className="flex w-full max-w-sm flex-col gap-4">
<Field label="Telefon" required description="Tasdiqlash kodi SMS orqali keladi">
<Input prefix="+998" placeholder="90 123 45 67" inputMode="tel" />
</Field>
<Field label="Email" error="Email manzil noto'g'ri">
<Input leftIcon={<Mail />} defaultValue="sarvar@mail" clearable />
</Field>
</div>
);
}
Props
Input accepts every other <input> attribute (type, name, placeholder, inputMode,
autoComplete, onChange ...), and ref points to the inner <input>. className goes to the
outer container; use inputClassName for the inner element.
| Prop | Type | Default | Description |
|---|---|---|---|
size | 'sm' | 'md' | 'lg' | 'md' | Height: 32 / 40 / 48px |
invalid | boolean | — | Error state (automatic inside a Field with an error) |
leftIcon | ReactNode | — | Icon on the left |
rightIcon | ReactNode | — | Icon on the right |
prefix | ReactNode | — | Text on the left with a divider (e.g. "+998") |
suffix | ReactNode | — | Text on the right with a divider (e.g. "so'm") |
clearable | boolean | false | Shows ✕ when there is a value; Esc clears too |
onClear | () => void | — | Called after clearing |
labels | { clear?: string; showPassword?: string; hidePassword?: string } | { clear: 'Tozalash', showPassword: "Parolni ko'rsatish", hidePassword: 'Parolni yashirish' } | aria-label text for the built-in buttons |
inputClassName | string | — | Class for the inner <input> |
type | string | 'text' | 'password' adds the show/hide button |
readOnly | boolean | false | Read-only; muted background, no clear button |
Data attributes
Set on the outer container (data-slot="input"):
| Attribute | Value |
|---|---|
data-size | sm · md · lg |
data-invalid | Present when invalid |
data-disabled | Present when disabled |
data-readonly | Present when readOnly |
Accessibility
- Native
<input>— browser autofill, validation and keyboard behavior all keep working. - With Field,
id,aria-describedby,aria-invalidandaria-requiredare wired up automatically. - The focus ring lives on the container (
:focus-within), so icons and prefixes stay inside the ring. Escclears the value (only withclearableand a non-empty field). On an empty field the event bubbles up — closing aDialog, for example.- The clear button is not in the
Taborder (tabIndex={-1});Esccovers it from the keyboard. The password toggle is focusable and itsaria-labelfollows its state. - Icons are
aria-hidden— if one carries meaning, put it in the label oraria-label.