Docs
Extending Puck
Custom Interfaces

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>
  );
}
Interactive Demo
HeadingBlock
HeadingBlock
Hello, world

Compositional components

Puck exposes its core components, allowing you to compose them together to create new layouts:

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 }}
            />
          ),
        },
      }}
    />
  );
}

Further reading