Skip to main content
Migrate to Polaris

Version 2025-07 is the last API version to support React-based UI components. Later versions use web components, native UI elements with built-in accessibility, better performance, and consistent styling with Shopify's design system. Check out the migration guide to upgrade your extension.

NumberField

The NumberField component provides a text input optimized for numeric values. Use this component to set minimum and maximum constraints, step increments, a suffix decoration, and virtual keyboard modes.

Use MoneyField for monetary values, and TextField for general text.

Support
Targets (46)

Supported targets


Props for the NumberField component, a text input for numeric values. Inherits standard input props, number constraints (min, max, step), autocomplete support, and field decoration props.

Anchor to label
label
string
required

The text content to display as the field's label. This label is always required for accessibility as it tells users what information the field expects. The label is rendered visually above the field.

Anchor to autocomplete
autocomplete
| AutocompleteField | `${} ${AutocompleteField}` | `${} ${AutocompleteField}` | `${} ${} ${AutocompleteField}` | boolean

A hint to the browser about the expected content of the field, used to offer autofill suggestions.

  • true: The field supports autofill, but no specific content type is specified.
  • false: The field contains sensitive or ephemeral data that should not be autofilled, such as one-time codes.
  • An AutocompleteField token (such as 'email' or 'street-address'): Tells the browser exactly what data to suggest for this field.

Learn more about the supported autocomplete values.

Anchor to defaultValue
defaultValue
string | string[]

The initial value of the field when it isn't controlled by state. Use this instead of value when you don't need to manage the field's state yourself. The component tracks its own value internally and reports changes through onChange.

Anchor to disabled
disabled
boolean
Default: false

Whether the field is disabled. When true, the field can't be edited by the user, won't receive focus, and won't be submitted with the form. Use this for fields that aren't relevant in the current context.

Anchor to error
error
string

An error message to display below the field. When set, the field receives a specific stylistic treatment (typically a red border) to communicate problems that have to be resolved immediately. The string value is displayed as the error message.

Pass undefined or omit this prop to clear the error state.

string

A unique identifier for the field.

Anchor to inputMode
inputMode
'decimal' | 'numeric'
Default: 'decimal'

The type of virtual keyboard displayed on touch devices.

  • decimal: Shows a numeric keyboard with a decimal separator, suitable for values that may include fractional digits (for example, prices).
  • numeric: Shows a numeric keyboard without a decimal separator, suitable for whole numbers (for example, quantities).
number

The highest decimal or integer to be accepted for the field. When using the stepper buttons, the value won't exceed this limit. If step would overshoot, then the value rounds down to max. A user can still type a number higher than max using the keyboard, so add appropriate validation with the error prop.

number
Default: 0

The lowest decimal or integer to be accepted for the field. When using the stepper buttons, the value won't go below this limit. If step would undershoot, then the value rounds up to min. A user can still type a number lower than min using the keyboard, so add appropriate validation with the error prop.

string

An identifier for the field that is unique within the nearest containing Form component.

Anchor to onBlur
onBlur
() => void

A callback fired when the field loses focus. This is useful for triggering validation after the user finishes interacting with the field, or for tracking which fields have been "touched" in a form.

Anchor to onChange
onChange
(value: number) => void

A callback that fires when the user finishes editing the field, typically on blur. Only fires if the value changed. Update your state in this callback and pass the new value back through the value prop.

This doesn't fire on every keystroke. Use onInput for real-time responses like clearing validation errors as the user types. Don't use onInput to control value because that can cause issues on lower-powered devices due to asynchronous rendering.

Anchor to onFocus
onFocus
() => void

A callback fired when the field receives focus. This is useful for clearing errors, showing helper text, or tracking user interaction with form fields.

Anchor to onInput
onInput
(value: number) => void

A callback that fires on every change the user makes in the field, including each keystroke. The callback receives the current value.

Use onInput for immediate responses like clearing validation errors as the user types. Don't use it to control the field's value prop. Use onChange for that instead.

Anchor to placeholder
placeholder
string

A short hint displayed inside the field when it's empty. Use placeholder text to show an example of the expected value (such as "100" or "Search by name"). Don't use placeholder text as a substitute for the label as it disappears after the user starts typing.

Anchor to readOnly
readOnly
boolean
Default: false

Whether the field is read-only. Unlike disabled, a read-only field can still receive focus and its value is included when the form is submitted. Use this when the value should be visible and selectable but not editable, such as a computed total.

Anchor to required
required
boolean

Whether the field needs a value. This requirement adds semantic value to the field, but it won't cause an error to appear automatically. If you want to present an error when this field is empty, you can do so with the error prop.

number
Default: 1

The increment amount for the stepper buttons. This can be an integer or decimal (such as 0.01 for cents). Each press of the stepper button increases or decreases the value by this amount. If max or min is set, the final value always rounds to the boundary rather than overshooting.

Anchor to suffix
suffix
string
Default: ''

A non-editable string displayed after the input area, such as "@shopify.com" or "%". The suffix isn't included in the field's value. It might be hidden until the user focuses the field if an inline label occupies the same space.

Anchor to value
value
T

The current value for the field. If omitted, then the field will be empty. You should update this value in response to the onChange callback.


Anchor to Set inventory restock quantitySet inventory restock quantity

Specify a restock quantity between 1 and 10,000. This example uses NumberField with min and max props to constrain the input, and a Button that submits the restock and closes the modal.

Set inventory restock quantity

Specify a restock quantity between 1 and 10,000. This example uses `NumberField` with `min` and `max` props to constrain the input, and a [Button](/docs/api/admin-extensions/2025-07/ui-components/actions/button) that submits the restock and closes the modal.

