ByteLog IconByteLog
Back to Blog

React Puck: The Visual Editor for Developers

December 18, 2025
Byteland Team
ReactVisual EditorNext.jsHeadless CMSOpen Source

In the landscape of modern web development, the tension between developer control and content creator autonomy is a tale as old as time. Headless CMS solutions promised a separate peace, but often left marketing teams stranded without the visual "drag-and-drop" tools they rely on.

Enter Puck (measured/puck), a self-hosted visual editor for React that is reshaping how we think about page building. Unlike proprietary SaaS builders that act as black boxes, Puck is a component you embed directly into your application. It gives you the visual experience of a Wix or Squarespace, but with the full power of your own React component library.

What is Puck? 🤔

At its core, Puck is a schema-driven visual editor. It doesn't save HTML. It doesn't save a proprietary binary format. It saves clean, typed JSON.

The Puck Editor Interface

What is Puck? 🤔

At its core, Puck is a schema-driven visual editor. It doesn't save HTML. It doesn't save a proprietary binary format. It saves clean, typed JSON.

This JSON describes intent: "Render a Hero component here with these props." Your application then takes this JSON and renders it using your actual React components. This means your design system is the single source of truth.

Key Innovations ✨

FeatureDescriptionBenefit
Self-HostedLives inside your React/Next.js/Remix appNo external iframe, no third-party downtime risk
Component OwnershipYou define the components (Hero, Card, etc.)Complete control over UI/UX and functionality
JSON OutputThe "source of truth" is a clean JSON treeDatabase agnostic. Store it in Postgres, Mongo, or a Git repo
TypeScript NativeBuilt with robust typing in mindTypesafe configuration ensures your editor never breaks production

The Evolution of Page Building 📉

To appreciate the leap Puck represents, we must look at how we got here.

Evolution of Visual Editing Tools

  1. Strictly Code (2010s): Developers hardcoded everything. Marketing had to ask devs to fix typos.
  2. Headless CMS (2018s): We decoupled content from code. Great for data, but marketing lost the ability to control "Layout". They could change the title, but not move the Hero section above the Feature section.
  3. Visual Data (2025): Puck treats layout component selection as data. Marketing gets a visual builder, but it generates clean data that Developers control.

Puck's Component-Driven Architecture

Core Architecture: Config, Data, Render 🔄

Puck acts as a bridge between your React components and the content creator. This bridge is built on three pillars.

1. The Config (The Schema) ⚙️

The generic Config object is where you define your components and their editable fields. This is where the magic happens. You map a component name (like "Hero") to:

  1. Render Function: How it looks.
  2. Fields: What can be edited (Text, Images, Radios, etc.).
  3. Default Props: Starting values.
