# Text Field
A form field for accepting text inputs
Section: components
Docs: https://nebula.kaluza.com/components/text-field
---
## When to use
- When the user needs to input specific information that cannot be predicted from a predefined list, for instance their name
- When the input is memorable information that will be quicker to type by hand than interact with a more structured input
- When it is essential to avoid bias - predefined lists can force users to pick a choice they don't actually agree with
## When not to use
- **When only an item from a predefined list will be accepted** - this is likely to result in an error. Use [Radio](/components/radio), [Checkbox](/components/checkbox), or [Select Field](/components/select-field) instead.
- **For large text inputs** - users will not be able to see what they have entered easily. Use [TextArea Field](/components/textarea-field) instead.
- **To indicate that a field is unavailable** - the disabled state is not supported. Use [Summary List](/components/summary-list) to show values that cannot be edited, or conditionally hide the field if it is not relevant in the current context.
## Anatomy
1. **Label**
2. **Hint**
3. **Input**
4. **Left icon** - optional
5. **Right icon** - optional
6. **Error message**
7. **Required/Optional indicator**
## Basic usage
```tsx
```
## Variants and features
### Input types
TextField has four specialised variants for specific input types. These are separate components that share the TextField API.
- **[NumberField](/components/subcomponents)** - for numeric input
- **[EmailField](/components/subcomponents)** - for email addresses
- **[TelField](/components/subcomponents)** - for telephone numbers
- **[PasswordField](/components/subcomponents)** - for passwords. Characters are masked as the user types. Always provide hint text listing any requirements for the password format, such as the types of characters allowed.
```tsx
```
### Left and right icons
Use the `iconLeft` or `iconRight` props to display an icon inside the input.
```tsx
```
### Common form features
The following features are shared across all Nebula form fields. See [Form guidance](/patterns/forms) for full detail.
**Validation and errors** - when a field is invalid, an error message appears below the input and the input border changes to indicate the error state.
**Hint text** - use the `hint` prop to provide additional context to help the user complete the field correctly. Keep hint text brief and specific to the field.
**Required and optional** - fields are required by default. Use the `optional` prop to mark a field as optional. The required or optional state is indicated visually as part of the label.
**Field width** - use the `characters` prop to set the input width to match the expected length of the content. Making inputs the right size for what they collect helps the user understand what to enter. TextField is responsive by default and displays full width below the smallest breakpoint - override with `fullWidth="always"` or `fullWidth="never"`.
```tsx
```
## Grouping
Text fields are typically part of a larger form. See the [Form component](/components/form) and [form guidelines](/patterns/forms) for guidance on grouping and layout.
## Rules and guidance
### Usage
- **TextField** MUST NOT be passed the disabled prop. Rationale: The prop passes through to the native input with no Nebula styling and removes the field from the tab order, making it invisible to keyboard users with no accessible indication of why. [id: textfield.no-disabled; testability: auto]
- **TextField** SHOULD NOT be used for text inputs that would cover multiple lines. Rationale: Users may not be able to see their entire input. Use TextAreaField instead. [id: textfield.no-multiline; testability: manual]
- **TextField** SHOULD use a specialised variant when the input type is known. Rationale: NumberField, EmailField, TelField, and PasswordField each set the correct input type, keyboard, and validation behaviour for their respective data types. [id: textfield.use-specialised-variant; testability: manual]
- **TextField** SHOULD use the characters prop to set input width to match the expected length of the content. Rationale: Appropriately sized inputs help users understand what data is expected. [id: textfield.character-width; testability: manual]
- **TextField icon** SHOULD be used to add context to the input, not as decoration. [id: textfield.icon-not-decorative; testability: manual]
- **TextField** SHOULD be replaced with plain text. For example in a SummaryList. When displaying a value the user cannot edit. Rationale: A field that looks interactive but cannot be changed is confusing. Plain text communicates the value without implying editability. [id: textfield.non-editable-as-text; testability: manual]
### Content
- **PasswordField hint text** SHOULD list any requirements for the password format, such as character types allowed. Rationale: Setting clear expectations upfront helps users avoid errors when entering a password. [id: textfield.hint-format-requirements; testability: manual]
## Accessibility
### In brief
Text Field is a native text input with a visible label. A screen reader announces the label (hint included), then the field, and reads an error as its description. Number Field, Tel Field, Email Field and Password Field are the same field with another input type.
### What Nebula provides
- A real `label` linked to the input by `id`, so clicking the label focuses the input and gives a larger target.
- `aria-required` on the input unless the field is `optional`, which adds "(optional)" to the label. A required asterisk, when the theme shows one, is hidden from screen readers.
- An `error` sets `aria-invalid` on the input and links the message to it with `aria-describedby`. The message has an icon as well as colour.
- With `characterLimit`, the limit is the input's description and a live region announces the characters remaining once typing pauses.
### Keyboard behaviour
The input is a native `input`, one tab stop reached with `Tab`, and it shows a focus ring when focus comes from the keyboard.
#### Interactions
- **Tab** - Moves focus to the input
### Accessible design
#### Never use the placeholder as the label or the hint
A placeholder disappears as soon as the user types, and Nebula shows it at half opacity. Put the question in the label and any extra guidance in the hint.
WCAG: [3.3.2 Labels or Instructions](https://www.w3.org/WAI/WCAG22/Understanding/labels-or-instructions.html)
#### Keep the hint short
The hint sits inside the label element, so a screen reader reads it as part of the field's name. Put what the user needs first in the label and keep the hint to one short line.
WCAG: [3.3.2 Labels or Instructions](https://www.w3.org/WAI/WCAG22/Understanding/labels-or-instructions.html)
#### If several fields answer one question, group them under a shared label
Wrap them in a `fieldset` with a `legend`, as the [Forms pattern](/patterns/forms/) describes, so a screen reader user hears what the group is for. A single field needs no group.
WCAG: [1.3.1 Info and Relationships](https://www.w3.org/WAI/WCAG22/Understanding/info-and-relationships.html)
#### Write the error so it says what to change
"Enter a name" tells the user what to do and "Invalid" does not. The message is read with the field, so it has to make sense on its own.
WCAG: [3.3.3 Error Suggestion](https://www.w3.org/WAI/WCAG22/Understanding/error-suggestion.html)
### Accessible development
#### Always pass errors through `error`, or through a `Form` with the field `name`
This is what sets `aria-invalid` and links the message to the input. Text placed beside the field is not announced with it.
WCAG: [3.3.1 Error Identification](https://www.w3.org/WAI/WCAG22/Understanding/error-identification.html)
#### If the field collects the user's own details, pass the matching `autoComplete` token
Use a token such as `email` or `tel`, so the browser and assistive technology can fill the field. A value passed here overrides the `off` default that the `disableAutocomplete` theme flag sets.
WCAG: [1.3.5 Identify Input Purpose](https://www.w3.org/WAI/WCAG22/Understanding/identify-input-purpose.html)
### Accessibility testing status
Reviewed on 9 October 2026 against v17.0.1.
| Test | Status | Tested with |
| --- | --- | --- |
| Default state | Tested | axe-core via the Storybook test runner (CI) |
| Advanced states | Partially tested | axe-core via the Storybook test runner (CI) |
| Keyboard navigation | Manually tested | keyboard (reported by the owner) |
| Screen reader | Partially tested | VoiceOver with Chrome (reported by the owner) |
What each test covers:
- Default state: The component as it first appears, before anything is clicked or changed.
- Advanced states: Every other state and variant: error, open, selected, each size and style, and hovering.
- Keyboard navigation: Whether every function can be reached and used with the keyboard alone, with a visible focus ring.
- Screen reader: Whether a screen reader announces what is on screen correctly: the name, the role and any state.
What these statuses mean:
- Tested: Every check in this row passed in an automated run that repeats on every pull request.
- Manually tested: Every check in this row was run once, by a person or with a tool they started, and all of them passed. It is not re-run on every change.
- Partially tested: Some checks in this row were run once and passed; the rest are still to do.
- Issue found: A check in this row failed. The ticket is shown beside the status.
- Not tested: No check in this row has been run yet.
- Not applicable: This row does not apply to the component, for the reason given beside it.
The table records which checks were run on this component on its own. It is not a statement of WCAG conformance, and it does not cover how the component is used.
## Properties
### TextField
Also accepts all props from `HTMLAttributes`, `InputHTMLAttributes`.
- `id` (type `string`, required). Unique id for the input, used to link the label, hint and error to it. Also used as the base for related ids such as `{id}-error`.
- `error` (type `ReactNode`, optional). Error message shown below the input. When set, the input gets `aria-invalid` and an error border, and the message is linked to it with `aria-describedby`. Takes precedence over an error provided by a surrounding `Form`.
- `label` (type `string`, required). Visible label for the field. It is associated with the input, so it forms the input's accessible name.
- `name` (type `string`, optional). The `name` attribute passed to the input. Also used to look up the field's error from a surrounding `Form`.
- `hint` (type `ReactNode`, optional). Helper text shown below the label. It is part of the label element, so it is read as part of the input's accessible name.
- `optional` (type `boolean`, optional, default `false`). Marks the field as not required. This appends "(optional)" to the label (or, when the theme marks required fields, hides the required asterisk) and sets `aria-required` on the input only when the field is not optional.
- `characterLimit` (type `number`, optional). Maximum number of characters, shown as a live "count/limit" counter below the input. The limit is advisory: it is not enforced on the input, but the counter turns to the error colour and screen readers are told how many characters remain or are over.
- `fullWidth` (type `'always' | 'never' | 'small'`, optional, default `'small'`). Controls when the field stretches to the full width of its container. `always` is always full width, `never` always uses the width set by `characters`, and `small` is full width on small screens only.
- `characters` (type `number`, optional, default `25`). Width of the input, as an approximate number of characters (the width is set to `0.8em` per character, rounded up). Does not limit how much can be typed.
- `affixWidth` (type `number`, optional, default `0`). Extra width in pixels added to the width calculated from `characters`, to make room for icons or buttons inside the input.
- `iconLeft` (type `IconName | (string & {})`, optional). Name of an icon to show inside the left of the input. The input padding increases to make room for it.
- `iconRight` (type `IconName | (string & {})`, optional). Name of an icon to show inside the right of the input. The input padding increases to make room for it.
- `autoComplete` (type `string`, optional). Hint to the browser's autofill, for example `email`. When the `disableAutocomplete` theme config flag is enabled this defaults to `off`; a value passed here overrides that.