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.

Button

The Button component triggers actions like submitting forms, opening dialogs, or navigating to other pages. It supports multiple visual variants and tones to establish a clear hierarchy of actions within your extension.

For navigation-focused interactions within text, use Link. To make a custom area clickable without button styling, use Pressable.

Support
Targets (46)

Supported targets


Props for the Button component. A button can either be a standard action button (ButtonBaseProps) or an anchor-style button that navigates to a URL (ButtonAnchorProps).

string
required

The URL to navigate to when the button is pressed. When set, the button behaves as a link. If an onClick callback is also provided, it runs first, then the navigation occurs.

Anchor to accessibilityLabel
accessibilityLabel
string

A label read by assistive technologies like screen readers to describe the button's purpose. Use this when the button contains only an icon or when the visible text doesn't provide enough context on its own for users who can't see the button.

Anchor to disabled
disabled
boolean
Default: false

Whether the button is disabled. When true, the button can't be interacted with and is rendered in a visually muted style to indicate it isn't available.

Anchor to download
download
boolean | string

When set, the linked resource is downloaded instead of being navigated to. Pass a string to specify a custom filename for the downloaded file, or true to use the default filename.

string

A unique identifier for the button. Use this when you need to reference the button programmatically or distinguish it from other buttons in the same view.

string

An alias for language. The language of the button's text content. Use this when the button text is in a different language than the rest of the page so assistive technologies can invoke the correct pronunciation. Must be a valid IANA language subtag.

Anchor to language
language
string

The language of the button's text content. Use this when the button text is in a different language than the rest of the page so assistive technologies can invoke the correct pronunciation. Must be a valid IANA language subtag.

Anchor to onBlur
onBlur
() => void

A callback fired when the button loses focus. Use this to trigger validation or other logic when the merchant moves away from the button.

Anchor to onClick
onClick
() => void

A callback fired when the link button is pressed. The callback runs first, then the navigation to href occurs.

Anchor to onFocus
onFocus
() => void

A callback fired when the button receives focus. Use this to trigger logic when the merchant focuses the button through keyboard navigation or other input methods.

Anchor to onPress
onPress
() => void

An alias for onClick. A callback fired when the link button is pressed. The callback runs first, then the navigation to href occurs.

Anchor to target
target
'_blank' | '_self'
Default: '_self'

The browsing context in which to open the linked URL.

  • _blank: Opens in a new tab or window.
  • _self: Opens in the same context (default).
string

An alias for href. The URL to navigate to when the button is pressed.

'default' | 'critical'
Default: 'default'

The color treatment of the button, used to convey its intent.

  • default: Standard button color for general actions.
  • critical: Red coloring to indicate a destructive or irreversible action, such as deleting a resource.
Anchor to variant
variant
'primary' | 'secondary' | 'tertiary'
Default: 'secondary'

The visual style of the button, used to convey its level of emphasis.

  • primary: A high-emphasis button for the main action in a section.
  • secondary: A medium-emphasis button for supporting actions.
  • tertiary: A low-emphasis button for less prominent actions, rendered as a plain text link-style button.
Anchor to accessibilityLabel
accessibilityLabel
string

A label read by assistive technologies like screen readers to describe the button's purpose. Use this when the button contains only an icon or when the visible text doesn't provide enough context on its own for users who can't see the button.

Anchor to accessibilityRole
accessibilityRole
Default: 'button'

The semantic role of the button for assistive technologies.

  • submit: Submits the nearest containing form when pressed.
  • button: A standard button that triggers an action (default).
  • reset: Resets the nearest containing form to its default values.
Anchor to disabled
disabled
boolean
Default: false

Whether the button is disabled. When true, the button can't be interacted with and is rendered in a visually muted style to indicate it isn't available.

string

A unique identifier for the button. Use this when you need to reference the button programmatically or distinguish it from other buttons in the same view.

string

An alias for language. The language of the button's text content. Use this when the button text is in a different language than the rest of the page so assistive technologies can invoke the correct pronunciation. Must be a valid IANA language subtag.

Anchor to language
language
string

The language of the button's text content. Use this when the button text is in a different language than the rest of the page so assistive technologies can invoke the correct pronunciation. Must be a valid IANA language subtag.

Anchor to onBlur
onBlur
() => void

A callback fired when the button loses focus. Use this to trigger validation or other logic when the merchant moves away from the button.

Anchor to onClick
onClick
() => void

A callback fired when the button is pressed. If href is set, the callback runs first, then the navigation occurs.

Anchor to onFocus
onFocus
() => void

A callback fired when the button receives focus. Use this to trigger logic when the merchant focuses the button through keyboard navigation or other input methods.

Anchor to onPress
onPress
() => void

An alias for onClick. A callback fired when the button is pressed. If href is set, the callback runs first, then the navigation occurs.

'default' | 'critical'
Default: 'default'

The color treatment of the button, used to convey its intent.

  • default: Standard button color for general actions.
  • critical: Red coloring to indicate a destructive or irreversible action, such as deleting a resource.
Anchor to variant
variant
'primary' | 'secondary' | 'tertiary'
Default: 'secondary'

The visual style of the button, used to convey its level of emphasis.

  • primary: A high-emphasis button for the main action in a section.
  • secondary: A medium-emphasis button for supporting actions.
  • tertiary: A low-emphasis button for less prominent actions, rendered as a plain text link-style button.

Anchor to Sync product to backendSync product to backend

Sync product data to a warehouse and close the action modal, or cancel. This example renders a primary Button that posts to a backend API and calls close(), alongside a secondary cancel button.

Sync product to backend