import { Config } from "@measured/puck"; type Props = { Hero: { title: string; variant: "simple" | "full" }; FeatureList: { items: { title: string }[] }; }; export const config: Config<Props> = { components: { Hero: { fields: { title: { type: "text" }, variant: { type: "radio", options: [ { label: "Simple", value: "simple" }, { label: "Full Width", value: "full" }, ], }, }, render: ({ title, variant }) => ( <section className={variant === "full" ? "p-20" : "p-10"}> <h1>{title}</h1> </section> ), }, // ... other components }, };

2. The Data (The Payload) 💾

When a user hits "Save", Puck generates a JSON payload. This is what you persist to your database.

{ "content": [ { "type": "Hero", "props": { "title": "Welcome to the Future", "variant": "full" } } ], "root": { "props": { "title": "Home Page" } } }

3. The Renderer (The Output) 🎨

The <Render /> component is a lightweight runtime that takes the Config and the Data and paints the screen. It creates a seamless mapping between the stored intent and the live UI.

The complete flow from developer configuration to final rendered page

Advanced Patterns & "Dry Goods" 🛠️

Here is where Puck shines for power users. It's not just basic fields; it supports complex application requirements.

Nested Layouts with Zones

Puck isn't just a flat list of components. With Zones, you can create deeply nested layouts (like Columns, Grids, or Card slots).

// Example: A Two-Column Layout Component TwoColumn: { render: () => ( <div className="grid grid-cols-2 gap-4"> <div className="border p-4"> <DropZone zone="left-column" /> </div> <div className="border p-4"> <DropZone zone="right-column" /> </div> </div> ) }

This allows content editors to drag other components inside the columns, creating infinite layout possibilities.

Server-Side Data Resolution (resolveData)

What if your component needs live data, like "Latest 3 Blog Posts"? You don't want to hardcode post titles in the JSON. You want them fetched at request time.

Puck allows you to define a resolveData function (in frameworks like Next.js) to hydrate props before rendering.

// puck.config.tsx export const config = { components: { LatestPosts: { resolveData: async ({ limit }) => { const posts = await db.posts.findMany({ take: limit }); return { posts }; // Injected into render }, render: ({ posts }) => <PostGrid data={posts} /> } } }

Custom Fields & Plugins

Need a specific Color Picker, a Google Maps coordinate selector, or a Discogs Album searcher? Puck allows custom field types. You can build a React component that acts as a form input and register it as a custom field type in your config.

Competitor Landscape: Puck vs The World 🥊

To understand where Puck fits, we need to compare it with the existing solutions involved in the "Content vs Code" dilemma.

1. Puck vs Builder.io / Plasmic (SaaS Visual Builders)

Tools like Builder.io are powerful SaaS solutions that offer a similar "drag-and-drop React component" experience. However, they act as a black box. Your content lives on their servers, and if their API goes down, your site might too. Puck, being self-hosted, gives you 100% data ownership and zero external dependencies.

2. Puck vs Contentful / Sanity (Headless CMS)

Traditional Headless CMSs excel at structured data (e.g., a database of blog posts), but they struggle with page layout. You often end up creating "Block" fields that are clunky to reorder and visualize. Puck effectively replaces the "Page Builder" aspect of a Headless CMS while letting you keep your existing CMS for structured content.

3. Puck vs WordPress / Wix

Legacy builders offer great visual editing but sacrifice developer experience. You lose the power of modern React, Component usability, and Git-based workflows. Puck brings the "Wix experience" to your rigorous Next.js codebase.

Summary Comparison Table

AspectPuck 🚀Builder.io ☁️Headless CMS 📄WordPress 👴
HostingSelf-HostedSaaS (Cloud)SaaS (Cloud)Self-Hosted / SaaS
Data Control100% Yours (JSON)Vendor Lock-inVendor Lock-inDatabase
Developer DXHigh (React)MediumHigh (API)Low (PHP)
Visual EditingExcellentExcellentPoor (Abstract)Good
CostFree (Open Source)Expensive (Enterprise)$$ / UserFree / $$

Why Puck Matters in 2025 🎯

As of late 2025, the React ecosystem has matured around Server Components (RSC). Puck is uniquely positioned because its output is serializable data.

  • RSC Compatible: The <Render> component works perfectly in Server Components.

  • Tailwind v4.0 Ready: Being unopinionated on style means it adopts your existing Tailwind setup instantly.

  • Version Control for Content: Because the content is JSON, you can literally Git-track your page content if you want to.

Various applications of Puck beyond website building

Implementation Guide 🚀

1. Installation

npm install @measured/puck

2. The Editor Route (Next.js App Router)

// app/admin/editor/page.tsx "use client"; import { Puck } from "@measured/puck"; import "@measured/puck/puck.css"; import { config } from "@/puck.config"; export default function Editor() { return ( <Puck config={config} data={initialData} onPublish={async (data) => { await saveToDb(data); }} /> ); }

3. The Public Route

// app/page.tsx import { Render } from "@measured/puck"; import { config } from "@/puck.config"; import { getFromDb } from "@/utils/db"; export default async function Page() { const data = await getFromDb(); return <Render config={config} data={data} />; }

Conclusion 🎊

Puck represents the "Post-Headless" era: Visual editing without the trade-offs. It respects the developer's need for code quality and the marketer's need for speed.

For teams building on Next.js or generic React stacks, Puck removes the need for expensive monthly contracts with Builder.io or Contentful, bringing the power of visual editing in-house, where it belongs.


Ready for Puck?

Last updated: December 2025