---
title: Migrate Collapsible from Polaris React
description: >-
  Replace Polaris React Collapsible with native details and summary or
  intentional conditional rendering.
source_url:
  html: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/collapsible
  md: >-
    https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/collapsible.md
api_name: app-home
---

# 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.

***

## Migrate a disclosure

## Polaris web components

```tsx
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

```tsx
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>
    </>
  );
}
```

***

## Replace Collapsible properties

| Polaris React | Destination | Migration notes |
| - | - | - |
| `open` | `open` on `details`, or the conditional-rendering predicate | Use controlled state only when other app behavior needs it. |
| `id` | Remove for native disclosure, or retain for app logic | `summary` already controls its containing `details`. |
| `transition` | Remove | Don't reproduce animation timing before semantics and state are correct. |
| `expandOnPrint` | Print-specific app styles only when the content must print | Verify the actual printed workflow. |
| `children` | Disclosure content or conditionally rendered content | Don'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.

***

## Test the migration

* 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`.

***

## Remove 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.

***

## Migration example

## Migrating Collapsible

##### Polaris web components

```tsx
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>
  );
}
```

##### Polaris React

```tsx
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>
    </>
  );
}
```

***

## Related guidance

* [Box component](https://shopify.dev/docs/api/app-home/web-components/layout-and-structure/box)
* [Migrate Button from Polaris React](https://shopify.dev/docs/apps/build/app-home/migrate-from-polaris-react/button)

***