Sync product data to a warehouse and close the action modal, or cancel. This example renders a `primary` `Button` that posts to a backend API and calls `close()`, alongside a `secondary` cancel button.

Sync product to backend

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

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

return (
<BlockStack>
<Text>Product: {productId}</Text>
<Button
variant="primary"
onPress={async () => {
await fetch('/api/products/sync', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({productId}),
});
close();
}}
>
Sync to warehouse
</Button>
<Button variant="secondary" onPress={() => close()}>
Cancel
</Button>
</BlockStack>
);
}

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

const stack = root.createComponent(BlockStack);

const info = root.createComponent(
Text,
{},
`Product: ${productId}`,
);

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

const cancelButton = root.createComponent(Button, {
variant: 'secondary',
onPress: () => close(),
}, 'Cancel');

stack.appendChild(info);
stack.appendChild(saveButton);
stack.appendChild(cancelButton);
root.appendChild(stack);
},
);

Anchor to Set button variants and tonesSet button variants and tones

Present publish, archive, and delete actions for a product using variant and tone props to communicate intent. This example uses primary for the main action, tertiary for a low-emphasis option, and critical tone for the destructive delete.

Set button variants and tones

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

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

return (
<BlockStack>
<Text>Choose an action for this product:</Text>
<Button
variant="primary"
onPress={async () => {
await fetch(`/api/products/${productId}/publish`, {method: 'POST'});
close();
}}
>
Publish product
</Button>
<Button
variant="tertiary"
onPress={async () => {
await fetch(`/api/products/${productId}/archive`, {method: 'POST'});
close();
}}
>
Archive product
</Button>
<Button
tone="critical"
onPress={async () => {
await fetch(`/api/products/${productId}`, {method: 'DELETE'});
close();
}}
>
Delete product
</Button>
</BlockStack>
);
}

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

const stack = root.createComponent(BlockStack);

const heading = root.createComponent(
Text,
{},
'Choose an action for this product:',
);

const publishButton = root.createComponent(Button, {
variant: 'primary',
onPress: async () => {
await fetch(`/api/products/${productId}/publish`, {method: 'POST'});
close();
},
}, 'Publish product');

const archiveButton = root.createComponent(Button, {
variant: 'tertiary',
onPress: async () => {
await fetch(`/api/products/${productId}/archive`, {method: 'POST'});
close();
},
}, 'Archive product');

const deleteButton = root.createComponent(Button, {
tone: 'critical',
onPress: async () => {
await fetch(`/api/products/${productId}`, {method: 'DELETE'});
close();
},
}, 'Delete product');

stack.appendChild(heading);
stack.appendChild(publishButton);
stack.appendChild(archiveButton);
stack.appendChild(deleteButton);
root.appendChild(stack);
},
);

Link to the product storefront page and documentation using href and target props. This example renders buttons as external links that open in new tabs, and uses accessibilityLabel to provide screen reader context.

Open external links

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

function App() {
const {data} = useApi('admin.product-details.block.render');
const productId = data.selected[0]?.id;
const numericId = productId?.split('/').pop();

return (
<BlockStack>
<Text>External resources</Text>
<Button
href={`https://your-store.myshopify.com/products/${numericId}`}
target="_blank"
variant="secondary"
accessibilityLabel="View product on storefront in a new tab"
>
View on storefront
</Button>
<Button
href="https://help.shopify.com/manual/products"
target="_blank"
variant="tertiary"
>
Product documentation
</Button>
</BlockStack>
);
}

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

export default extension(
'admin.product-details.block.render',
(root, api) => {
const {data} = api;
const productId = data.selected[0]?.id;
const numericId = productId?.split('/').pop();

const stack = root.createComponent(BlockStack);

const label = root.createComponent(
Text,
{},
'External resources',
);

const storefrontLink = root.createComponent(Button, {
href: `https://your-store.myshopify.com/products/${numericId}`,
target: '_blank',
variant: 'secondary',
accessibilityLabel: 'View product on storefront in a new tab',
}, 'View on storefront');

const docsLink = root.createComponent(Button, {
href: 'https://help.shopify.com/manual/products',
target: '_blank',
variant: 'tertiary',
}, 'Product documentation');

stack.appendChild(label);
stack.appendChild(storefrontLink);
stack.appendChild(docsLink);
root.appendChild(stack);
},
);

  • Label buttons clearly: Use strong, actionable verbs that describe the action, such as Save, Edit, or Add tags. Write labels in sentence case and avoid unnecessary articles like a, an, or the.
  • Use appropriate tones: Apply critical tone only for destructive actions that are difficult or impossible to undo, such as deleting a resource. Leave the tone as default for all other actions.
  • Establish clear hierarchy: Avoid placing multiple primary buttons in the same view. Reserve primary for the single most important action, use secondary for supporting actions, and tertiary for low-emphasis or supplementary actions.
  • Position consistently: Place buttons in predictable locations. In AdminAction components, use the primaryAction and secondaryAction properties. In Form components, place submit buttons at the bottom of the form.
  • Provide accessibility labels for icon-only usage: When using a Button without visible text, always set accessibilityLabel so screen reader users understand the button's purpose.

  • The Button component doesn't include a built-in loading state. To indicate that an async operation is in progress, consider disabling the button and displaying a nearby ProgressIndicator component.
  • When using href for navigation, external URLs (domains outside the Shopify admin) might be blocked or trigger a confirmation dialog depending on the extension context and browser security settings.
  • Disabled buttons (disabled={true}) can't receive focus or be triggered by keyboard, which may cause confusion if the reason for disabling isn't clear to the merchant. Pair disabled states with visible helper text explaining why the action is unavailable.

Was this page helpful?