On this page
Currency Field
A form field for entering monetary amounts, with a currency symbol prefix
- Overview
- Rules and guidance
- Properties
- Styles
- Accessibility
When to use
- When the user needs to enter a monetary amount, such as a payment value, budget, or balance
- When the currency context helps users understand what to enter, for example when the amount could be confused with a percentage or unit quantity
- When the accepted currencies are GBP, EUR, AUD, or USD
When not to use
- For plain numbers without currency context - use Text Field instead.
- To display a read-only currency value - use Summary List or a plain text element instead.
- For currencies other than GBP, EUR, AUD, or USD - the component does not support other currencies. Use Text Field with appropriate labelling instead.
- To indicate that a field is unavailable - the disabled state is not supported. Use Summary List to show values that cannot be edited, or conditionally hide the field if it is not relevant in the current context.
Anatomy
- Label
- Hint
- Currency symbol (displayed as a prefix icon inside the input, not editable by the user)
- Input
- Error message
- Required/Optional indicator
Basic usage
Edit live code
<CurrencyField id="saving" label="Saving" characters={10} />
Variants and features
Currency
Use the currency prop to set the currency. The symbol is displayed as a prefix inside the input and announced to screen readers via a visually hidden label.
Supported values: GBP (default), EUR, AUD, USD.
Edit live code
<Stack spaceBetween={4}> <CurrencyField id="saving-gbp" label="Pounds" characters={10} /> <CurrencyField id="saving-eur" label="Euros" currency="EUR" characters={10} /> <CurrencyField id="saving-aud" label="Australian dollars" currency="AUD" characters={10} /> <CurrencyField id="saving-usd" label="US dollars" currency="USD" characters={10} /> </Stack>
Common form features
The following features are shared across all Nebula form fields. See Form guidance 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. For currency fields, hint text is useful for clarifying expected format, for example "Do not include pence" or "Enter the amount in pounds".
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. The default of 25 characters is appropriate for most monetary amounts. Currency Field is responsive by default and displays full width below the smallest breakpoint. Override with fullWidth="always" or fullWidth="never".
Edit live code
<Stack spaceBetween={4}> <CurrencyField id="saving-error" label="Monthly payment" error="Enter an amount" /> <CurrencyField id="saving-hint" label="Monthly payment" hint="Do not include pence" /> <CurrencyField id="saving-optional" label="Additional payment" optional /> <CurrencyField id="saving-width" label="Monthly payment" characters={10} /> </Stack>
Grouping
Currency fields are typically part of a larger form. See the Form component and form guidelines for guidance on grouping and layout.
usage
- CurrencyField must only be used when the user is entering a monetary amountThe currency symbol prefix and decimal inputMode create an expectation of monetary input. Using it for other numeric input misleads users.Copy rule ID for currencyfield.use-for-monetary-input
- CurrencyField must only use one of the four supported currency values (GBP, EUR, AUD, USD)These are the only values that map to a symbol icon and a visually hidden label. Other currencies have no symbol or accessible label.Copy rule ID for currencyfield.supported-currencies-only
- CurrencyField must not be used to display a read-only valueIt renders an interactive input element. Use Summary List or a text element for read-only currency values.Copy rule ID for currencyfield.do-not-use-for-read-only
- CurrencyField must not be passed the disabled propThe 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. Use error handling or conditional visibility instead.Copy rule ID for currencyfield.no-disabled-state
- CurrencyField should be replaced with plain text. For example in a SummaryList. When displaying a value the user cannot editA field that looks interactive but cannot be changed is confusing. Plain text communicates the value without implying editability.Copy rule ID for currencyfield.non-editable-as-text
content
- Label must always be providedid and label are required props. The label is the primary accessible name for the input.Copy rule ID for currencyfield.label-required
- Label must describe the monetary value being entered, not the currencyThe currency symbol is already shown as a prefix. The label should describe the value, for example "Monthly budget" not "Amount in pounds".Copy rule ID for currencyfield.label-describes-value
- Error message must describe the exact problem and what the user should do to correct itVague messages like "Invalid amount" do not help users understand what is wrong. Prefer "Enter an amount greater than zero" or "Enter a number, for example 250".Copy rule ID for currencyfield.error-message-specific
- CurrencyField must not require or expect the user to type the currency symbolThe component renders the symbol as a prefix icon. Including the symbol in user input will break value parsing.Copy rule ID for currencyfield.no-symbol-in-input
- Hint should be provided when the expected format or constraints are not obvious from the label aloneMonetary inputs often have constraints (e.g. no pence, minimum value) that users may not anticipate.Copy rule ID for currencyfield.hint-for-format
- Error message should not repeat the currency symbol in the error messageThe symbol is already visible in the input prefix. Repeating it adds noise without value.Copy rule ID for currencyfield.error-message-no-symbol
interaction
- Currency symbol will be announced to screen readers via a visually hidden label added by the componentThe component automatically adds a visually hidden element (e.g. "pounds") linked via aria-describedby. Do not suppress or override this.Copy rule ID for currencyfield.currency-announced
- CurrencyField must use inputMode="decimal" unless an integer-only value is explicitly requiredThe component defaults to inputMode="decimal" to trigger the numeric keyboard with a decimal separator on mobile. Overriding to "numeric" removes the decimal point for users who need to enter pence.Copy rule ID for currencyfield.decimal-input-mode
- CurrencyField must validate on form submission, not while the user is typingInline validation on change causes premature errors and degrades usability. See forms pattern guide.Copy rule ID for currencyfield.validate-on-submit
composition
- CurrencyField must always receive an id propid is required to associate the label with the input via htmlFor and to generate the error element id for aria-describedby.Copy rule ID for currencyfield.id-required
- If used inside a Form componentCurrencyField must include a name prop when used inside a Form componentForm uses the name prop to route errors from the errors array to the correct field via context.Copy rule ID for currencyfield.name-when-in-form
- CurrencyField should set the characters prop to reflect the expected length of the input valueInput width is a affordance. An oversized input for a small value (e.g. a two-digit count) can imply a larger entry is expected.Copy rule ID for currencyfield.characters-appropriate
CurrencyField
Also accepts all props from HTMLAttributes, InputHTMLAttributes.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
id | string | — | Yes | 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 | ReactNode | — | No | 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 | string | — | Yes | Visible label for the field. It is associated with the input, so it forms the input's accessible name. |
name | string | — | No | The `name` attribute passed to the input. Also used to look up the field's error from a surrounding `Form`. |
hint | ReactNode | — | No | 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 | boolean | false | No | 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. |
fullWidth | always never small | 'small' | No | 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 | number | 25 | No | 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 | number | 40 | No | Extra width in pixels added to the width calculated from `characters`, to make room for icons or buttons inside the input. |
currency | GBP EUR AUD USD | 'GBP' | No | Currency whose symbol is shown in the input. `GBP` shows a pound sign, `EUR` a euro sign, and `AUD` and `USD` both a dollar sign. The currency name is also announced to screen readers. |
iconAlignment | left right | 'left' | No | Which side of the input the currency symbol appears on. |
This component has no styles of its own to document.
In brief
Currency Field is a native text input for an amount of money, with the currency symbol drawn inside it. The symbol is hidden from screen readers, and the currency name (pounds, euro or dollar) is added to the field's description, not to its name.
What Nebula provides
- A real label linked to the input by id, with aria-required unless the field is optional.
- A decimal keypad on touch devices, through inputMode.
- An error sets aria-invalid and is linked to the input with aria-describedby. The message has an icon as well as colour.
- The symbol is hidden from screen readers, and the currency name is linked to the input as part of its description.
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
If the unit matters, say it in the label
The currency name is a description, which some screen readers skip or read late, and Australian and US dollars are both announced as "dollar". Write "Direct Debit amount in euros" when the symbol alone could be misread.
WCAG: 3.3.2 Labels or Instructions
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, such as the decimal format, in the hint.
WCAG: 3.3.2 Labels or Instructions
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
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
Avoid the browser's own form validation for the amount
The input carries pattern="[0-9]*", which a form that is not noValidate rejects for a decimal such as 25.50 with a browser message. Validate the amount on submit and show the result through error.
WCAG: 3.3.1 Error Identification
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 these tests and statuses mean
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.
The tests
- 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.
The statuses
- 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.