TextInput
A single-line field for entering and editing free-form text.
Installation
- Install the package.
npm install @hashintel/ds-components- Import the component.
import { useState } from "react";
import { TextInput } from "@hashintel/ds-components";
export const EntityTypeNameInput = () => {
const [name, setName] = useState("");
return (
<TextInput
name="entityTypeName"
placeholder="e.g. Research paper"
value={name}
onChange={setName}
/>
);
};When to use
- Do: Use TextInput for a single line of free-form text: names, titles, emails, URLs, search queries.
When not to use
- Don't: Don't use TextInput for multi-line text — use TextArea.
- Don't: Don't use it for numeric values — NumberInput adds min/max, stepping, and rejects non-numeric keys.
- Don't: Don't collect values from a known set as free text — use Select, or Combobox when the list is long or searchable.
Examples
Basic
TextInput is always controlled — value and onChange are required, and the handler receives the new string first with the raw change event second. null and undefined values render as empty.
Input types
Set type to match the content so devices show the right keyboard and browsers validate natively; for numbers use NumberInput. Autofill and password managers are blocked by default — set autocomplete on fields they should fill, such as sign-in emails and passwords.
Use inputMode when the content is digits but not a number — IDs, one-time codes — to get the numeric keyboard without number-input semantics.
Disabled
disabled greys the field and ignores input. Prefer readonly when the user only needs to see the value — disabled styling has poor contrast and suggests the value might become editable.
Invalid
invalid turns the border red and sets aria-invalid. Pair it with a visible message — Form.Field's errors renders and announces one.
Loading
Set loading while a lookup or save runs — a spinner appears at the end of the field. The input stays editable.
Read-only
readonly strips the field chrome entirely and renders the value as plain text — use it to display stored values alongside editable ones. Unlike disabled, the value reads as content, not as a greyed-out control.
Clearable
clearable adds a clear button that appears when the field is hovered or focused and a value is present. The default clear empties the input through the normal change pipeline — onChange fires with "" — and keeps focus in the field.
Edit icon
showEditIcon adds a pencil at the end of the field to signal that a displayed value is editable. The icon hides while the input is focused.
Variants
default draws a bordered field; subtle is borderless and reads as inline text until hovered or focused. Use subtle for click-to-edit values such as titles and table cells.
Sizes
md is the default. Use sm and below in dense chrome such as toolbars and table cells, and lg for prominent page-level fields; give Form.Field the same size so the label scales with the input.
Widths
The presets align stacked inputs without custom CSS; fullWidth — the default — fills the container. fitContent tracks the value's length as the user types.
Prefix and suffix
An adornment is an icon ({ iconName }), text ({ text }), or any node ({ content }); adding onClick to the first two renders it as a button. Set the adornment's variant: "subtle" to drop its background and divider.
Styled value
styledValue renders a formatted view of the value while the input is unfocused; focusing reveals the raw value for editing. Use it to show units, thousands separators or highlighting without corrupting what the user typed.
Connected inputs
Set connectToRightInput on the left input and connectToLeftInput on the right to join a pair into one visual control. Only the default variant connects — subtle and readonly inputs render separately.
In a form field
Form.Field supplies the label, description and errors, and links the label to the input automatically — see the Form docs for layouts, rows and sections.
Behavior
- Browser autofill and password managers are suppressed by default — set
autocompleteto allow them. Clearable inputs always suppress autofill.
Content
- Never put requirements in the placeholder — it disappears as the user types. Put format rules in Form.Field's
description. - Keep prefix/suffix text to short units and symbols: "https://", "kg", "MB".
- Write error messages as instructions that reuse the label's words: "Enter an entity type name".
Accessibility
TextInput renders a native <input> with standard text-editing behavior.
What TextInput provides
- Inside a Form.Field the input adopts the field's generated id, so the label is associated automatically;
aria-*props pass through to the<input>.
What you must do
- Always name the input: wrap it in a Form.Field, or pass
aria-labelwhere a visible label genuinely can't fit — a placeholder is not a label. - Icon adornment buttons (
{ iconName, onClick }) carry no accessible name — when the action needs one, pass{ content }with your own labelled button. - Don't signal errors with
invalidcolor alone — pair it with a message, e.g. Form.Field'serrors.
API reference
TextInput
| Prop | Type | Description |
|---|---|---|
| value | string | null | undefined | The input value |
| onChange | (value: string, event: ChangeEvent<HTMLInputElement, Element>) => void | The onChange handler |
| onClick | MouseEventHandler<Element> | |
| size | "xxs" | "xs" | "sm" | "md" | "lg" default: "md" | The size (height) of the element |
| placeholder | string | |
| prefix | PrefixOrSuffix | Element rendered at the start of the input. |
| suffix | PrefixOrSuffix | Element rendered at the end of the input. |
| variant | "subtle" | "default" default: "default" | The input's visual style: subtle removes the border so it reads as inline text. |
| type | HTMLInputTypeAttribute default: "text" | The HTML type of the underlying input element, such as text or number. |
| loading | boolean | Whether a loading spinner is shown inside the input. |
| disabled | boolean | Set to show the input as disabled |
| tabIndex | number | |
| autoFocus | boolean | "never" | Set to true to make the element focused on mount or 'never' to prevent the item being auto-focused |
| onKeyDown | KeyboardEventHandler<Element> | |
| onFocus | FocusEventHandler<HTMLInputElement> | The onFocus handler |
| onBlur | FocusEventHandler<HTMLInputElement> | The onBlur handler |
| width | "xs" | "sm" | "md" | "lg" | "fullWidth" | "fitContent" default: "fullWidth" | A set of standard width presets, for aligning with other inputs. |
| required | boolean | Marks the input as required: sets the native required attribute, so assistive technology announces it and form validation blocks empty submits. Purely functional — the input looks unchanged; pair it with Form.Field's required for the visible mark. |
| name | string | The name of the input |
| invalid | boolean | Set to show the input as invalid |
| inputMode | "search" | "text" | "none" | "email" | … 4 more | The keyboard that should be used on mobile devices. |
| readonly | boolean | Whether editing is disabled. Unlike disabled, strips the input styles and renders the value as plain text. |
| align | "left" | "right" | "center" default: "left" | The alignment of the text inside the input. |
| connectToLeftInput | boolean | Whether this input renders as connected to the input on its left. Set the matching prop on both inputs; subtle and readonly inputs are never connected. |
| connectToRightInput | boolean | Whether this input renders as connected to the input on its right. Set the matching prop on both inputs; subtle and readonly inputs are never connected. |
| styledValue | ReactNode | A formatted view of the value, shown while the input is unfocused. |
| clearable | boolean | { onClear: () => void; } | Whether the input can be cleared. true empties it through the normal change pipeline (onChange fires with ""); pass { onClear } to control clearing yourself; false reserves the clear button's space without enabling it. |
| showEditIcon | boolean | |
| autocomplete | boolean default: false | Whether the browser may autocomplete the input. |
| maxLength | number | |
| minLength | number | |
| pattern | string | |
| spellcheck | boolean |
PrefixOrSuffix
The object shape prefix and suffix take.
| Prop | Type | Description |
|---|---|---|
| iconName | "function" | "filter" | "at" | "circle" | … 137 more | Fills the slot with a design-system icon — a slot takes exactly one of iconName, text, or content. |
| text | string | Fills the slot with a short text label, such as a unit or protocol. |
| content | ReactNode | Fills the slot with arbitrary content. |
| onClick | (() => void) | (() => void) | Makes the slot a button. |
| disabled | boolean | Disables just the slot's button. |
| variant | "subtle" | "default" default: "default" | subtle blends the slot into the field, with no divider from the input text. |
Was this page helpful?