Skip to main content

Migrate Collapsible from Polaris React

Polaris web components don't have a direct Collapsible. Use native details and summary for a merchant-controlled disclosure. Use conditional rendering when app state determines whether content exists at all.


Anchor to Migrate a disclosureMigrate a disclosure

Polaris web components

function AdvancedSettings() {
return (
<details>
<summary>Advanced settings</summary>
<s-box paddingBlockStart="base">
<s-text-field
label="Webhook URL"
name="webhookUrl"
details="Send order events to this HTTPS endpoint"
/>
</s-box>
</details>
);
}

Polaris React

import {Button, Collapsible, TextField} from '@shopify/polaris';

function AdvancedSettings() {
const [open, setOpen] = useState(false);
return (
<>
<Button
onClick={() => setOpen((value) => !value)}
ariaExpanded={open}
ariaControls="advanced-settings"
>
Advanced settings
</Button>
<Collapsible id="advanced-settings" open={open}>
<TextField label="Webhook URL" autoComplete="url" />
</Collapsible>
</>
);
}

Anchor to Replace Collapsible propertiesReplace Collapsible properties

Polaris ReactDestinationMigration notes
openopen on details, or the conditional-rendering predicateUse controlled state only when other app behavior needs it.
idRemove for native disclosure, or retain for app logicsummary already controls its containing details.
transitionRemoveDon't reproduce animation timing before semantics and state are correct.
expandOnPrintPrint-specific app styles only when the content must printVerify the actual printed workflow.
childrenDisclosure content or conditionally rendered contentDon't hide required primary task content.

Use conditional rendering for permission-gated, mode-specific, or unavailable content. Use hidden only when keeping DOM state is intentional and test that hidden controls can't receive focus.

Don't use a disclosure for validation errors, primary actions, or information merchants must read to complete the task. Keep those visible.


  • Toggle with pointer, Enter, and Space, and verify the expanded state is announced.
  • Tab through closed and open states and confirm hidden controls aren't focusable.
  • Preserve or intentionally reset field values when closing.
  • Test browser reload and deep-link restoration if open state was persisted.
  • Print the page when the old call site used expandOnPrint.

Anchor to Remove Polaris ReactRemove Polaris React

Remove Collapsible, manual ariaExpanded and ariaControls wiring replaced by native disclosure, and transition helpers with no remaining caller. Remove @shopify/polaris only after no other route in scope imports it.


Migrating Collapsible

import {useEffect, useRef, useState} from 'react';

export function CollapsibleMigrationExample() {
const [open, setOpen] = useState(true);
const button = useRef<HTMLElementTagNameMap['s-button']>(null);

useEffect(() => {
button.current?.setAttribute('aria-expanded', String(open));
button.current?.setAttribute('aria-controls', 'shipping-details');
}, [open]);

return (
<s-stack gap="small" alignItems="start">
<s-button ref={button} onClick={() => setOpen((expanded) => !expanded)}>
Shipping details
</s-button>
<div id="shipping-details" hidden={!open}>
<s-text>Ships in 2–3 days.</s-text>
</div>
</s-stack>
);
}
import {useState} from 'react';
import {Button, Collapsible, Text} from '@shopify/polaris';

export function CollapsibleMigrationExample() {
const [open, setOpen] = useState(true);
return (
<>
<Button onClick={() => setOpen(!open)}>Shipping details</Button>
<Collapsible open={open} id="shipping">
<Text as="p">Ships in 2 to 3 days.</Text>
</Collapsible>
</>
);
}

Preview



Was this page helpful?