Docs
Integrating Puck
Component Configuration

Component Configuration

Puck's core behaviour is configured via the Config. This describes:

  • which components are available to Puck
  • how to render each component
  • which fields to show when the user selects a component
  • additional information, like category grouping

The Config is provided via the config prop to the main Puck components:

  • <Puck> reads the Config and renders an editor UI. The user interacts with the editor to produce a data payload.
  • <Render> walks a data payload and renders it according to the provided Config.

The render function

Components can be defined by via the components object in Config. Every definition must provide a render function:

const config = {
  components: {
    HeadingBlock: {
      render: () => {
        return <h1>Hello, world</h1>;
      },
    },
  },
};

This tells Puck that HeadingBlock is a valid component, and describes how to render it.

When the user drags the component onto the preview and hits Publish in the editor UI via the <Puck> component, this Config will produce a data payload like this:

{
  "content": [
    {
      "type": "HeadingBlock",
      "props": {
        "id": "HeadingBlock-1234"
      }
    }
  ],
  "root": {}
}

The data payload and Config together tell <Render> how to render the page. It can also be provided to <Puck> as an initial data payload.

Interactive Demo
Try interacting with the heading
Hello, world

TypeScript

If you're using TypeScript, we recommend strictly typing your config:

import type { Config } from "@measured/puck";
 
type Components = {
  HeadingBlock: {};
};
 
const config: Config<Components> = {
  components: {
    HeadingBlock: {
      render: () => {
        return <h1>Hello, world</h1>;
      },
    },
  },
};

Adding fields

Fields allow users to provide input to components. The value of each field is passed in as a prop to the render function.

You can define a field via the fields parameter:

const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      render: ({ title }) => {
        return <h1>{title}</h1>;
      },
    },
  },
};

This will render a Text field when the user selects an instance of the HeadingBlock component in the editor UI.

Interactive Demo
Text field example

When the user modifies the input, the editor will produce a data payload like this:

{
  "content": [
    {
      "type": "HeadingBlock",
      "props": {
        "id": "HeadingBlock-1234",
        "title": "Hello, world"
      }
    }
  ],
  "root": {}
}

TypeScript

It's best to define the props for the component if using TypeScript. This enables strict type checking for your fields.

import type { Config } from "@measured/puck";
 
type Components = {
  HeadingBlock: {
    title: string;
  };
};
 
const config: Config<Components> = {
  // ...
};

Setting default props

Default props allow you to set an initial value for a prop when a new component is added.

Provide an object to the defaultProps parameter to configure this:

const config = {
  components: {
    HeadingBlock: {
      fields: {
        title: {
          type: "text",
        },
      },
      defaultProps: {
        title: "Hello, world",
      },
      render: ({ title }) => {
        return <h1>{title}</h1>;
      },
    },
  },
};

Unlike default parameters (opens in a new tab), defaultProps are stored in the data payload and will populate the Puck fields.

Interactive Demo
Text field example
Hello, world