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.

TextField

The TextField component provides a single-line text input for collecting short-form text from merchants, such as names, titles, or search queries. It supports labels, placeholder text, validation errors, read-only and disabled states, autocomplete hints, and a suffix decoration.

For multi-line input, use TextArea. For specialized input types, use EmailField, URLField, NumberField, or PasswordField.

Support
Targets (46)

Supported targets


Props for the TextField component, a single-line text input for entering short-form content such as names, emails, or search queries. It extends standard input props with min/max length constraints, autocomplete support, and a field decoration option (suffix).

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 maxLength
maxLength
number

The maximum number of characters the user can enter. If the current value exceeds this limit, then the field will be in an error state. This doesn't prevent the user from typing beyond the limit. Use the error prop to communicate the constraint.

Anchor to minLength
minLength
number

The minimum number of characters required for a valid input. If the current value is shorter than this limit, then the field will be in a validation error state. This doesn't prevent the user from submitting a shorter value. Use the error prop to communicate the constraint.

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: string) => 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: string) => 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.

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 Collect product metadataCollect product metadata

Tag a product with a custom warehouse label and save it from an action modal. This example uses TextField to capture the value, with a Button that saves the label.

Collect product metadata

Tag a product with a custom warehouse label and save it from an action modal. This example uses `TextField` to capture the value, with a [Button](/docs/api/admin-extensions/2025-07/ui-components/actions/button) that saves the label.

Collect product metadata

import {useState} from 'react';
import {reactExtension, useApi, TextField, 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 [label, setLabel] = useState('');

return (
<BlockStack>
<TextField
label="Custom warehouse label"
name="warehouseLabel"
value={label}
onChange={setLabel}
/>
<Button
variant="primary"
onPress={async () => {
await fetch('/api/products/label', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, label}),
});
close();
}}
>
Save label
</Button>
</BlockStack>
);
}

export default reactExtension(
'admin.product-details.action.render',
() => <App />,
);
import {extension, TextField, 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 customLabel = '';

const stack = root.createComponent(BlockStack);

const field = root.createComponent(TextField, {
label: 'Custom warehouse label',
name: 'warehouseLabel',
onChange: (value) => {
customLabel = value;
},
});

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

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

Anchor to Validate input with error messagesValidate input with error messages

Validate SKU format on each keystroke using the error prop and onChange callback. This example checks minimum length and character restrictions as the merchant types, displaying inline error messages that prevent invalid data from being submitted.

Validate input with error messages

import {useState} from 'react';
import {reactExtension, useApi, TextField, 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 [sku, setSku] = useState('');
const [error, setError] = useState(undefined);

function validate(value) {
if (value.length < 3) {
return 'SKU must be at least 3 characters';
}
if (!/^[A-Z0-9-]+$/i.test(value)) {
return 'SKU can only contain letters, numbers, and hyphens';
}
return undefined;
}

return (
<BlockStack>
<TextField
label="SKU"
name="sku"
required
value={sku}
error={error}
onChange={(value) => {
setSku(value);
setError(validate(value));
}}
/>
<Button
variant="primary"
onPress={async () => {
const validationError = validate(sku);
if (!validationError) {
await fetch('/api/products/sku', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, sku}),
});
close();
}
}}
>
Save SKU
</Button>
</BlockStack>
);
}

export default reactExtension(
'admin.product-details.action.render',
() => <App />,
);
import {extension, TextField, 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 skuValue = '';

const stack = root.createComponent(BlockStack);

const skuField = root.createComponent(TextField, {
label: 'SKU',
name: 'sku',
required: true,
onChange: (value) => {
skuValue = value;
if (value.length < 3) {
skuField.updateProps({error: 'SKU must be at least 3 characters'});
} else if (!/^[A-Z0-9-]+$/i.test(value)) {
skuField.updateProps({error: 'SKU can only contain letters, numbers, and hyphens'});
} else {
skuField.updateProps({error: undefined});
}
},
});

const saveButton = root.createComponent(
Button,
{
variant: 'primary',
onPress: async () => {
if (skuValue.length >= 3) {
await fetch('/api/products/sku', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId, sku: skuValue}),
});
close();
}
},
},
'Save SKU',
);

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

Anchor to Add suffix labels to fieldsAdd suffix labels to fields

Display a unit or domain label after the editable portion of the field with the suffix prop. This example adds kg to a weight field and .myshopify.com to a handle field, making the implied format visible so merchants don't need to type it.

Add suffix labels to fields

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

function App() {

return (
<BlockStack>
<Text fontWeight="bold">Shipping configuration</Text>
<TextField label="Package weight" name="weight" suffix="kg" />
<TextField
label="Shopify handle"
name="handle"
suffix=".myshopify.com"
readOnly
/>
</BlockStack>
);
}

export default reactExtension(
'admin.product-details.block.render',
() => <App />,
);
import {extension, TextField, 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'},
'Shipping configuration',
);

const weightField = root.createComponent(TextField, {
label: 'Package weight',
name: 'weight',
suffix: 'kg',
});

const handleField = root.createComponent(TextField, {
label: 'Shopify handle',
name: 'handle',
suffix: '.myshopify.com',
readOnly: true,
});

stack.appendChild(heading);
stack.appendChild(weightField);
stack.appendChild(handleField);
root.appendChild(stack);
},
);

  • Write a clear label: The required label prop tells merchants and screen reader users what information the field expects. Use specific, concise labels like "Product title" or "Order note".
  • Use placeholder text as a hint, not a label: Placeholders disappear when the merchant starts typing, so use them for formatting hints (like "Summer Sale") rather than as a replacement for the label.

  • The minLength and maxLength props constrain input length but don't display a character counter. If merchants need to see remaining characters, you must implement a counter separately.
  • TextField doesn't support input masking or formatting (such as phone number patterns).

Was this page helpful?