DEV Community

Cover image for Puck v0.16: Permissions
Chris Villa for Measured

Posted on • Originally published at measured.co

Puck v0.16: Permissions

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


We're celebrating 5,000 stars on GitHub! Thank you to our wonderful community!

Puck v0.16 is a big release, introducing the headline permissions API and — you guessed it — quality of life improvements. This one took a while to put together, and we appreciate your patience and support.

  • Permissions: Toggle Puck features like duplication, dragging, deletion through the new permissions and resolvePermissions APIs.
  • Action bar override: Create custom action bars using the actionBar override, or extend the default one using the new <ActionBar> component.
  • iframe style injection: Access the iframe document to inject styles directly, or make other changes, via the new iframe override. We also introduced the emotion-cache plugin for the common Emotion use-case.
  • History injection: Inject the undo/redo history via the a series of new APIs
  • React to actions: React to actions dispatched by Puck via the onAction callback.
  • Optional fields: Optional props are no longer required to define in fields, since they may be defined

Upgrade today or get started with:

npx create-puck-app@latest
Enter fullscreen mode Exit fullscreen mode

Permissions

Permissions enable you to toggle core Puck functionality globally, on a per-component basis or dynamically. Huge thanks to @xaviemirmon for his efforts on this.

export function Editor() {
  return (
    <Puck
      permissions={{
        delete: false,
        duplicate: true
      }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Action bar override

The new actionBar override enables you to create a custom action bar overlay, or extend the default one using the <ActionBar> component:

const overrides = {
  actionBar: ({ children }) => (
    <ActionBar label="Actions">
      {/* Render default actions */}
      <ActionBar.Group>{children}</ActionBar.Group>

      {/* Render new actions */}
      <ActionBar.Group>
        <ActionBar.Action onClick={() => console.log("Clicked!")}></ActionBar.Action>
      </ActionBar.Group>
    </ActionBar>
  ),
};
Enter fullscreen mode Exit fullscreen mode

iframe style injection

The iframe override enables you to access the iframe document, making it possible to inject styles into the head:

const overrides = {
  iframe: ({ children, document }) => {
    useEffect(() => {
      if (document) {
        document.body.setAttribute("style", "background: hotpink;");
      }
    }, [document]);

    return <>{children}</>;
  },
};
Enter fullscreen mode Exit fullscreen mode

The new emotion-cache plugin uses this API to create an emotion cache inside the iframe, making Puck easy to use with any Emotion-based component library.

History injection

Use the new history injection APIs to provide your own undo/redo history via the initialHistory prop, or dynamically via the setHistories and setHistoryIndex functions from usePuck().history.

const historyState = {
  data: {
    root: {
      props: { title: "My History" },
    },
  },
};

export function Editor() {
  return (
    <Puck
      initialHistory={{
        histories: [{ state: historyState }],
        index: 0,
      }}
      // ...
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

React to actions

The onAction API enables you to react to Puck’s internal actions as they’re dispatched:

export function Editor() {
  return (
    <Puck
      onAction={(action, appState, prevAppState) => {
        if (action.type === "insert") {
          console.log("New component was inserted", appState);
        }
      }}
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Breaking changes

history.data is now history.state

When using the usePuck history API, data is now renamed state.

history.id is now optional (TypeScript)

When using the usePuck history API id is now optional. Puck will always generate an id, but TypeScript may complain.

lastData is now returned as null instead of {} when empty in resolvers

When using the lastData option provided to resolveData or resolveFields functions, and there is no previous data, lastData will now be null instead of {}.

Full changelog

Features

  • add actionBar override for adding component controls (48ec0d7)
  • add automatic RSC export, replacing /rsc bundle (d21eba6)
  • add isDisabled prop to Drawer.Item (cad95b8)
  • add generic type to usePuck hook (01703a9)
  • add iframe override for style injection (7cac376)
  • add initialHistory prop to Puck (54b5a87)
  • add onAction API to track and react to state changes (c7007ac)
  • add permissions API (a43914d)
  • add plugin for injecting Emotion cache (f8a88b9)
  • add resolvePermissions API (f0655f0)
  • add waitForStyles option to iframe config (bc81d9c)
  • call resolveData when new item inserted (3298831)
  • don't mandate fields for optional props (5a219ef)
  • export ActionBar component for use in overrides (04fd6c5)
  • infer Data type from user config (50045bb)
  • make ID optional in History type (BREAKING CHANGE) (d917229)
  • provide ES Module build (ff9076b)
  • rename history.data to history.state (BREAKING CHANGE) (b09244c)
  • show spinner if iframe load takes over 500ms (cfecf54)
  • streamline usePuck history API (c8b2807)
  • upgrade "next" recipe to typescript@5.5.4 (60fe631)

Bug Fixes

  • add favicon to next recipe to prevent Puck 404 (2c52d27)
  • add missing readOnly state to External fields (bf1449d)
  • always record history on component insert (88c5ab6)
  • don't cache /edit route in Next recipe (94f16b2)
  • don't submit buttons if Puck used in form (f761e5f)
  • ensure demo types are satisfied with TypeScript@5 (958dc25)
  • export missing Plugin type (eb42734)
  • fix crash if component in data is missing from config (0daf478)
  • improve resiliency of iframe CSS for some frameworks, like Mantine (538cb05)
  • make Config and Data types more robust (6bcf555)
  • prevent infinite loop when using plugins with some frameworks (3870871)
  • prevent Tailwind from clashing with viewport zoom select (9151255)
  • remove body margin in remix recipe (0898b26)
  • resize viewport when changed via app state (14419ec)
  • resolve fields when switching between items of same type (a3518ca)
  • return lastData as null instead of empty object in resolvers (BREAKING CHANGE) (648eb92)
  • show warning if heading-analyzer styles aren't loaded (4e7110b)
  • use correct color in FieldLabel labels (b0469a1)

New Contributors

Full Changelog: https://github.com/measuredco/puck/compare/v0.15.0...v0.16.0

Top comments (0)