TextInput
Overviewimport { CounterInput, DigitInput, InlineInput, InputSelect, TagInput, TextInput } from '@nili/ui';This is the library’s own overview page, rendered live. Switch the language or the theme in the header and it follows.
Types
Twelve presets from the Figma page. Each sets keyboard, autofill and direction for the kind of value being asked for. Adornments are slots — startIcon, endIcon, startAffix, endAffix, select, button, emojiPicker.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
This is a hint text to help user.
import {
TextInput,
InputSelect,
Icon,
Button,
CompactButton,
} from "@nili/ui"
import { RiMailLine, RiSearchLine, RiBankCardLine } from "@remixicon/react"
export function Example() {
return (
<>
<TextInput
type="email"
label="Email Address"
sublabel="(Optional)"
information
hint="This is a hint text to help user."
placeholder="hello@nili.design"
startIcon={<Icon icon={RiMailLine} />}
required
/>
<TextInput
type="phone"
label="Phone Number"
select={
<InputSelect icon={<span>🇺🇸</span>} aria-label="Country code">
+1
</InputSelect>
}
/>
<TextInput type="website" label="Website" startAffix="https://" />
<TextInput
type="amount"
label="Amount"
prefix="$"
select={
<InputSelect icon={<span>🇪🇺</span>} aria-label="Currency">
EUR
</InputSelect>
}
/>
</>
)
}States & sizes
Default, filled, disabled and error across the three sizes. Hover and focus are CSS states, so they are not drawn here.
Enter a valid email address
import { TextInput } from "@nili/ui"
export function Example() {
return (
<>
<TextInput label="Default" placeholder="Enter text..." />
<TextInput label="Filled" defaultValue="hello@nili.design" type="email" />
<TextInput label="Disabled" defaultValue="Locked" disabled />
<TextInput
label="Error"
defaultValue="not-an-email"
type="email"
error="Enter a valid email address"
/>
<TextInput size="sm" label="Small" />
<TextInput size="xs" label="X-Small" />
</>
)
}Tag Input
A specialized field for creating and managing tags. Enter or comma commits; Backspace on an empty field removes the last one.
Press Enter to add a tag
import { TagInput } from "@nili/ui"
export function Example() {
return (
<TagInput
label="Cities"
hint="Press Enter to add a tag"
defaultValue={["Berlin", "London", "Paris"]}
placeholder="Add a city..."
dismissLabel="Remove"
/>
)
}Counter Input
A numeric field with increment and decrement controls attached to it.
Between 1 and 10
Between 1 and 10
Between 1 and 10
import { CounterInput } from "@nili/ui"
export function Example() {
return (
<CounterInput
label="Guests"
hint="Between 1 and 10"
defaultValue={2}
min={1}
max={10}
decrementLabel="Decrease"
incrementLabel="Increase"
/>
)
}Digit Input
Captures numeric data one digit at a time — one-time codes and PINs. Paste fills the whole set.
We sent a code to your phone
That code is not right
import { DigitInput } from "@nili/ui"
export function Example() {
return (
<>
<DigitInput
length={4}
label="Verification code"
hint="We sent a code to your phone"
groupLabel="Verification code"
cellLabel={(index, total) => `Digit ${index} of ${total}`}
/>
<DigitInput
length={6}
error={t('f.codeWrong')}
defaultValue="123"
groupLabel="PIN"
cellLabel={(index, total) => `Digit ${index} of ${total}`}
/>
</>
)
}Inline Input
A compact field for editing data in place. The border only appears on hover and focus — it reads as text until you touch it.
import { InlineInput, Icon, CompactButton } from "@nili/ui"
import { RiUser3Line, RiPencilLine } from "@remixicon/react"
export function Example() {
return (
<InlineInput
label="Display name"
placeholder="Enter a name"
defaultValue="Ada Lovelace"
startIcon={<Icon icon={RiUser3Line} />}
button={
<CompactButton appearance="ghost" size="md" aria-label="Edit">
<Icon icon={RiPencilLine} />
</CompactButton>
}
/>
)
}CounterInput
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | md | sm | xs | md | Figma `📏 Size` — Medium (40) · Small (36) · X-Small (32). |
| label | ReactNode | — | Figma `🔖 Label`. Omit to hide. |
| hint | ReactNode | — | Figma `💡 Hint Text`. Omit to hide. |
| error | ReactNode | — | Marks the field invalid *and* supplies the message. Figma `State=Error`. |
| required | boolean | — | |
| value | number | — | |
| defaultValue | number | 0 | |
| onValueChange | (value: number) => void | — | |
| min | number | 0 | |
| max | number | — | |
| step | number | 1 | |
| disabled | boolean | — | |
| locale | string | — | Digits follow the locale, so `16` renders in Persian digits under `fa`. |
| formatValue | (value: number) => string | — | Overrides the rendered value — units, currency, `2 hrs`. |
| decrementLabel* | string | — | Accessible names for the two buttons. Required, not optional: an icon-only button with no accessible name is announced as just "button". |
| incrementLabel* | string | — | |
| id | string | — | |
| name | string | — | |
| className | string | — | |
| rootClassName | string | — | |
| fieldClassName | string | — |
DigitInput
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| length | number | 4 | How many cells. Figma draws one; the count is yours. |
| value | string | — | |
| defaultValue | string | ||
| onValueChange | (value: string) => void | — | |
| onComplete | (value: string) => void | — | Fires once the last cell is filled. |
| label | ReactNode | — | |
| hint | ReactNode | — | |
| error | ReactNode | — | Marks every cell invalid *and* supplies the message. Figma `State=Error`. |
| required | boolean | — | |
| disabled | boolean | — | |
| autoComplete | string | one-time-code | `one-time-code` by default, which is what lets iOS and Android autofill. |
| groupLabel | string | — | Accessible name for the group, e.g. `'Verification code'`. |
| cellLabel | (index: string, total: string) => string | — | Names each cell for a screen reader: `(index, total) => string`. |
| id | string | — | |
| name | string | — | |
| className | string | — | |
| rootClassName | string | — |
InlineInput
extends Omit< ComponentPropsWithoutRef<'input'>, 'size' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| disabled | boolean | — | |
| required | boolean | — | |
| readOnly | boolean | — | |
| checked | boolean | — | |
| defaultChecked | boolean | — | |
| placeholder | string | — | |
| value | string | — | |
| defaultValue | string | — | |
| name | string | — | |
| maxLength | number | — | |
| autoComplete | string | — | |
| aria-label | string | — | |
| startIcon | ReactNode | — | Figma `⬅️ Left Icon` / `⬅️ Pick Left`. |
| button | ReactNode | — | Figma `🕹️ Button` — a trailing CompactButton, usually a pencil. |
| invalid | boolean | false | Marks the field invalid. There is no message slot: this control is inline. |
| label | string | — | Accessible name, since an inline field rarely has a visible label. |
| rootClassName | string | — |
InputSelect
extends Omit< ComponentPropsWithoutRef<'button'>, 'children' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| children | ReactNode | — | Figma `✏️ Edit Text` — `+98`, `EUR`, `can view`. |
| disabled | boolean | — | |
| required | boolean | — | |
| readOnly | boolean | — | |
| value | string | — | |
| defaultValue | string | — | |
| name | string | — | |
| aria-label | string | — | |
| size | md | sm | xs | md | |
| icon | ReactNode | — | Figma `🌍 Pick Country` — a flag, a currency mark, any leading glyph. |
| chevron | boolean | true | Hides the chevron for a display-only affix. |
TagInput
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | md | sm | xs | md | Figma `📏 Size` — Medium (40) · Small (36) · X-Small (32). |
| label | ReactNode | — | Figma `🔖 Label`. Omit to hide. |
| hint | ReactNode | — | Figma `💡 Hint Text`. Omit to hide. |
| error | ReactNode | — | Marks the field invalid *and* supplies the message. Figma `State=Error`. |
| required | boolean | — | |
| startIcon | ReactNode | — | Figma `⬅️ Left Icon` / `⬅️ Pick Left`. |
| value | string[] | — | The tags. Controlled. |
| defaultValue | string[] | — | |
| onValueChange | (tags: string[]) => void | — | |
| placeholder | string | — | |
| maxTags | number | — | Refuses to add beyond this many. |
| dismissLabel | string | — | Accessible name for each tag's dismiss button, e.g. `'Remove'`. |
| disabled | boolean | — | |
| id | string | — | |
| name | string | — | |
| className | string | — | |
| rootClassName | string | — | |
| fieldClassName | string | — |
TextInput
extends Omit< ComponentPropsWithoutRef<'input'>, 'size' | 'type' | 'prefix' >
Other props
| Prop | Type | Default | Notes |
|---|---|---|---|
| disabled | boolean | — | |
| required | boolean | — | |
| readOnly | boolean | — | |
| checked | boolean | — | |
| defaultChecked | boolean | — | |
| placeholder | string | — | |
| value | string | — | |
| defaultValue | string | — | |
| name | string | — | |
| maxLength | number | — | |
| autoComplete | string | — | |
| aria-label | string | — | |
| size | md | sm | xs | md | |
| type | basic | button | search | card | emoji | email | phone | website | amount | date | password | dropdown | basic | Figma's `Type`. Sets the input mode, autofill hint and direction for the kind of value being asked for; see {@link TextInputType}. |
| htmlType | ComponentPropsWithoutRef<'input'>['type'] | — | Escape hatch: forces the native attribute when the preset gets it wrong. |
| label | ReactNode | — | Rendered above the field and tied to it. |
| sublabel | ReactNode | — | Figma Label's `💬 Sublabel` — the muted "(Optional)" beside the label. |
| information | boolean | ReactNode | false | Figma Label's `ℹ️ Information` glyph. `true` uses the default icon. |
| informationLabel | string | — | Accessible name for that glyph. |
| labelAction | ReactNode | — | Figma Label's `🌟 Button` — trailing slot on the label row. |
| hint | ReactNode | — | The line under the field. Becomes the error message when `error` is set. |
| error | ReactNode | — | Validation message. Its presence is what marks the field invalid, so the two can never disagree — a red border with no message, or a message with a field that still looks fine. |
| startIcon | ReactNode | — | Icon inside the field, on the leading edge. |
| endIcon | ReactNode | — | Icon or control inside the field, on the trailing edge. |
| startAffix | ReactNode | — | Attached block before the field — `https://`, a currency. |
| endAffix | ReactNode | — | Attached block after the field — `.com`, a unit. |
| shortcut | ReactNode | — | `type="search"` — the keyboard shortcut chip on the trailing edge (⌘K). |
| cardProvider | ReactNode | — | `type="card"` — the detected card brand, shown once enough digits exist. |
| strength | PasswordStrengthLevel | false | false | `type="password"` — wires the existing `PasswordStrength` meter under the field. Pass a level, or `false` to hide it. |
| strengthLabel | string | — | Text read out beside the strength meter, e.g. `'Strong password'`. |
| emojiPicker | ReactNode | — | `type="emoji"` — the picker trigger on the leading edge. |
| select | ReactNode | — | `type="dropdown"` — an attached `<Dropdown>` sharing the field's border. |
| button | ReactNode | — | `type="button"` — an attached action sharing the field's border. |
| reveal | boolean | true | Shows a reveal toggle on a password field. On by default — a field nobody can read back is a field people get wrong twice and then paste from a note. Turn it off where a shoulder is the threat model. |
| revealLabel | string | Show password | Accessible name for the reveal toggle while the value is hidden. |
| hideLabel | string | Hide password | Accessible name for the reveal toggle while the value is visible. |
| suffix | ReactNode | — | Figma's `Suffix` — muted text pinned inside the trailing edge. |
| prefix | ReactNode | — | Muted text before the value — Figma's `€` on an Amount field. |
| selectPosition | start | end | — | Overrides which edge `select` sits on. Defaults to the type's own side: `start` for Phone, `end` for Amount and Dropdown. |
| latin | boolean | — | Pins the field to left-to-right regardless of the ambient direction. |
| rootClassName | string | — | Wrapper class. Use `className` for the `<input>` itself. |
| fieldClassName | string | — | Class for the bordered field box. |
TextInputAdornment
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| side | start | end | start | |
| size | md | sm | xs | md |
TextInputAffix
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| side | start | end | start | |
| size | md | sm | xs | md |
TextInputAttached
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| side | start | end | end |
TextInputControl
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | md | sm | xs | md |
TextInputField
internalVariant axes — from the component’s cva definition
| Prop | Type | Default | Notes |
|---|---|---|---|
| size | md | sm | xs | md | |
| invalid | 'true' | 'false' | false |