LayoutComposer

The LayoutComposer component provides a flexible layout composition system for building admin pages with widget support. It manages the rendering of widgets in specific sections and supports different layout structures.

The LayoutComposer is useful for plugins that want to create custom admin pages with a consistent layout and custom widget injection zones.

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

Basic Example#

In the following example, the LayoutComposer is used to create a two-column layout for a custom brand details page:

Code
1import { LayoutComposer } from "@medusajs/dashboard/components"2
3const BrandDetailsPage = () => {4  // retrieve brand...5  return (6    <LayoutComposer7      widgetsZonePrefix="brand.details"8      preferredLayoutId="core:two-column"9      data={brand}10      sections={{11        main: (12          <>13            <BrandGeneralSection brand={brand} />14            <BrandVariantsSection brand={brand} />15          </>16        ),17        side: (18          <>19            <BrandMediaSection brand={brand} />20            <BrandStatusSection brand={brand} />21          </>22        ),23      }}24    />25  )26}

The widgetsZonePrefix prop determines the widget injection zones for the page. In this example, the UI route exposes the following zones:

  • brand.details: Widgets rendered in the main section of the brand details page.
  • brand.details.side: Widgets rendered in the side section of the brand details page.

Single Column Layout#

The LayoutComposer also supports single-column layouts, which is useful for listing pages or pages with a single main content area. For example:

Code
1import { LayoutComposer } from "@medusajs/dashboard/components"2
3const BrandListPage = () => {4  return (5    <LayoutComposer6      widgetsZonePrefix="brand.list"7      preferredLayoutId="core:single-column"8      sections={{9        main: (10          <BrandListSection />11        ),12      }}13    />14  )15}

The widgetsZonePrefix prop determines the widget injection zones for the page. In this example, the UI route exposes the following zone:

  • brand.list: Widgets rendered in the main section of the brand list page.

Props#

Prop

Type

Description

widgetsZonePrefix

string

The prefix used to determine widget injection zones for the page. For a two-column layout, the component exposes {prefix} (main section) and {prefix}.side (side section). For a single-column layout, it exposes {prefix} only.

preferredLayoutId

string

The ID of the preferred layout to use. By default, accepted values are "core:single-column", "core:single-row", "core:two-column", and "core:settings-sidebar".

sections

Record<string, ReactNode>

Object mapping section names to their content. The key is the name of the section, and the value is a ReactNode. Available sections depend on the chosen layout:

  • core:single-column: main
  • core:single-row: main
  • core:two-column: main, side
  • core:settings-sidebar: general, developer, myAccount, extensions

data

unknown (optional)

Data passed from the UI route to the LayoutComposer. This data is passed to widgets injected into the zones, allowing them to access the page's main data.

Custom Layouts#

You can create custom layouts by adding a layout file under src/admin/layouts/ in your plugin. A layout file must have a default export (the React component) and a named config export created with defineLayoutConfig.

For example, create the file src/admin/layouts/three-column.tsx with the following content:

src/admin/layouts/three-column.tsx
1import { defineLayoutConfig } from "@medusajs/admin-sdk"2import type { LayoutComponentProps } from "@medusajs/dashboard/components"3
4const ThreeColumnLayout = ({ sections }: LayoutComponentProps) => (5  <div className="grid grid-cols-3 gap-4">6    <div>{sections.main}</div>7    <div>{sections.side}</div>8    <div>{sections.extra}</div>9  </div>10)11
12export const config = defineLayoutConfig({13  id: "my-plugin:three-column",14  sections: [15    { id: "main", ordering: "list" },16    { id: "side", ordering: "list" },17    { id: "extra", ordering: "list" },18  ],19})20
21export default ThreeColumnLayout

To get type-safe section names when using your custom layout, augment the LayoutSectionRegistry interface in @medusajs/admin-shared:

index.d.ts
1declare module "@medusajs/admin-shared" {2  interface LayoutSectionRegistry {3    "my-plugin:three-column": "main" | "side" | "extra"4  }5}

You can then use the custom layout in the LayoutComposer:

Code
1import { LayoutComposer } from "@medusajs/dashboard/components"2
3const BrandPage = () => {4  return (5    <LayoutComposer6      widgetsZonePrefix="brand.list"7      preferredLayoutId="my-plugin:three-column"8      sections={{9        main: <BrandListSection />,10        side: <BrandFiltersSection />,11        extra: <BrandStatsSection />,12      }}13    />14  )15}
Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa 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