Skip to main content

Migrate Toast from Polaris React

The Toast API replaces the Polaris React Toast component. Both display brief, non-blocking feedback, but the Toast API is imperative: call shopify.toast.show() when an operation finishes instead of rendering a component from state.


The following example removes the Toast component, the state that controls whether it is mounted, and the Frame that hosts it. The success and error messages move into the operation that produces each result.

Migrating a save notification

function SaveProduct({onSave}: {onSave: () => Promise<void>}) {
async function handleSave() {
try {
await onSave();
shopify.toast.show('Product saved', {duration: 5000});
} catch {
shopify.toast.show("Product couldn't be saved", {isError: true});
}
}

return <s-button onClick={handleSave}>Save product</s-button>;
}
import {Button, Frame, Toast} from '@shopify/polaris';
import {useState} from 'react';

export function SaveProduct({onSave}: {onSave: () => Promise<void>}) {
const [toast, setToast] = useState<{
content: string;
error?: boolean;
} | null>(null);

async function handleSave() {
try {
await onSave();
setToast({content: 'Product saved'});
} catch {
setToast({content: "Product couldn't be saved", error: true});
}
}

return (
<Frame>
<Button onClick={handleSave}>Save product</Button>
{toast && (
<Toast
content={toast.content}
duration={5000}
error={toast.error}
onDismiss={() => setToast(null)}
/>
)}
</Frame>
);
}

Preview


Map the properties used at each call site to the options passed to shopify.toast.show().

Polaris ReactToast APIMigration notes
contentFirst argument to shopify.toast.show()Pass the message as a string.
durationdurationThe value remains a duration in milliseconds.
errorisErrorRename the option.
action.contentactionPass the action label as a string.
action.onActiononActionMove the callback to the toast options.
onDismissonDismissThe Toast API callback runs when the user clicks the dismiss control. Don't use it to detect expiry after duration.

For example, migrate an undo action from a Polaris React action descriptor:

Migrating a toast action

shopify.toast.show('Product archived', {
action: 'Undo',
onAction: restoreProduct,
onDismiss: dismissToast,
});
<Toast
content="Product archived"
action={{
content: 'Undo',
onAction: restoreProduct,
}}
onDismiss={dismissToast}
/>

Anchor to Removed state and hostingRemoved state and hosting

Remove state used only to mount and unmount Toast, such as active or a nullable toast descriptor. Call shopify.toast.show() from the success or failure path instead.

Keep state that belongs to the underlying operation. For example, a Save button might still need loading state while the request is in progress.

Toast required a Polaris React Frame ancestor. The Toast API renders the notification in the Shopify admin, so it doesn't need an in-iframe host. Remove Frame only after migrating its other consumers, including navigation, loading indicators, and contextual save bars.

To hide a toast before its duration elapses, store the ID returned by shopify.toast.show() and pass it to shopify.toast.hide(id). Most confirmation toasts should expire on their own.


Anchor to Preserve actionable errorsPreserve actionable errors

A toast is temporary. When an error requires the user to change a field, retry an operation, or refer to details, keep that error visible in the relevant form or page. You can also show an error toast as immediate feedback, but it shouldn't be the only place the recovery instructions appear.


  • Verify the success, error, action, and manual-dismiss paths used by the app.
  • Confirm that repeated actions don't leave stale toast state in the React tree.
  • Confirm that route changes and component unmounts don't suppress operation results that should still be shown.
  • Test inside the Shopify admin, where the host renders the toast.

Anchor to Remove Polaris ReactRemove Polaris React

After every Toast call site is migrated, remove the Toast import and state used only to render it. Remove Frame separately after its remaining consumers are migrated. Remove @shopify/polaris only after no other route in scope imports it.



Was this page helpful?