--- title: Sheet description: >- The Sheet component displays essential information for customers at the bottom of the screen, appearing above other elements. Use it sparingly to avoid distracting customers. api_version: 2025-07 api_name: customer-account-ui-extensions source_url: html: >- https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/ui-components/overlays/sheet md: >- https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/ui-components/overlays/sheet.md --- Migrate to Polaris Version 2025-07 is the last API version to support React-based UI components. Later versions use [web components](https://shopify.dev/docs/api/customer-account-ui-extensions/latest/polaris-web-components), native UI elements with built-in accessibility, better performance, and consistent styling with [Shopify's design system](https://shopify.dev/docs/apps/design). Check out the [migration guide](https://shopify.dev/docs/apps/build/customer-accounts/migrate-to-web-components) to upgrade your extension. # Sheet **Requires configuration of the \[\`collect\_buyer\_consent\`]\(/docs/api/customer-account-ui-extensions/2025-07#configuration) capability to be rendered.:** The Sheet component displays essential information for customers at the bottom of the screen, appearing above other elements. Use it sparingly to avoid distracting customers. The library automatically applies the [WAI-ARIA Dialog pattern](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) to both the activator and the sheet content. ### Support Targets (25) ### Supported targets * Customer​Account::Kitchen​Sink * [customer-account.​footer.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/footer#footer-render-after-) * [customer-account.​order-index.​announcement.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-index#order-index-targets) * [customer-account.​order-index.​block.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-index#order-index-block-) * [customer-account.​order-status.​announcement.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-status#order-status-announcement-) * [customer-account.​order-status.​block.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-status#order-status-block-) * [customer-account.​order-status.​cart-line-item.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-status#cart-line-item-render-after-) * [customer-account.​order-status.​cart-line-list.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-status#cart-line-list-render-after-) * [customer-account.​order-status.​customer-information.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-status#customer-information-render-after-) * [customer-account.​order-status.​fulfillment-details.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/fulfillment-status#fulfillment-status-targets) * [customer-account.​order-status.​payment-details.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/payments-and-returns#payments-and-returns-targets) * [customer-account.​order-status.​return-details.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/payments-and-returns#return-details-render-after-) * [customer-account.​order-status.​unfulfilled-items.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/fulfillment-status#unfulfilled-items-render-after-) * [customer-account.​order.​action.​menu-item.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-actions#order-action-menu-item-) * [customer-account.​order.​action.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/order-actions#order-action-) * [customer-account.​order.​page.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/full-page#order-specific-full-page-) * [customer-account.​page.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/full-page#customer-account-full-page-) * [customer-account.​profile.​addresses.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/profile-page-default#profile-page-default-targets-) * [customer-account.​profile.​announcement.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/profile-page-default#announcement-) * [customer-account.​profile.​block.​render](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/profile-page-default#profile-block-) * [customer-account.​profile.​company-details.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/profile-page-b2b#profile-page-b2b-targets-) * [customer-account.​profile.​company-location-addresses.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/profile-page-b2b#company-location-addresses-render-after-) * [customer-account.​profile.​company-location-payment.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/profile-page-b2b#company-location-payment-render-after-) * [customer-account.​profile.​company-location-staff.​render-after](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07/targets/profile-page-b2b#company-location-staff-render-after-) * customer-account.​profile.​payment.​render-after #### Use cases * **Privacy consent:** Collect customer consent for data collection, cookies, or tracking in compliance with privacy regulations. * **Cookie preferences:** Let customers manage their cookie preferences with granular controls for different tracking categories. * **Terms acceptance:** Present terms of service or policy updates that require customer acknowledgment before proceeding. * **Essential notices:** Display critical information that requires customer attention, such as regional compliance notices. *** ## Properties Configure the following properties on the Sheet component. * **accessibilityLabel** **string** A label that describes the purpose of the sheet, announced by screen readers. If not set, it will use the value of `heading`. * **defaultOpen** **boolean** Whether the sheet should be open when it first renders. Use sparingly — only when the user must interact with the sheet before proceeding (for example, a privacy consent prompt). Only takes effect on the initial render. * **heading** **string** A heading rendered at the top of the sheet. * **id** **string** A unique identifier for the component. Use this to target the component in scripts or stylesheets, or to distinguish it from other instances of the same component. * **onHide** **() => void** A callback fired when the sheet is closed. * **onShow** **() => void** A callback fired when the sheet is opened. * **primaryAction** **RemoteFragment** The primary action to perform, provided as a button component. Up to two buttons can be rendered. * **secondaryAction** **RemoteFragment** The secondary action to perform, provided as a button component. Only one button can be rendered. *** ## Examples ### Collect consent preferences Use the sheet to collect customer consent for cookies and tracking. This example displays a consent prompt with accept and decline buttons, and a secondary action that opens preferences. ## Collect consent preferences ##### React ```tsx import { reactExtension, Button, Link, Sheet, TextBlock, useApi, useCustomerPrivacy, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.footer.render-after', () => , ); function Extension() { const {applyTrackingConsentChange, ui} = useApi(); const {shouldShowBanner} = useCustomerPrivacy(); const sheetId = 'sheet-consent'; const handleConsentChange = async ({ analytics, marketing, preferences, saleOfData, }) => { try { const result = await applyTrackingConsentChange({ type: 'changeVisitorConsent', analytics, marketing, preferences, saleOfData, }); // Check if operation was successful if (result.type === 'success') { ui.overlay.close(sheetId); } else { // Handle failure case here } } catch (error) { // Handle error case here } }; return ( } secondaryAction={ } > This website uses cookies to ensure you get the best experience on our website. Privacy Policy ); } ``` ##### JS ```js import { extension, Sheet, Button, Link, TextBlock, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.footer.render-after', ( root, { applyTrackingConsentChange, customerPrivacy, ui, }, ) => { customerPrivacy.subscribe( ({shouldShowBanner}) => { const primaryFragment = root.createFragment(); const secondaryFragment = root.createFragment(); const handleConsentChange = async ({ analytics, marketing, preferences, saleOfData, }) => { try { const result = await applyTrackingConsentChange({ type: 'changeVisitorConsent', analytics, marketing, preferences, saleOfData, }); // Check if operation was successful if (result) { ui.overlay.close(sheetId); } else { // Handle failure case here } } catch (error) { // Handle error case here } }; const declineButton = root.createComponent( Button, { kind: 'secondary', onPress: () => handleConsentChange({ analytics: false, marketing: false, preferences: false, saleOfData: false, }), }, 'I decline', ); const agreeButton = root.createComponent( Button, { kind: 'secondary', onPress: () => handleConsentChange({ analytics: true, marketing: true, preferences: true, saleOfData: true, }), }, 'I agree', ); const settingsButton = root.createComponent( Button, { kind: 'secondary', }, 'Settings', ); primaryFragment.appendChild( declineButton, ); primaryFragment.appendChild(agreeButton); secondaryFragment.appendChild( settingsButton, ); const sheetId = 'sheet-consent'; const sheet = root.createComponent( Sheet, { id: sheetId, heading: 'We value your privacy', accessibilityLabel: 'A sheet that collects privacy consent preferences', defaultOpen: shouldShowBanner, primaryAction: primaryFragment, secondaryAction: secondaryFragment, }, ); const textBlock = root.createComponent( TextBlock, null, [ 'We and our partners use cookies and other technologies to improve your experience, measure performance, and tailor marketing. Details in our ', root.createComponent( Link, null, 'Privacy Policy', ), ], ); sheet.appendChild(textBlock); root.appendChild(sheet); }, ); }, ); ``` ### Link preferences in the description Save space in the action slot by placing secondary actions in the content area. This example shows privacy and cookie policy links alongside a preferences link in the sheet body. ## Link preferences in the description ![A sheet with privacy policy, cookie policy, and preferences links in the content area.](https://shopify.dev/assets/assets/images/templated-apis-screenshots/checkout-ui-extensions/2025-07/sheet-description-preferences-BIGz6p6u.png) ## Link preferences in the description ##### React ```tsx import { reactExtension, Button, BlockStack, Link, Sheet, TextBlock, useCustomerPrivacy, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.footer.render-after', () => , ); function Extension() { const {shouldShowBanner} = useCustomerPrivacy(); const sheetId = 'sheet-consent'; return ( } > This website uses cookies to ensure you get the best experience on our website. Privacy Policy ‧{' '} Cookie Policy ‧{' '} Preferences modal..., > Preferences ); } ``` ##### JS ```js import { extension, Sheet, Button, Link, TextBlock, BlockStack, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.footer.render-after', (root, {customerPrivacy}) => { const primaryFragment = root.createFragment(); const declineButton = root.createComponent( Button, { kind: 'secondary', onPress: () => {}, }, 'I decline', ); const agreeButton = root.createComponent( Button, { kind: 'secondary', onPress: () => {}, }, 'I agree', ); primaryFragment.appendChild(declineButton); primaryFragment.appendChild(agreeButton); const sheetId = 'sheet-consent'; const sheet = root.createComponent(Sheet, { id: sheetId, accessibilityLabel: 'A sheet that collects privacy consent preferences', defaultOpen: customerPrivacy.current.shouldShowBanner, primaryAction: primaryFragment, }); const textBlock = root.createComponent( TextBlock, null, 'This website uses cookies to ensure you get the best experience on our website.', ); const linkBlock = root.createComponent( TextBlock, null, [ root.createComponent( Link, null, 'Privacy Policy', ), ' ‧ ', root.createComponent( Link, null, 'Cookie Policy', ), ' ‧ ', root.createComponent( Link, { // overlay: Preferences modal...,, }, 'Preferences', ), ], ); const blockStack = root.createComponent( BlockStack, {spacing: 'none'}, [textBlock, linkBlock], ); sheet.appendChild(blockStack); root.appendChild(sheet); }, ); ``` ### Use an icon button for preferences Use an icon button in the secondary actions area to allow more space for the primary actions. ## Use an icon button for preferences ![A sheet with an icon button in the secondary actions area.](https://shopify.dev/assets/assets/images/templated-apis-screenshots/checkout-ui-extensions/2025-07/sheet-icon-button-preferences-Dkgm2RBy.png) ## Use an icon button for preferences ##### React ```tsx import { reactExtension, Button, Link, Icon, Sheet, TextBlock, useCustomerPrivacy, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.footer.render-after', () => , ); function Extension() { const {shouldShowBanner} = useCustomerPrivacy(); return ( } secondaryAction={ } > This website uses cookies to ensure you get the best experience on our website.{' '} Privacy Policy. ); } ``` ##### JS ```js import { extension, Sheet, Button, Link, TextBlock, Icon, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.footer.render-after', (root, {customerPrivacy}) => { const primaryFragment = root.createFragment(); const secondaryFragment = root.createFragment(); const declineButton = root.createComponent( Button, { kind: 'secondary', onPress: () => {}, }, 'I decline', ); const agreeButton = root.createComponent( Button, { kind: 'secondary', onPress: () => {}, }, 'I agree', ); const preferencesButton = root.createComponent( Button, { kind: 'secondary', }, root.createComponent(Icon, { source: 'settings', }), ); primaryFragment.appendChild(declineButton); primaryFragment.appendChild(agreeButton); secondaryFragment.appendChild( preferencesButton, ); const sheet = root.createComponent(Sheet, { accessibilityLabel: 'A sheet that collects privacy consent preferences', defaultOpen: customerPrivacy.current.shouldShowBanner, primaryAction: primaryFragment, secondaryAction: secondaryFragment, }); const textBlock = root.createComponent( TextBlock, null, [ 'This website uses cookies to ensure you get the best experience on our website. ', root.createComponent( Link, null, 'Privacy Policy', ), ], ); sheet.appendChild(textBlock); root.appendChild(sheet); }, ); ``` ### Structure content with layout components Use layout components in the sheet's content area to structure different types of content in specific ways. ## Structure content with layout components ![A sheet using layout components to structure content.](https://shopify.dev/assets/assets/images/templated-apis-screenshots/checkout-ui-extensions/2025-07/sheet-layout-content-MpjSN0s-.png) ## Structure content with layout components ##### React ```tsx import { reactExtension, Button, Link, Image, InlineLayout, Sheet, TextBlock, useCustomerPrivacy, } from '@shopify/ui-extensions-react/checkout'; export default reactExtension( 'purchase.checkout.footer.render-after', () => , ); function Extension() { const {shouldShowBanner} = useCustomerPrivacy(); return ( } secondaryAction={ } > This website uses cookies to ensure you get the best experience on our website.{' '} Learn more. ); } ``` ##### JS ```js import { extension, Sheet, Button, Link, TextBlock, Image, InlineLayout, } from '@shopify/ui-extensions/checkout'; export default extension( 'purchase.checkout.footer.render-after', (root, {customerPrivacy}) => { const primaryFragment = root.createFragment(); const secondaryFragment = root.createFragment(); const declineButton = root.createComponent( Button, { kind: 'secondary', onPress: () => {}, }, 'I decline', ); const agreeButton = root.createComponent( Button, { kind: 'secondary', onPress: () => {}, }, 'I agree', ); const preferencesButton = root.createComponent( Button, { kind: 'plain', }, 'Preferences', ); primaryFragment.appendChild(declineButton); primaryFragment.appendChild(agreeButton); secondaryFragment.appendChild( preferencesButton, ); const sheet = root.createComponent(Sheet, { accessibilityLabel: 'A sheet that collects privacy consent preferences', defaultOpen: customerPrivacy.current.shouldShowBanner, primaryAction: primaryFragment, secondaryAction: secondaryFragment, }); const textBlock = root.createComponent( TextBlock, null, [ 'This website uses cookies to ensure you get the best experience on our website.', root.createComponent( Link, null, 'Learn more', ), ], ); const inlineLayout = root.createComponent( InlineLayout, { padding: 'none', spacing: 'small100', columns: [38, 'fill'], }, [ root.createComponent(Image, { source: 'https://yourawesomeimage.com', }), textBlock, ], ); sheet.appendChild(inlineLayout); root.appendChild(sheet); }, ); ``` *** ## Best practices * **Limit to one sheet at a time:** Don't stack multiple sheets. If the workflow requires more than one decision, combine them or use a modal for the additional step. * **Reserve for content that requires acknowledgment:** Sheets work best when customers need to actively respond before continuing, not for passive informational content. * **Include clear actions:** Provide clear primary and secondary actions so customers understand their choices and can respond quickly. The primary section supports up to two buttons, and the secondary section supports one button. Keep button labels brief so they don't wrap to more than one line. * **Keep content concise:** Use short content, small text sizes, or remove the header to avoid scrolling. When content exceeds the maximum height, heading and content become scrollable while the actions slot and dismiss button remain fixed. * **Provide an accessibility label:** Use `accessibilityLabel` when the sheet heading alone doesn't fully describe the purpose for screen readers. * **Handle all consent outcomes:** When implementing consent flows, handle acceptance (cookies load, sheet doesn't re-appear), denial (cookies don't load, sheet doesn't re-appear), and dismissal (cookies don't load, sheet re-appears on refresh). *** ## Limitations * The sheet requires configuration of the [`collect_buyer_consent`](https://shopify.dev/docs/api/customer-account-ui-extensions/2025-07#configuration) capability to be rendered. Without it, the component won't appear. * Sheets always appear at the bottom of the screen and can't be repositioned. Element positions, padding, spacing, and maximum height are controlled by Shopify. * The component is intended for consent-related use cases and may not render in other contexts. ***