
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 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
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 (
);
}
Action bar override
The new actionBar override enables you to create a custom action bar overlay, or extend the default one using the component:
const overrides = {
actionBar: ({ children }) => (
{/* Render default actions */}
{children}
{/* Render new actions */}
console.log("Clicked!")}>
★
),
};
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}>;
},
};
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 (
);
}
React to actions
The onAction API enables you to react to Puck’s internal actions as they’re dispatched:
export function Editor() {
return (
{
if (action.type === "insert") {
console.log("New component was inserted", appState);
}
}}
/>
);
}
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 [email protected] (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
- @mkilpatrick made their first contribution in https://github.com/measuredco/puck/pull/505
- @nova4u made their first contribution in https://github.com/measuredco/puck/pull/538
- @antonmalyavkin made their first contribution in https://github.com/measuredco/puck/pull/585
Full Changelog: https://github.com/measuredco/puck/compare/v0.15.0...v0.16.0