LayoutComposer - Admin Components

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
import { LayoutComposer } from "@medusajs/dashboard/components"

const BrandDetailsPage = () => {
  // retrieve brand...
  return (
    <LayoutComposer
      widgetsZonePrefix="brand.details"
      preferredLayoutId="core:two-column"
      data={brand}
      sections={{
        main: (
          <>
            <BrandGeneralSection brand={brand} />
            <BrandVariantsSection brand={brand} />
          </>
        ),
        side: (
          <>
            <BrandMediaSection brand={brand} />
            <BrandStatusSection brand={brand} />
          </>
        ),
      }}
    />
  )
}

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
import { LayoutComposer } from "@medusajs/dashboard/components"

const BrandListPage = () => {
  return (
    <LayoutComposer
      widgetsZonePrefix="brand.list"
      preferredLayoutId="core:single-column"
      sections={{
        main: (
          <BrandListSection />
        ),
      }}
    />
  )
}

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#

widgetsZonePrefixstring
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.
preferredLayoutIdstring
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".
sectionsobject
The content of each section of the layout. The key is the section's name, and the value is a ReactNode. The available sections depend on the value of preferredLayoutId.
dataunknownOptional
Data passed from the UI route to the LayoutComposer. The component passes this data to the widgets injected into the zones, allowing them to access the page's main data.
hasOutletbooleanOptional
Whether to render an outlet after the layout, which nested routes use to show modals such as drawers and dialogs.

Default: true

disableWidgetsbooleanOptional
Whether to disable the widget injection zones of this layout.

Default: false


LayoutComposer.Entry#

Merchants can reorder the sections of a page, and Medusa saves their preference using each entry's component name. Wrap a section in LayoutComposer.Entry with a stable id so that the preference survives renaming or minifying the component.

Code
import { LayoutComposer } from "@medusajs/dashboard/components"

const BrandDetailsPage = () => {
  return (
    <LayoutComposer
      widgetsZonePrefix="brand.details"
      preferredLayoutId="core:two-column"
      sections={{
        main: (
          <>
            <LayoutComposer.Entry id="brand-general">
              <BrandGeneralSection brand={brand} />
            </LayoutComposer.Entry>
            <LayoutComposer.Entry id="brand-variants">
              <BrandVariantsSection brand={brand} />
            </LayoutComposer.Entry>
          </>
        ),
      }}
    />
  )
}

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
import { defineLayoutConfig } from "@medusajs/admin-sdk"
import type {
  LayoutComponentProps,
} from "@medusajs/dashboard/components"

const ThreeColumnLayout = ({
  sections,
}: LayoutComponentProps) => (
  <div className="grid grid-cols-3 gap-4">
    <div>{sections.main}</div>
    <div>{sections.side}</div>
    <div>{sections.extra}</div>
  </div>
)

export const config = defineLayoutConfig({
  id: "my-plugin:three-column",
  sections: [
    { id: "main", ordering: "list" },
    { id: "side", ordering: "list" },
    { id: "extra", ordering: "list" },
  ],
})

export default ThreeColumnLayout

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

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

You can then use the custom layout in the LayoutComposer:

Code
import { LayoutComposer } from "@medusajs/dashboard/components"

const BrandPage = () => {
  return (
    <LayoutComposer
      widgetsZonePrefix="brand.list"
      preferredLayoutId="my-plugin:three-column"
      sections={{
        main: <BrandListSection />,
        side: <BrandFiltersSection />,
        extra: <BrandStatsSection />,
      }}
    />
  )
}
Found a problem with this page?Report Issue
Edited Nov 14·Edit this page