DEV Community

Cover image for Puck 0.13: Custom UIs, object fields & DropZone restrictions
Chris Villa for Measured

Posted on • Originally published at measured.co

Puck 0.13: Custom UIs, object fields & DropZone restrictions

Puck is the open-source visual editor for React built by Measured - a self-hosted alternative to builder.io, WordPress and other WYSIWYG tools.


Puck v0.13.0 introduces some of our most powerful APIs yet, enabling completely custom interfaces, adding support for object fields and mechanisms to restrict DropZones.

TLDR

  1. Custom interfaces: Take complete control of the Puck UI with the new custom interface APIs.
  2. Object fields: Represent objects as fields with the new object field type.
  3. DropZone restrictions: The new allow and disallow props allow you to restrict which components can be dropped into DropZones.
  4. New plugin API (Breaking Change): The plugin API has been updated to align with the new custom interfaces API. This is a breaking change. The plugin API remains experimental.
  5. New transformProps API: The new transformProps API makes it easier to rename props on your components without breaking your payload.
  6. New ui prop: Set the initial UI state for the Puck editor on render.
  7. Add search to external fields: Show a search input in the external field modal, enabling the user to query your external API.

Highlights

🎨 Custom interfaces

It's now possible to create completely custom Puck interfaces to integrate more deeply with your own UI and create a seamless experience for your users.

custom-puck-ui-example

This can be achieved by passing children to the <Puck> component.

import { Puck } from "@measured/puck";

export function Editor() {
  return (
    <Puck>
      <div style={{ background: "hotpink" }}>
        <Puck.Preview />
      </div>
    </Puck>
  );
}
Enter fullscreen mode Exit fullscreen mode

See this demo.

🪝 The usePuck hook

Access Puck's internals using the usePuck hook to extend Puck's functionality with powerful custom 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

🗃️ Object fields

Object fields enable you to represent your object types with the fields API. No more flattening your props!

const config = {
  components: {
    Example: {
      fields: {
        params: {
          type: "object",
          objectFields: {
            title: { type: "text" },
          },
        },
      },
      render: ({ params }) => {
        return <p>{params.title}</p>;
      },
    },
  },
};
Enter fullscreen mode Exit fullscreen mode

🙅 DropZone restrictions

Restrict which components can be passed into a DropZone component with the allow and disallow props.

const MyComponent = () => (
  <DropZone zone="my-content" allow={["HeadingBlock"]} />
);
Enter fullscreen mode Exit fullscreen mode

Deprecations

renderHeader deprecated

The renderHeader prop has been deprecated in favor of the overrides API.

// Before
export function Editor() {
  return (
    <Puck
      renderHeader={({ appState, dispatch }) => ()}
    />
  );
}

// After
export function Editor() {
  return (
    <Puck
      overrides={{
        header: ({ appState, dispatch }) => ()
      }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

renderHeaderActions deprecated

The renderHeaderActions prop has been deprecated in favor of the overrides API.

// Before
export function Editor() {
  return (
    <Puck
      renderHeaderActions={({ appState, dispatch }) => ()}
    />
  );
}

// After
export function Editor() {
  return (
    <Puck
      overrides={{
        headerActions: ({ appState, dispatch }) => ()
      }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Breaking changes

renderComponentList removed

The renderComponentList prop has been removed in favor of the overrides API.

// Before
export function Editor() {
  return (
    <Puck
      renderComponentList={({ appState, dispatch }) => ()}
    />
  );
}

// After
export function Editor() {
  return (
    <Puck
      overrides={{
        componentList: ({ appState, dispatch }) => ()
      }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Plugin API revamped

The plugin API has been significantly revamped to match the overrides API.

// Before
export function Editor() {
  return (
    <Puck
      plugins={[
        { renderFields: ({ appState, dispatch }) => () }
      ]}
    />
  );
}

// After
export function Editor() {
  return (
    <Puck
      plugins={[
        overrides: {
          form: ({ appState, dispatch }) => ()
        }
      ]}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Changelog

See the GitHub release for a full changelog.

Top comments (0)