StackedFocusModal - Admin Components

Note: This component is available since Medusa v2.21.0.

The StackedFocusModal component renders a full-screen modal above a route modal. The Medusa Admin uses it for selections that need a lot of space, such as the table that adds countries to a region.

Code
1import {2  StackedFocusModal,3} from "@medusajs/dashboard/components"

Stacked Modals#

Render a StackedFocusModal inside a RouteFocusModal or a RouteDrawer, which both provide the stacked modal context. Unlike route modals, a StackedFocusModal is not tied to a route. The user opens it with a trigger, and the parent modal stays mounted underneath.

Every StackedFocusModal requires an id prop. The parent modal uses the ID to track which stacked modal is open, so you can render more than one stacked modal in the same form.

Use a StackedFocusModal when the content needs the full screen, such as a data table. For narrower content, use the StackedDrawer.


Example#

The following UI route renders a StackedFocusModal inside a RouteFocusModal to select products:

src/admin/routes/brands/[id]/products/page.tsx
1import { Button, Heading } from "@medusajs/ui"2import {3  RouteFocusModal,4  StackedFocusModal,5} from "@medusajs/dashboard/components"6
7const STACKED_MODAL_ID = "select-products"8
9const BrandProducts = () => {10  return (11    <RouteFocusModal>12      <RouteFocusModal.Header />13      <RouteFocusModal.Body>14        <StackedFocusModal id={STACKED_MODAL_ID}>15          <StackedFocusModal.Trigger asChild>16            <Button variant="secondary" size="small">17              Add Products18            </Button>19          </StackedFocusModal.Trigger>20          <StackedFocusModal.Content>21            <StackedFocusModal.Header>22              <StackedFocusModal.Title asChild>23                <Heading>Add Products</Heading>24              </StackedFocusModal.Title>25            </StackedFocusModal.Header>26            <StackedFocusModal.Body>27              <ProductTable />28            </StackedFocusModal.Body>29            <StackedFocusModal.Footer>30              <StackedFocusModal.Close asChild>31                <Button variant="secondary" size="small">32                  Cancel33                </Button>34              </StackedFocusModal.Close>35            </StackedFocusModal.Footer>36          </StackedFocusModal.Content>37        </StackedFocusModal>38      </RouteFocusModal.Body>39    </RouteFocusModal>40  )41}42
43export default BrandProducts

To close the modal from your own code, such as after the user confirms the selection, use the useStackedModal hook. Refer to the Stacked Drawer documentation for the hook's returned values.


Props#

Loading...

Sub-components#

Component

Description

StackedFocusModal.Trigger

Opens the modal when clicked. Pass asChild to use your own button.

StackedFocusModal.Content

The modal's content. It renders a transparent overlay so that the parent modal stays visible.

StackedFocusModal.Header

The modal's header. It renders the close button, so render it even when it has no children.

StackedFocusModal.Title

The modal's title. Pass asChild to render your own heading element.

StackedFocusModal.Description

An accessible description of the modal's purpose.

StackedFocusModal.Body

The modal's scrollable content area.

StackedFocusModal.Footer

The modal's footer. Place the cancel and confirm buttons in it.

StackedFocusModal.Close

Closes the modal when clicked, keeping the parent modal open. Pass asChild to use your own button.


useStackedModal#

StackedFocusModal shares the useStackedModal hook with the StackedDrawer. The hook returns getIsOpen and setIsOpen to read and change a stacked modal's open state by its ID, and register and unregister that the components call for you.

src/admin/routes/brands/[id]/products/page.tsx
1import { Button } from "@medusajs/ui"2import { useStackedModal } from "@medusajs/dashboard/components"3
4const ProductTable = () => {5  const { setIsOpen } = useStackedModal()6
7  const handleSave = () => {8    setIsOpen("select-products", false)9  }10
11  return (12    <Button size="small" onClick={handleSave}>13      Save14    </Button>15  )16}
Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Docs MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break
⇧↵