DocsIntegrating PuckMulti-column Layouts

Multi-column Layouts

Multi-column layouts can be achieved with two Puck APIs:

  1. The <DropZone> component, enabling you to nest components
  2. The inline parameter, enable multi-directional drag-and-drop

Nesting components with DropZones

Puck provides support for nesting components via the <DropZone> API.

The <DropZone> component can be rendered anywhere within your component, creating a zone that you can drop components into.

import { DropZone } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      render: () => {
        return (
          <div>
            <DropZone zone="my-content" />
          </div>
        );
      },
    },
    Card: {
      render: () => <div>Hello, world</div>,
    },
  },
};
Interactive Demo
Nested components example

The zone identifier is unique to the component, and can be reused in different components.

You can combine multiple DropZones to achieve fixed layouts:

import { DropZone } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      render: () => {
        return (
          <div
            style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}
          >
            <DropZone zone="left-column" />
            <DropZone zone="right-column" />
          </div>
        );
      },
    },
    Card: {
      render: () => <div>Hello, world</div>,
    },
  },
};
Interactive Demo
Multi-column DropZone example

Advanced CSS layouts

By default, Puck will wrap your components in a div element for a vertical block layout, restricting drag-and-drop to the y-axis.

The inline component parameter allows you to eliminate the wrapping element. Combining this with DropZones enables advanced CSS layouts with multi-directional drag-and-drop.

Basic CSS grid example

For example, set inline: true to implement CSS grid layouts. When using inline mode, you must specify which element is draggable by passing the puck.dragRef prop to your element’s ref prop.

import { DropZone } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      render: () => (
        <DropZone
          zone="my-content"
          style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }} // Use CSS grid in this DropZone
        />
      ),
    },
    Card: {
      inline: true, // Enable inline mode, removing the Puck wrapper
      render: ({ text, puck }) => (
        <div
          ref={puck.dragRef} // Let Puck know this element is draggable
        >
          {text}
        </div>
      ),
    },
  },
};
Interactive Demo
CSS grid example

Advanced CSS grid example

You can create more sophisticated layouts, too. Here’s an advanced CSS grid example, with items spanning multiple rows and columns.

import { DropZone } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      render: () => (
        <DropZone
          zone="my-content"
          style={{
            display: "grid",
            gridTemplateColumns: "1fr 1fr 1fr 1fr",
            gridTemplateRows: "1fr 1fr 1fr 1fr",
            gap: 16,
          }}
        />
      ),
    },
    Card: {
      inline: true, // Enable inline mode, removing the Puck wrapper
      render: ({ text, puck }) => (
        <div
          ref={puck.dragRef} // Let Puck know this element is draggable
          style={{
            gridColumn: `span ${spanCol}`,
            gridRow: `span ${spanRow}`,
          }}
        >
          {text}
        </div>
      ),
    },
  },
};
Interactive Demo
Advanced grid example

Restricting components

The allow and disallow DropZone props allow you to restrict which components can be dragged into a DropZone.

import { DropZone } from "@measured/puck";
 
const config = {
  components: {
    Example: {
      render: () => {
        return (
          <div>
            <DropZone zone="my-content" allow={["Card"]} />
          </div>
        );
      },
    },
  },
};

This can be combined with categories to restrict based on your existing groups:

import { DropZone } from "@measured/puck";
 
const config = {
  categories: {
    typography: {
      components: ["Card"],
    },
  },
  components: {
    Example: {
      render: () => {
        return (
          <div>
            <DropZone
              zone="my-content"
              allow={categories.typography.components}
            />
          </div>
        );
      },
    },
  },
};

Further reading