Skip to main content

Migrate to the Polaris button component

The Polaris button component triggers actions and navigation. It replaces the previous Button component and is available as <s-button> in API versions 2025-10 and newer.


The following properties are different in the Polaris button component.

The previous Button onPress prop is now called onClick.

The previous Button to prop is now called href.

The previous Button appearance prop is now called tone.

Previous valueNew valueMigration notes
Default (no value)'auto''auto' is the new default.
'critical''critical'No change needed.
'monochrome'Removedmonochrome is no longer available. 'neutral' is the closest alternative, but the behavior isn't identical.

The previous Button kind prop is now called variant.

Previous valueNew valueMigration notes
Default (primary)'auto'The default changed from 'primary' to 'auto'.
'primary''primary'No change needed.
'secondary''secondary'No change needed.
'plain'RemovedUse <s-link> instead.

Migrating kind to variant

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<s-button variant="secondary">
Cancel
</s-button>
);
}
import {
reactExtension,
Button,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<Button kind="secondary">
Cancel
</Button>
);
}

Migrating kind='plain' to s-link

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<s-link href="/details">
View details
</s-link>
);
}
import {
reactExtension,
Button,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<Button kind="plain" to="/details">
View details
</Button>
);
}

Anchor to submit and accessibilityRole="submit"submit and accessibilityRole="submit"

The previous Button submit prop and accessibilityRole="submit" have been replaced with type="submit".

Previous patternNew patternMigration notes
submit={true}type="submit"Use type prop.
accessibilityRole="submit"type="submit"Use type prop.

Anchor to toggles, activateAction, and activateTargettoggles, activateAction, and activateTarget

The previous Button toggles, activateAction, and activateTarget props have been replaced with command and commandFor.

Previous patternNew patternMigration notes
toggles="modal-id"command="--toggle" + commandFor="modal-id"Toggle behavior for modals, sheets, etc.
activateAction="copy" + activateTarget="element-id"command="--copy" + commandFor="element-id"Copy behavior now uses the command pattern.

Migrating activateAction to command

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<>
<s-button command="--copy" commandFor="promo-code">
Copy promo code
</s-button>
<s-clipboard-item id="promo-code" text="SAVE25"></s-clipboard-item>
</>
);
}
import {
reactExtension,
Button,
ClipboardItem,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<>
<Button activateAction="copy" activateTarget="promo-code">
Copy promo code
</Button>
<ClipboardItem id="promo-code" text="SAVE25" />
</>
);
}

Anchor to Combining toggle and copyCombining toggle and copy

The previous Button accepted both toggles (to show an overlay like a tooltip) and activateAction (to copy a value) on the same element. With the new command pattern, command accepts a single action. To copy a value and surface a tooltip from the same button, use command="--copy" for the copy action and interestFor to show a sibling <s-tooltip> on hover and focus.

Migrating combined toggles and activateAction

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<>
<s-button
command="--copy"
commandFor="promo-code"
interestFor="copy-tooltip"
>
Copy promo code
</s-button>
<s-tooltip id="copy-tooltip">Copies SAVE25 to the clipboard</s-tooltip>
<s-clipboard-item id="promo-code" text="SAVE25"></s-clipboard-item>
</>
);
}
import {
reactExtension,
Button,
ClipboardItem,
Tooltip,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<>
<Button
toggles="copy-tooltip"
activateAction="copy"
activateTarget="promo-code"
overlay={
<Tooltip id="copy-tooltip">
Copies SAVE25 to the clipboard
</Tooltip>
}
>
Copy promo code
</Button>
<ClipboardItem id="promo-code" text="SAVE25" />
</>
);
}

The Polaris button component no longer supports loadingLabel. Loading state accessibility is handled automatically.

The Polaris button component no longer supports inlineAlignment. The previous Button filled its container by default, and inlineAlignment aligned the button's content within that full-width control. The Polaris button defaults to intrinsic width (inlineSize="auto"), so to preserve the previous behavior, set inlineSize="fill" and wrap the button's children in <s-stack> using justifyContent to align the content. There isn't a dedicated content-alignment prop on <s-button>, so the nested stack is the recommended pattern.

'fill' on <s-button> vs. <s-box>

<s-button> keeps the 'fill' keyword for inlineSize. This differs from <s-box>, where 'fill' is removed in favor of '100%'. Each component's accepted values are documented on its own reference page.

Migrating inlineAlignment to a content stack

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<s-button inlineSize="fill">
<s-stack direction="inline" justifyContent="end" minInlineSize="100%">
Pay now
</s-stack>
</s-button>
);
}
import {
reactExtension,
Button,
Text,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<Button inlineAlignment="end">
<Text>Pay now</Text>
</Button>
);
}

The Polaris button component no longer supports overlay. Render the overlay as a sibling element with an id and wire the trigger to it. The migration pattern depends on what kind of overlay you're showing:

  • For modals, sheets, and popovers (click-activated overlays): use command and commandFor on the trigger.
  • For tooltips (hover/focus-activated overlays): use interestFor on the trigger pointing to a sibling <s-tooltip id="...">. See the tooltip migration guide for details.

The example below shows the modal case.

Migrating overlay to command

import '@shopify/ui-extensions/preact';
import {render} from 'preact';

export default function extension() {
render(<Extension />, document.body);
}

function Extension() {
return (
<>
<s-button command="--show" commandFor="details-modal">
More info
</s-button>
<s-modal id="details-modal" heading="Details">
<s-paragraph>Delivery in 2 to 4 business days.</s-paragraph>
</s-modal>
</>
);
}
import {
reactExtension,
Button,
Modal,
Text,
} from '@shopify/ui-extensions-react/checkout';

export default reactExtension(
'purchase.checkout.block.render',
() => <Extension />,
);

function Extension() {
return (
<Button
overlay={
<Modal id="details-modal" title="Details">
<Text>Delivery in 2 to 4 business days.</Text>
</Modal>
}
>
More info
</Button>
);
}

The Polaris button component introduces the following new properties:

New propTypeDescription
command'--auto' | '--toggle' | '--copy' | '--show' | '--hide'Sets the action to run when the button is activated.
commandForstringSets the ID of the target component for the command.
inlineSize'auto' | 'fill' | 'fit-content'Controls the button's inline size. Defaults to 'auto'.
target'auto' | '_blank'Controls where a linked URL opens when href is used.
interestForstringSets the ID of the component that should respond to hover and focus.

The previous Button component filled the available inline space by default. The Polaris button component defaults to 'auto' sizing. Set inlineSize='fill' to match the previous behavior.

Previous valueNew valueMigration notes
Default (fill)'auto'Set inlineSize='fill' to preserve the previous behavior.

Was this page helpful?