npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bespokecode/payloadcms-zen-editor

v0.1.1

Published

A visual block editor for Payload CMS — edit native Payload blocks in a fullscreen, Elementor-style UI with live preview.

Readme

@bespokecode/payloadcms-zen-editor

A visual, fullscreen block editor for Payload CMS. Lets editors open any document in a side-by-side "Zen" view: a live preview iframe on the right and a drag-sortable block list with autosave and publish controls on the left. Native Payload blocks — no lock-in, no custom data format.

Features

  • Fullscreen editing UI accessible from the standard Payload document toolbar
  • Live-preview iframe with desktop / tablet / mobile breakpoints
  • Drag-and-drop block reordering
  • Add / duplicate / delete blocks via a block picker drawer
  • Autosave on every change; explicit Publish button
  • Click-to-select blocks in the preview (requires ZenEditable wrapper — see below)
  • Zero host-project dependencies — ships its own @dnd-kit/* and lucide-react

Requirements

  • Payload CMS 3.x
  • Next.js 15 or 16
  • React 19

Installation

pnpm add @bespokecode/payloadcms-zen-editor

Setup

1. Payload config

Add the plugin to your payload.config.ts and tell it which collections and fields to manage:

// payload.config.ts
import { zenEditorPlugin } from '@bespokecode/payloadcms-zen-editor'
import { buildConfig } from 'payload'

export default buildConfig({
  // ...
  plugins: [
    zenEditorPlugin({
      collections: {
        // key = collection slug, value = per-collection options
        pages: { blocksField: 'layout' },
        posts: { blocksField: 'content' },
      },
    }),
  ],
})

The plugin injects an "Open Zen Editor" button into the document toolbar and registers a /zen view route for each enabled collection. No changes are made to your collection schema.

Live preview URL (required for the preview pane)

The Zen editor resolves the preview URL from the collection's admin.livePreview.url setting. Configure it the same way you would for Payload's built-in live preview:

// collections/Pages.ts
export const Pages: CollectionConfig = {
  slug: 'pages',
  admin: {
    livePreview: {
      url: ({ data, req }) =>
        `${process.env.NEXT_PUBLIC_SERVER_URL}/pages/${data.slug}`,
    },
  },
  // ...
}

If livePreview.url is not set the preview pane will be blank, but editing still works.

2. Frontend wiring

Two pieces are needed in your Next.js frontend.

ZenEditorBridge — root layout (once)

Mount this component once in your root layout. In normal browsing it renders nothing. When the page is loaded inside the Zen editor iframe it activates the postMessage bridge.

// app/layout.tsx
import { ZenEditorBridge } from '@bespokecode/payloadcms-zen-editor/client'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <ZenEditorBridge />
      </body>
    </html>
  )
}

ZenEditable — block renderer

Wrap each block in your block renderer with ZenEditable. Pass the block's position index and its blockType. This enables click-to-select in the preview pane and shows hover/selection overlays.

// components/RenderBlocks.tsx
import { ZenEditable } from '@bespokecode/payloadcms-zen-editor/client'

export const RenderBlocks: React.FC<{ blocks: Block[] }> = ({ blocks }) => (
  <>
    {blocks.map((block, index) => {
      const Block = blockComponents[block.blockType]
      if (!Block) return null
      return (
        <ZenEditable key={block.id ?? index} index={index} blockType={block.blockType}>
          <Block {...block} />
        </ZenEditable>
      )
    })}
  </>
)

API

zenEditorPlugin(config) — server

| Option | Type | Description | |---|---|---| | collections | Record<string, { blocksField: string }> | Map of collection slugs to their config. blocksField is the name of the blocks array field (e.g. 'layout'). |

<ZenEditable> — client

| Prop | Type | Description | |---|---|---| | index | number | Zero-based position of this block in the blocks array. | | blockType | string | The block type string (matches block.blockType). | | children | ReactNode | Your rendered block markup. |

<ZenEditorBridge /> — client

No props. Mount once in the root layout.

useZenMode() — client hook

Returns { mode: 'inactive' | 'active' }. Can be used to conditionally hide UI elements that should not appear inside the editor preview.

import { useZenMode } from '@bespokecode/payloadcms-zen-editor/client'

export function Navigation() {
  const { mode } = useZenMode()
  if (mode === 'active') return null
  return <nav>...</nav>
}

Workspace / monorepo usage

When consumed as a pnpm workspace package the top-level exports in package.json point directly to the TypeScript source (./src/...). Your Next.js bundler compiles it on-the-fly — no build step needed during development.

At publish time pnpm applies the publishConfig.exports overrides, which point to the compiled dist/ output. This dual-state setup means workspace consumers always get the latest source while npm consumers get a pre-compiled package.

Note: publishConfig.exports override is supported by pnpm 9+ and npm 7+. If you are using an older package manager you may need to swap the exports field manually before publishing.

License

MIT — see LICENSE.