CLI tool
puckeditor/puck avatar
puckeditor/puck

Puck: a React visual editor you embed, not a hosted page builder

The visual editor for React.

13,325 stars973 forksTypeScriptMIT

At a glance

What is it?
Puck is an MIT-licensed React component that turns your own components into a drag-and-drop editor. It suits teams that already own a database and a component library, and it is the wrong tool if you want a finished CMS with a hosted backend.
Who is it for?
Adopt Puck if your team already owns a React component library and a database, and you want editing on top of both rather than a separate CMS. Do not adopt it if you need a hosted backend, a finished admin UI, or a product with a stable public API: the repository is still on 0.x, and the package.json requires Node >=24.0.0 and pnpm >=11.0.0, so verify your build image before you start.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository received new commits within the last day.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 16, 2026, and from our analysis. They are not legal advice.

DEEP OPEN-SOURCE ANALYSIS

What Puck solves, and who it is actually for

Puck is a visual editor you mount inside a React application. It is not a standalone product with its own admin panel and database. The README describes it as "a modular, open-source visual editor for React.js" and says you use it to "build custom drag-and-drop experiences with your own application and React components". That sentence defines the audience precisely: teams that already have React components and want non-engineers to arrange them on a page.

The audience is narrower than the topic list suggests. The repository carries topics such as no-code and page-builder, but the quick start is JavaScript. You write a config object that maps component names to fields and render functions, then pass that config to a Puck component. A no-code buyer looking for a dashboard to log into will not find one here. A product team with a design system, a Postgres table, and a marketing site that keeps needing new landing pages will recognise the shape immediately.

The MIT licence matters for that second group. The README states Puck is "licensed under MIT, making it suitable for both internal systems and commercial applications", and that you "own your data and there's no vendor lock-in". There is no hosted service in the loop, so nothing about your page data leaves your infrastructure.

The config object is the whole architecture

There is no plugin runtime to learn and no schema language. Puck's mechanism is one object: `config.components` maps a component key to a `fields` definition and a `render` function. The editor reads `fields` to decide which inputs to show in the sidebar, and calls `render` to draw the component on the canvas.

Data flows in two directions through the same config. In the editor, Puck holds the current page state and hands it to `onPublish` when the user saves. On the public site, the `Render` component takes the same config plus stored `data` and produces the page. Because both sides share one config, a component cannot drift between what the editor previews and what visitors see. That is the design decision worth noting: Puck does not own your rendering, it only owns the editing layer.

The config also means your component library is the limit of what editors can build. If a marketing request needs a component that does not exist in the config, no amount of dragging will produce it. Puck gives editors a constrained palette, not a general-purpose layout engine, and that constraint is the point.

Install and first render

The README gives two entry points. The package install is `npm i @puckeditor/core --save`, and the alternative is a scaffolded app via `npx create-puck-app my-app`. Start with the package if you are adding Puck to an existing codebase.

bash
npm i @puckeditor/core --save

Then define a config and render the editor. The README's example registers a single `HeadingBlock` with one `text` field and an `onPublish` callback that stands in for your save function.

jsx
// Editor.jsx
import { Puck } from "@puckeditor/core";

const config = {
  components: {
    HeadingBlock: {
      fields: {
        children: {
          type: "text",
        },
      },
      render: ({ children }) => {
        return <h1>{children}</h1>;
      },
    },
  },
};

const initialData = {};

const save = (data) => {};

export function Editor() {
  return <Puck config={config} data={initialData} onPublish={save} />;
}

On screen you should get the Puck editor with a single HeadingBlock available to drop onto the canvas. Publishing calls `save` with the page data; in the README that function is empty, so nothing is persisted until you write it.

The public page uses the same config with the stored data, through the `Render` component.

jsx
// Page.jsx
import { Render } from "@puckeditor/core";

export function Page() {
  return <Render config={config} data={data} />;
}

If you would rather start from a working app, `npx create-puck-app my-app` scaffolds one from the recipes. The README lists two: `next`, described as a Next.js example using App Router and static page generation, and `react-router`, a React Router v7 example using dynamic routes to create pages at any level. Pick the recipe that matches your router before you write config by hand, because the recipe already wires the editor route and the render route together.

Where Puck stops, and where that hurts