Set inventory restock quantity

import {useState} from 'react';
import {reactExtension, useApi, NumberField, Button, BlockStack} from '@shopify/ui-extensions-react/admin';

function App() {
const {data, close} = useApi('admin.product-details.action.render');
const productId = data.selected[0]?.id;
const [quantity, setQuantity] = useState(0);

return (
<BlockStack>
<NumberField
label="Restock quantity"
name="quantity"
min={1}
max={10000}
value={quantity}
onChange={setQuantity}
/>
<Button
variant="primary"
onPress={async () => {
await fetch('/api/inventory/restock', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, quantity}),
});
close();
}}
>
Submit restock
</Button>
</BlockStack>
);
}

export default reactExtension(
'admin.product-details.action.render',
() => <App />,
);
import {extension, NumberField, Button, BlockStack} from '@shopify/ui-extensions/admin';

export default extension(
'admin.product-details.action.render',
(root, api) => {
const {data, close} = api;
const productId = data.selected[0]?.id;
let quantity = 0;

const stack = root.createComponent(BlockStack);

const field = root.createComponent(NumberField, {
label: 'Restock quantity',
name: 'quantity',
min: 1,
max: 10000,
onChange: (value) => {
quantity = value;
},
});

const saveButton = root.createComponent(
Button,
{
variant: 'primary',
onPress: async () => {
await fetch('/api/inventory/restock', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, quantity}),
});
close();
},
},
'Submit restock',
);

stack.appendChild(field);
stack.appendChild(saveButton);
root.appendChild(stack);
},
);

Anchor to Configure decimal pricing fieldsConfigure decimal pricing fields

Collect fractional values like cost pricing and profit margins from merchants. This example uses inputMode="decimal" and combines step with suffix to guide merchants through entering currency amounts and percentages with the correct precision.

Configure decimal pricing fields

import {reactExtension, NumberField, BlockStack, Text} from '@shopify/ui-extensions-react/admin';

function App() {

return (
<BlockStack>
<Text fontWeight="bold">Cost pricing</Text>
<NumberField
label="Cost per item"
name="costPerItem"
inputMode="decimal"
min={0}
step={0.01}
suffix="USD"
/>
<NumberField
label="Profit margin"
name="margin"
inputMode="decimal"
min={0}
max={100}
step={0.5}
suffix="%"
/>
</BlockStack>
);
}

export default reactExtension(
'admin.product-details.action.render',
() => <App />,
);
import {extension, NumberField, BlockStack, Text} from '@shopify/ui-extensions/admin';

export default extension(
'admin.product-details.action.render',
(root) => {

const stack = root.createComponent(BlockStack);

const heading = root.createComponent(
Text,
{fontWeight: 'bold'},
'Cost pricing',
);

const costField = root.createComponent(NumberField, {
label: 'Cost per item',
name: 'costPerItem',
inputMode: 'decimal',
min: 0,
step: 0.01,
suffix: 'USD',
});

const marginField = root.createComponent(NumberField, {
label: 'Profit margin',
name: 'margin',
inputMode: 'decimal',
min: 0,
max: 100,
step: 0.5,
suffix: '%',
});

stack.appendChild(heading);
stack.appendChild(costField);
stack.appendChild(marginField);
root.appendChild(stack);
},
);

Anchor to Enforce numeric constraints with errorsEnforce numeric constraints with errors

Enforce warehouse-specific limits using min, max, and step constraints with inline error feedback. This example validates slot numbers against a maximum capacity and shows an error when the value exceeds the allowed range.

Enforce numeric constraints with errors

import {useState} from 'react';
import {reactExtension, NumberField, BlockStack, Text} from '@shopify/ui-extensions-react/admin';

function App() {
const [error, setError] = useState(undefined);

return (
<BlockStack>
<Text fontWeight="bold">Warehouse slot configuration</Text>
<NumberField
label="Storage slot number"
name="slotNumber"
min={1}
max={500}
step={1}
error={error}
onChange={(value) => {
setError(value > 500 ? 'Slot number cannot exceed 500' : undefined);
}}
/>
<NumberField
label="Units per pallet"
name="unitsPerPallet"
min={1}
max={200}
step={1}
/>
</BlockStack>
);
}

export default reactExtension(
'admin.product-details.block.render',
() => <App />,
);
import {extension, NumberField, BlockStack, Text} from '@shopify/ui-extensions/admin';

export default extension(
'admin.product-details.block.render',
(root) => {

const stack = root.createComponent(BlockStack);

const heading = root.createComponent(
Text,
{fontWeight: 'bold'},
'Warehouse slot configuration',
);

const slotField = root.createComponent(NumberField, {
label: 'Storage slot number',
name: 'slotNumber',
min: 1,
max: 500,
step: 1,
error: undefined,
onChange: (value) => {
if (value > 500) {
slotField.updateProps({
error: 'Slot number cannot exceed 500',
});
} else {
slotField.updateProps({error: undefined});
}
},
});

const palletField = root.createComponent(NumberField, {
label: 'Units per pallet',
name: 'unitsPerPallet',
min: 1,
max: 200,
step: 1,
});

stack.appendChild(heading);
stack.appendChild(slotField);
stack.appendChild(palletField);
root.appendChild(stack);
},
);

  • Set min, max, and step for bounded values: When the acceptable range is known (like quantities, percentages, or weights), set constraints to prevent invalid entries.

  • The min and max props define valid value boundaries but don't prevent merchants from typing values outside the range. You must validate the value in your onChange handler and set the error prop accordingly.
  • NumberField returns a numeric value through onChange, not a string. Non-numeric input (like letters) isn't passed through to the callback.

Was this page helpful?