Custom Interfaces
Puck uses compositional patterns and UI overrides to enable completely custom editor interfaces.
See a custom interface example (opens in a new tab)
Composition
Custom interfaces can be implementing by providing children
to the <Puck>
component:
import { Puck } from "@measured/puck";
export function Editor() {
return (
<Puck>
<div
style={{ display: "grid", gridTemplateColumns: "1fr 2fr", gridGap: 16 }}
>
<div>
{/* Render the drag-and-drop preview */}
<Puck.Preview />
</div>
<div>
{/* Render the component list */}
<Puck.Components />
</div>
</div>
</Puck>
);
}
Compositional components
Puck exposes its core components, allowing you to compose them together to create new layouts:
<Puck.Components>
- A draggable list of components.<Puck.Fields>
- The fields for the currently selected item.<Puck.Outline>
- An interactive outline.<Puck.Preview>
- A drag-and-drop preview.
The internal UI for these components can be changed by implementing UI overrides or theming.
Helper components
Puck also exposes helper components for even deeper customization:
<Drawer>
- A reference list of items that can be dragged into a droppable area, normally<Puck.Preview>
.<Drawer.Item>
- An item that can be dragged from a<Drawer>
.<FieldLabel>
- A styled label for creating inputs.
Custom components
Access the Puck AppState
via the usePuck
hook to integrate with Puck with custom editor components:
import { Puck, usePuck } from "@measured/puck";
const JSONRenderer = () => {
const { appState } = usePuck();
return <div>{JSON.stringify(appState.data)}</div>;
};
export function Editor() {
return (
<Puck>
<JSONRenderer />
</Puck>
);
}
UI Overrides
UI overrides allow you to change how Puck renders. It can be used with or without compositional components.
Use the overrides
prop to implement an override:
import { Puck } from "@measured/puck";
export function Editor() {
return (
<Puck
// ...
overrides={{
// Render a custom element for each item in the component list
componentItem: ({ name }) => (
<div style={{ backgroundColor: "hotpink" }}>{name}</div>
),
}}
/>
);
}
There are many different overrides available. See the overrides
API reference for the full list.
Field type override example
Field type overrides deserve a special mention as they showcase the power of this API.
Using overrides
, it's possible to provide a custom implementation of all fields of a certain type by specifying the fieldTypes
.
import { Puck } from "@measured/puck";
export function Editor() {
return (
<Puck
// ...
overrides={{
fieldTypes: {
// Override all text fields with a custom input
text: ({ name, onChange, value }) => (
<input
defaultValue={value}
name={name}
onChange={(e) => onChange(e.currentTarget.value)}
style={{ border: "1px solid black", padding: 4 }}
/>
),
},
}}
/>
);
}