The improvements in Puck 0.20 introduce a few breaking changes and some deprecations, but the upgrade path is straightforward and shouldn’t affect most apps.
This guide covers everything you need to know to upgrade safely and take advantage of what’s new:
If you’d like to help test Puck 0.20, follow this guide and report any issues you encounter.
For a full overview of what’s new in Puck 0.20, check out the release blog post.
Run the following command to install the latest version of Puck:
npm install --save-exact @measured/puck@^0.20.0
usePuck.get
has been removedIn Puck 0.19, useGetPuck
was introduced to access the Puck state outside the render lifecycle.
At the same time, usePuck.get
was also mistakenly included. This function was never meant to be public and caused re-renders on every state change.
As a result, usePuck.get
has been removed. Use useGetPuck
instead to achieve the same behavior.
// Before:
import { createUsePuck } from "@measured/puck";
const usePuck = createUsePuck();
const SaveDataButton = () => {
const get = usePuck((s) => s.get);
const handleSave = useCallback(() => {
const { appState } = get();
savePage(appState.data);
}, [get]);
return <button onClick={handleSave}>Save</button>;
};
// After:
import { useGetPuck } from "@measured/puck";
const SaveDataButton = () => {
const getPuck = useGetPuck();
const handleSave = useCallback(() => {
const { appState } = getPuck();
savePage(appState.data);
}, [getPuck]);
return <button onClick={handleSave}>Save</button>;
};
overrides.components
The components
override has been renamed and is now deprecated. Using it will show a warning:
The `components` override has been deprecated and renamed to `drawer`
To fix this warning, replace your components
override with drawer
:
// Before:
<Puck
data={data}
config={config}
overrides={{
components: ({ children }) => (
<div style={{ display: "flex", flexWrap: "wrap" }}>{children}</div>
),
}}
/>
// After:
<Puck
data={data}
config={config}
overrides={{
drawer: ({ children }) => (
<div style={{ display: "flex", flexWrap: "wrap" }}>{children}</div>
),
}}
/>
overrides.componentItem
The componentItem
override has been renamed and is now deprecated. Using it will show a warning:
The `componentItem` override has been deprecated and renamed to `drawerItem`
To fix this warning, replace your componentItem
override with drawerItem
:
// Before:
<Puck
data={data}
config={config}
overrides={{
componentItem: ({ children }) => (
<div style={{ border: "1px solid red" }}>{children}</div>
),
}}
/>
// After:
<Puck
data={data}
config={config}
overrides={{
drawerItem: ({ children }) => (
<div style={{ border: "1px solid red" }}>{children}</div>
),
}}
/>
Config
type genericsPassing multiple generics to type your Puck config has been deprecated.
To migrate, use a single generic object instead:
All properties on the object are optional—you can pass only what you need.
// Before:
type Components = {
Heading: { title: string };
};
type RootProps = { title: string };
type Categories = "typography" | "layout" | "other";
const config: Config<Components, RootProps, Categories> = {
//...
};
// After:
type Components = {
Heading: { title: string };
};
type RootProps = { title: string };
type Categories = ["typography", "layout", "other"];
type MyFields = { checkbox: { type: "checkbox" } };
const config: Config<{
components: Components;
root: RootProps;
categories: Categories;
fields: MyFields;
}> = {
//...
};
ComponentConfig
type genericsPassing multiple generics to type your component configs has been deprecated.
To migrate, use a single generic object instead:
All properties on the object are optional—you can pass only what you need.
// Before:
type Props = {
title: string;
};
const Heading: ComponentConfig<Props> = {
// ...
};
// After:
type Props = {
title: string;
};
type CustomFields = { checkbox: { type: "checkbox" } };
const Heading: ComponentConfig<{
props: Props;
fields: CustomFields;
}> = {
// ...
};
Previously, migrate
would fail with an error when used with DropZone data containing dynamic zone names:
Could not migrate DropZone "DropZoneComponent-123:dynamicZone-0" to slot field.
No slot exists with the name "dynamicZone-0".
To handle these cases, you can now use the migrateDynamicZonesForComponent
option.
migrateDynamicZonesForComponent
is an object that maps component names to custom migration functions.
Each function receives:
It should return the component’s updated props with the migrated data.
const legacyData = {
root: {
props: {
title: "Legacy Zones Migration",
},
},
content: [
{
type: "Columns",
props: {
id: "Columns-123",
columns: [{}],
},
},
],
zones: {
"Columns-123:column-0": [
{
type: "Header",
props: {
id: "Header-123",
title: "Drop zone 0",
},
},
],
},
};
const newData = migrate(legacyData, config, {
migrateDynamicZonesForComponent: {
// zones = { column-0: [{ type: "Header", props: {...} }] }
Columns: (props, zones) => {
return {
...props,
columns: Object.values(zones).map((zone) => ({
column: zone,
})),
};
},
},
});
console.log(newData);
// {
// root: {
// props: {
// title: "Legacy Zones Migration"
// },
// },
// content: [
// {
// type: "Columns",
// props: {
// id: "Columns-123",
// columns: [
// {
// column: [
// {
// type: "Header",
// props: {
// id: "Header-123",
// text: "Drop zone 0",
// },
// },
// ],
// },
// ],
// },
// },
// ],
// },
Check out the full changelog, including known issues, on the GitHub release.
If you’re interested in learning more about Puck, check out the demo or read the docs. If you like what you see, please give us a star on GitHub to help others find Puck too!