Multi-column Layouts
Puck supports nested and multi-column layouts across any CSS layout using the <DropZone>
component.
Nested components
Add the <DropZone>
component to your render function to create 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>,
},
},
};
You can also use DropZones in the root
render function to change the default layout. See Root DropZones
Fixed layouts
Combine multiple DropZones to achieve fixed layouts. By default, components inside a DropZone are arranged along the vertical (block
) axis.
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: ({ text }) => <div>{text}</div>,
},
},
};
Fluid layouts
Apply the CSS display property to a DropZone via the style
or className
props to arrange your components in different layouts. Puck supports drag-and-drop for all display
values, including grid
and flex
.
import { DropZone } from "@measured/puck";
const config = {
components: {
Example: {
render: () => (
<DropZone
zone="my-content"
// Use CSS grid in this DropZone
style={{ display: "grid", gridTemplateColumns: "2fr 1fr", gap: 16 }}
/>
),
},
Card: {
render: ({ text }) => <div>{text}</div>,
},
},
};
Removing the wrapper
By default, Puck will wrap your components in a div
element. For some layouts, you may need to eliminate the wrapping element and treat the child component as a direct descendant of its’ parent DropZone.
For example, this is required if you wish to use CSS rules like flex-grow
, grid-column
, or grid-row
.
Use the inline
component parameter to remove the wrapping element. When using this API, you must also 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 1fr 1fr",
gridTemplateRows: "1fr 1fr 1fr 1fr",
gap: 16,
}}
/>
),
},
Card: {
inline: true, // Enable inline mode, removing the Puck wrapper
render: ({ text, spanCol, spanRow, puck }) => (
<div
ref={puck.dragRef} // Let Puck know this element is draggable
style={{
gridColumn: `span ${spanCol}`,
gridRow: `span ${spanRow}`,
}}
>
{text}
</div>
),
},
},
};
Restricting components
Use the allow
and disallow
DropZone props 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>
);
},
},
},
};
Combine this with categories to restrict behavior 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>
);
},
},
},
};
Root DropZones
The root
uses a DropZone internally. When providing a render function to root
, the children
prop renders a DropZone for a zone called default-zone.
const config = {
root: {
render: ({ children }) => {
// children renders <DropZone zone="default-zone" />
return <div>{children}</div>;
},
},
};
Instead of rendering children, use DropZone to achieve multi-column layouts at the root:
const config = {
root: {
render: () => (
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 16 }}>
<DropZone zone="left-column" />
<DropZone zone="right-column" />
</div>
),
},
};
When passing default-zone to a DropZone in the root render function, the component data will be stored under content
. Otherwise, the component data will be stored under zones