Skip to content

TextField

Single- or multi-line text input. The label, the description and the error are one component, wired together by Base UI's Field.

tsx
import { TextField } from 'neba';

<TextField label="Email" value={email} onChange={(event) => setEmail(event.target.value)} />;

Props

PropTypeDefaultDescription
variantshared'solid' | 'outline' | 'text''outline'Weight of the surface. Even solid is not flooded with colour — a field holds user data
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'Height and type scale. The same heights as Button, so a row's baseline holds
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role. The surface is white, so it reaches the edge, the focus ring and the caret
densityshared'default' | 'compact''default'Padding only — never the height, never the type scale
elevationshared0 | 1 | 2 | 30Drop shadow depth. A field is a well, not a surface that floats — rarely raised
labelReactNodeLabel above the control, wired to it by Base UI's Field
descriptionReactNodeHelper text below the control
errorReactNodeError message below the control. Its presence also turns the field invalid
invalidboolean!!errorForces the invalid state without a message, for when a form library owns validity
multilinebooleanfalseRenders a textarea instead of an input. Every other axis stays identical
rowsnumber3Visible rows in multiline mode
resize'none' | 'vertical' | 'horizontal' | 'both''vertical'Which way the user may drag it. Horizontal breaks a form's column
startIconReactNodeContent before the control
endIconReactNodeContent after the control
loadingbooleanfalseSpinner in place of endIcon. Typing is deliberately still allowed
readOnlybooleanfalseRead-only. Selecting and copying still work
disabledbooleanfalseUnavailable
fullWidthbooleanfalseStretches to the width of the container

Every native <input> attribute passes straight through. color and size are omitted because they collide with the ones above, and onChange is widened so the same handler types against a <textarea> in multiline mode.

Examples

Variants

Even solid is not flooded with the accent colour. What a field holds is user data, and a caret, a text selection and a placeholder all have to stay legible on top of it. The colour family shows up in the edge, the focus ring and the caret instead.

The sheet itself is white, and going outlinesolid → hover → focus makes it more opaque, not more coloured. Colour has the whole rule.

Sizes

The same heights as Button, so a field and a button in one row share a baseline.

Multiline

multiline renders a <textarea> and changes nothing else. rows={1} is exactly as tall as the single-line field. Horizontal resizing breaks a form's column, so only the vertical axis is on by default.

Icons and progress

loading puts a spinner in the endIcon slot and marks the field busy, but typing is deliberately still allowed — a field is usually loading because of what was typed into it.

States

Give error any content and the whole field re-points at the danger family — edge, focus ring, caret and message all turn over together. To mark it invalid without a message, pass invalid directly.

Controlled

value and onChange are the native ones.

Accessibility

  • Base UI's Field connects the label, description and error to the control with id and aria-describedby.
  • There is no floating-label variant on purpose: floating labels need a transform, and controls in this library never transform.
  • The focus ring belongs to the shell rather than the control inside it, so it traces the acrylic edge.
  • Clicking the shell's own padding puts the caret in the field, the way a native <input> behaves.

Released under the MIT License