The README's `save` function is `(data) => {}`. That is the honest shape of the project: persistence, authentication, revision history, and publishing workflows are yours to build. Puck does not ship a database adapter or a draft system, and the README does not document rollback. If your editors expect to undo a publish from yesterday, you are implementing that yourself.

The version number is the second constraint. The latest release listed is v0.23.0, published on 2026-08-07, and the repository package.json carries `"version": "0.23.0"`. A 0.x line means the maintainers have not committed to API stability. Teams that pin dependencies and upgrade on a quarterly cadence should budget for config migrations between minor releases, and should read CHANGELOG.md before bumping.

There is also a toolchain floor. The root package.json declares `"engines": { "node": ">=24.0.0", "pnpm": ">=11.0.0" }` and `"packageManager": "pnpm@11.26.0"`. That applies to building the repository itself; the published package's own engine range is not stated in the README. Either way, a CI image on Node 20 will not build this monorepo, and the README does not discuss it.

Finally, Puck is the wrong tool when the requirement is a multi-tenant CMS with roles, scheduling, and localisation out of the box. Puck gives you the canvas. Everything around the canvas is an application you have not written yet.

How Puck differs from a headless CMS

The closest alternative framing is a headless CMS such as Strapi or Payload, and the difference is where the schema lives. In a headless CMS you define content types in the CMS, and your front end fetches them over an API. The CMS owns the data model, the admin UI, and the database.

Puck inverts that. Your React components are the schema. The config you write for Puck is the same config that renders the page, and the data Puck produces is whatever you choose to store. There is no content API to call and no second admin application to deploy. The trade-off is real in both directions: a headless CMS gives you roles, media handling and a content API on day one, while Puck gives you a canvas that can render any component you already own, including ones a generic CMS field editor could never express.

A second comparison point is the scaffolded route. `npx create-puck-app my-app` produces a working Next.js or React Router app, which is closer to a starter kit than to a CMS install. If you want to evaluate the editing experience before writing any code, the README links a demo at demo.puckeditor.com/edit, and the repository README also points to the awesome-puck community repo for plugins and custom fields.

Maintenance, releases and what the licence leaves open

The repository is not archived, and the last push was on 2026-09-15. Release cadence is visible in the changelog entries: v0.22.3 on 2026-07-28, v0.22.4 on 2026-07-29, and v0.23.0 on 2026-08-07. The release scripts in the root package.json use lerna, conventional-recommended-bump and a generated changelog, so version bumps follow conventional commits rather than a fixed schedule. The practical upgrade cost is reading CHANGELOG.md and re-testing your config, since a 0.x minor can change field or config behaviour.

Support routes are listed in the README: a Discord server for discussions, GitHub issues for questions, and a paid discovery call for hands-on support and consultancy. The README also credits the awesome-puck community repository for plugins and custom fields, which means some extension work lives outside the main repository and carries its own maintenance burden.

The MIT licence is permissive: it allows commercial and internal use, and the README states that directly. That is a statement about the licence text, not legal advice. If you redistribute Puck inside a product, or combine it with differently licensed dependencies, have your own counsel review the terms rather than relying on a README sentence.

Editorial conclusion

Adopt Puck if your team already owns a React component library and a database, and you want editing on top of both rather than a separate CMS. Do not adopt it if you need a hosted backend, a finished admin UI, or a product with a stable public API: the repository is still on 0.x, and the package.json requires Node >=24.0.0 and pnpm >=11.0.0, so verify your build image before you start. Before committing, check that the Puck component config can express every field your editors need, because the README only documents the text field type and leaves the rest to the documentation site.

Frequently asked questions

How do I install Puck in an existing React app?

Install the package with npm i @puckeditor/core --save, then define a config object with a components map and render the Puck component with that config, initial data and an onPublish callback. The README shows this as a single Editor.jsx file.

Does Puck include a backend or database for storing page data?

No. In the README the save function is an empty stub, so persistence is your responsibility. Puck produces the page data through onPublish, and you store it wherever your application already stores content.

Can I use Puck with Next.js?

Yes. The README states Puck plays well with all React.js environments including Next.js, and one of the two provided recipes is a Next.js example using App Router and static page generation, scaffolded with npx create-puck-app my-app.

Official sources

  1. License: MIT
  2. Project website
  3. puckeditor/puck on GitHub
  4. README
  5. Releases
Community notes

Community notes