TextField
StableA single-line text box with room for an icon or unit, two sizes and an invalid state.
Also known as input, text input, textbox, text box
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Search with an icon
function Example() {
const [q, setQ] = useState("");
return (
<div className="w-72">
<TextField type="search" aria-label="Search chats" placeholder="Search chats" leading={<SearchIcon />} value={q} onChange={(e) => setQ(e.target.value)} />
</div>
);
}The icon is decorative. The box needs its own name because there is no visible label.
When to use it
A single line of text, email, link, phone number, search or password, with the same look and states as the other form controls.
Made of: Box · Leading slot · Input · Trailing slot
Use it for
- A name, email, link or phone number in a form.
- A search box with a search icon.
- A value with a unit, such as a price with a currency sign.
Not for
- A number the user steps up and down: use NumberField.
- Picking one value from a fixed list: use Select.
- Writing a message to the AI: use PromptBox.
- A long, multi-line message with attachments and send: use Composer.
Do and avoid
Add the part after the @ sign.
Do
- Wrap it in Field so it has a visible label.
- Use the matching type so phones show the right keyboard.
- Use leading for an icon and trailing for a unit.
- Keep placeholder text as an example, not an instruction.
Avoid
- Do not use a placeholder as the only label.
- Do not show an invalid border without a message in text.
- Do not use it for a number the user steps. Use NumberField.
- Do not put a button in leading or trailing without giving the button its own label.
On a phone
- It fills the width of its container and is 44px tall on touch screens, even at size sm.
- Text is 16px on touch screens, so iPhones do not zoom in when the field gets focus.
- The type prop picks the on-screen keyboard (email, tel, url, search), and extra attributes such as inputMode pass through to the input.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| type | "text" | "email" | "password" | "search" | "url" | "tel" | "text" | Native input type. Password shows dots and has no built-in show or hide button. |
| invalid | boolean | none | Danger border and aria-invalid. Always explain the problem in text too. A passed aria-invalid has the same effect, which is how Field sets it. |
| leading | ReactNode | none | Content before the text, such as an icon. It is hidden from screen readers. |
| trailing | ReactNode | none | Content after the text, such as a unit. It is not hidden from screen readers. |
| size | "sm" | "md" | "md" | md is 40px tall, sm is 32px. Both are 44px on coarse pointers such as touch screens. |
| className | string | none | Classes for the outer box, so width and margins apply to it. |
| ref | Ref<HTMLInputElement> | none | Reaches the native input. A normal prop in React 19. |
| ...rest | Omit<InputHTMLAttributes<HTMLInputElement>, "size" | "type"> | none | Everything else goes to the <input>: value, onChange, placeholder, disabled, readOnly, required, id, aria-label, aria-describedby and so on. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one TextField switches all of this from its properties panel, across 20 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| Invalid | invalid | On or off |
| Size | size | SM, MD |
| State | Hover, Pressed and Focus come from the browser. Disabled sets disabled. | Default, Hover, Pressed, Focus, Disabled |
| Placeholder | placeholder | Any text |
States
States this part has beyond its default look. Build each one your screen can reach. States set by a property are in the Code tab.
- hover
- The border gets stronger when the pointer is over the box.
- focus
- A focus ring is drawn around the whole box (icon and unit included) when you reach it with the keyboard.
- disabled
- Pass disabled. The box is dimmed, ignores typing and is skipped by the Tab key.
- readonly
- Pass readOnly. The box gets a sunken background; the text can be selected and copied but not changed.
- invalid
- Pass invalid for a danger border and aria-invalid. Explain the problem in text too.
Tokens
Change one of these and every part that uses it follows.
--border--border-strong--danger--surface--surface-sunken--fg--fg-muted--fg-subtle--focus-ring--dur-fast--radius-field
Built in
- It is a native input, so typing, selecting, autofill and spell check work as the browser provides.
- The focus ring wraps the whole box, including the icon and unit, and shows for keyboard focus.
- invalid sets aria-invalid on the input.
- A disabled input is skipped by the Tab key. A read-only input can be focused and its text copied.
- The box is 44px tall on coarse pointers.
- The leading slot is hidden from screen readers.
What you need to do
- Give it a name: wrap it in Field, or pass aria-label (for example on a search box with no visible label).
- When invalid is set, also show a message in text. Field does this when you pass error.
- Pick the right type, such as email or tel, so phones show the right keyboard.
- Password has no show or hide button. Add one yourself if you need it, and keep it a real button with a label.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide