Puck: Unleash Your React Components with Visual Editing Magic
Puck is an innovative open-source visual editor designed specifically for React applications. It bridges the gap between developers and content creators, offering a powerful drag-and-drop interface that works seamlessly with your existing React component library. Let’s dive into the world of Puck and discover how it can transform your content creation workflow.
What Sets Puck Apart?
Puck isn’t just another page builder – it’s a game-changer for React developers and content teams alike. Here’s why:
- React-First Approach: Built from the ground up for React, ensuring seamless integration with your existing projects.
- Component Flexibility: Use your own React components, maintaining full control over your design system.
- Open-Source Freedom: Licensed under MIT, Puck is free to use in both personal and commercial projects.
- Self-Hosted Solution: Avoid vendor lock-in by hosting Puck within your own infrastructure.
- Headless CMS Compatibility: Easily integrate with your favorite headless CMS or use Puck as a standalone solution.
- Extensibility: Enhance Puck’s functionality with a growing ecosystem of plugins and adapters.
Getting Started with Puck
Let’s walk through the process of adding Puck to your React project.
Installation
First, install Puck using npm:
npm install @measured/puck
For those starting a new project, Puck offers a convenient generator:
npm create puck-app
Basic Configuration
To use Puck, you’ll need to create a configuration file that defines your components and their editable properties. Here’s a simple example:
import type { Config } from "@measured/puck";
type Props = {
HeadingBlock: { title: string };
};
export const config: Config<Props> = {
components: {
HeadingBlock: {
fields: {
title: { type: "text" },
},
defaultProps: {
title: "Heading",
},
render: ({ title }) => (
<div style={{ padding: 64 }}>
<h1>{title}</h1>
</div>
),
},
},
};
export default config;
This configuration defines a simple HeadingBlock
component with an editable title
field.
Rendering the Puck Editor
To render the Puck editor in your application, use the <Puck>
component:
import { Puck } from "@measured/puck";
import "@measured/puck/puck.css";
import config from "./puck.config";
const initialData = {};
const save = (data) => {
// Save logic here
};
export function Editor() {
return <Puck config={config} data={initialData} onPublish={save} />;
}
Rendering Puck-Generated Content
To display the content created with Puck, use the <Render>
component:
import { Render } from "@measured/puck";
import config from "./puck.config";
export function Page({ data }) {
return <Render config={config} data={data} />;
}
Advanced Puck Features
Custom Fields
Puck allows you to create custom field types for more complex editing scenarios. Here’s an example of a custom color picker field:
import { Puck } from "@measured/puck";
import ColorPicker from "your-color-picker-component";
const config = {
components: {
ColorBlock: {
fields: {
color: {
type: "custom",
render: ({ onChange, value }) => (
<ColorPicker color={value} onChange={onChange} />
),
},
},
render: ({ color }) => (
<div style={{ backgroundColor: color, height: 100, width: 100 }} />
),
},
},
};
Dynamic Fields
Use the resolveFields
API to dynamically show or hide fields based on other prop values:
const config = {
components: {
ConditionalBlock: {
fields: {
type: { type: "select", options: ["text", "image"] },
},
resolveFields: ({ type }) => {
if (type === "text") {
return { content: { type: "text" } };
} else {
return { src: { type: "text" } };
}
},
render: ({ type, content, src }) => (
type === "text" ? <p>{content}</p> : <img src={src} alt="" />
),
},
},
};
Permissions API
Puck v0.16 introduced a powerful permissions API, allowing you to control which editing features are available:
<Puck
config={config}
data={data}
permissions={{
delete: false,
duplicate: true,
insert: (componentData) => componentData.type !== "RestrictedComponent",
}}
/>
This example disables deletion, enables duplication, and restricts insertion of a specific component type.
Extending Puck
Plugins
Puck’s plugin system allows you to extend its functionality. For example, the heading-analyzer
plugin helps ensure proper heading structure for accessibility:
import { Puck } from "@measured/puck";
import { headingAnalyzer } from "@measured/puck-plugin-heading-analyzer";
<Puck
config={config}
data={data}
plugins={[headingAnalyzer()]}
/>
Custom Action Bars
Create tailored editing experiences by customizing the action bar for your components:
const config = {
components: {
CustomBlock: {
fields: { /* ... */ },
render: ({ /* ... */ }) => { /* ... */ },
actionBar: ({ remove, duplicate }) => (
<>
<button onClick={remove}>Delete</button>
<button onClick={duplicate}>Clone</button>
<button onClick={() => console.log("Custom action")}>
Custom Action
</button>
</>
),
},
},
};
Conclusion
Puck offers a powerful, flexible solution for creating visual editing experiences in React applications. By seamlessly integrating with your existing component library and offering extensive customization options, Puck empowers both developers and content creators to build dynamic, on-brand pages with ease.
Whether you’re building a marketing site, a content-heavy application, or a custom CMS, Puck provides the tools you need to create a tailored visual editing experience. Its open-source nature and growing community ensure that Puck will continue to evolve and improve, making it a solid choice for your next React project.
Give Puck a try in your next project, and experience the magic of visual editing with your own React components!