Skip to content

Repository files navigation

Payload Visual Blocks

Visual page builder for Payload CMS with thumbnail block picker

npm version License: MIT TypeScript Payload CMS

FeaturesInstallationUsageBlocksCustom BlocksConfiguration


✨ Features

  • Visual Block Picker — Browse and select blocks with thumbnail previews
  • Category-Based Organization — Blocks organized by type: Heroes, Features, CTAs, Testimonials, Pricing, and more
  • Pre-built Modern UI Blocks — Production-ready components styled with shadcn/ui design patterns
  • Error Isolation — Graceful fallbacks for all operations
  • Tailwind CSS v4 — Modern styling with CSS variables for easy theming
  • WCAG 2.1 AA Accessible — Semantic HTML, ARIA labels, and keyboard navigation

Installation

Option A: Zero-Config Install (Recommended)

Run our automated installation script to install the package, configure Payload, set up Tailwind CSS, and automatically integrate the frontend renderer.

The script will:

  1. Install the package
  2. Add the plugin to payload.config.ts
  3. Add the plugin paths to tailwind.config.js
  4. Auto-Integrate:
    • Updates src/components/RenderBlocks.tsx (or similar) to use the VisualBlocksRenderer with Hybrid Rendering (Server Components for your blocks, Client Components for visual blocks).
    • Injects the necessary import into your page.tsx.

Mac/Linux:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Exudius/payload-plugin-visual-blocks/main/bin/install.sh)"

Windows (PowerShell):

irm https://raw.githubusercontent.com/Exudius/payload-plugin-visual-blocks/main/bin/install.ps1 | iex

Option B: Manual Installation

1. Install the package

# npm
npm install payload-plugin-visual-blocks

2. Configure Tailwind CSS

The plugin uses Tailwind CSS. You need to add the plugin's paths to your tailwind.config.js so the styles are generated correctly.

// tailwind.config.js
module.exports = {
  content: [
    // ... your existing content paths
    './node_modules/payload-plugin-visual-blocks/dist/**/*.{js,jsx,ts,tsx}', // Add this line
  ],
  // ...
}

3. Add to Payload Config

Import and add the plugin to your payload.config.ts.

// payload.config.ts
import { buildConfig } from 'payload'
import { visualBlocks } from 'payload-plugin-visual-blocks'

export default buildConfig({
  // ...
  plugins: [
    visualBlocks({
      // Apply to specific collections (optional, defaults to all with blocks fields)
      collections: ['pages'],
    }),
  ],
})

Usage

To render the blocks on your frontend (Next.js App Router), you need to use the VisualBlocksRenderer component.

Automatic Integration

If you used the Zero-Config Install script, your src/components/RenderBlocks.tsx should already be updated to use the plugin with Hybrid Rendering. The script creates a backup (.bak) before making changes.

It also attempts to inject the component into your page.tsx. If that step was skipped (for safety), you just need to add <RenderBlocks blocks={layout} /> to your page JSX.

Manual Integration

If the automated script couldn't update your file, or if you prefer to do it manually, follow these steps:

  1. Open your RenderBlocks.tsx (usually in src/components/).

  2. Import the renderer:

    import { VisualBlocksRenderer } from 'payload-plugin-visual-blocks/client'
  3. Update the component to use Hybrid Rendering (Server Components for your blocks, Client Components for visual blocks):

    export const RenderBlocks: React.FC<any> = (props) => {
      const { blocks } = props
    
      if (!blocks || !Array.isArray(blocks) || blocks.length === 0) {
        return null
      }
    
      return (
        <>
          {blocks.map((block, index) => {
            const { blockType } = block
    
            // 1. Render your existing Server Components directly
            if (blockType && blockType in blockComponents) {
               const Block = blockComponents[blockType]
               return <Block key={index} {...block} />
            }
    
            // 2. Delegate to VisualBlocksRenderer for plugin blocks (Client Component)
            return (
              <VisualBlocksRenderer
                key={index}
                blocks={[block]}
              />
            )
          })}
        </>
      )
    }
  4. Render Blocks in Page:

    Finally, import and use your updated RenderBlocks component in your page.tsx.

    // src/app/(frontend)/[slug]/page.tsx
    import { RenderBlocks } from '@/components/RenderBlocks'
    
    export default async function Page({ params }) {
      // ... fetch your page data
    
      return (
        <main>
          {/* Pass the layout/blocks field to the renderer */}
          <RenderBlocks blocks={page.layout} />
        </main>
      )
    }

Customizing the Renderer

The VisualBlocksRenderer accepts additional props for customization:

<VisualBlocksRenderer
  blocks={pageData.layout}
  // Add your own custom blocks
  customBlocks={{
    'my-custom-block': MyCustomBlockComponent,
  }}
  // Custom class for the wrapper
  className="my-page-content"
  // Custom class for each block wrapper
  blockClassName="my-12"
/>

Shipped Blocks

The plugin ships with production-ready blocks organized into categories:

Heroes

Slug Description
hero-01 Centered layout with badge, headline, dual CTAs, and stats row
hero-02 50/50 split layout with text on left, image on right
hero-03 Full-width background image with gradient overlay

Features

Slug Description
features-01 3-column grid with icons and descriptions
features-02 Modern bento grid layout with varied card sizes
features-03 Alternating left/right image-text sections

Call-to-Action (CTA)

Slug Description
cta-01 Compact gradient banner with inline form
cta-02 Card-style CTA with supporting image

Testimonials

Slug Description
testimonials-01 Single featured quote with star rating and avatar
testimonials-02 Multiple testimonials with carousel navigation

Pricing

Slug Description
pricing-01 3-tier pricing table with monthly/yearly toggle

🛠 Adding Custom Blocks

You can easily create your own blocks that integrate with the visual picker using the createBlock helper.

1. Create the Block

// src/blocks/MyCustomBlock.tsx
import React from 'react'
import { createBlock } from 'payload-plugin-visual-blocks'

interface MyBlockProps {
  title: string
  content: string
}

export const MyCustomBlock = createBlock<MyBlockProps>({
  config: {
    slug: 'my-custom-block',
    interfaceName: 'MyCustomBlock',
    labels: { singular: 'My Block', plural: 'My Blocks' },
    fields: [
      { name: 'title', type: 'text', required: true },
      { name: 'content', type: 'textarea' },
    ],
  },
  Component: ({ title, content }) => (
    <div className="p-8 bg-white rounded-lg shadow">
      <h2 className="text-2xl font-bold">{title}</h2>
      <p className="mt-2 text-gray-600">{content}</p>
    </div>
  ),
})

2. Register the Block

// payload.config.ts
import { visualBlocks } from 'payload-plugin-visual-blocks'
import { MyCustomBlock } from './blocks/MyCustomBlock'

export default buildConfig({
  plugins: [
    visualBlocks({
      additionalBlocks: [MyCustomBlock.config],
    }),
  ],
})

3. Render the Block

Add it to your VisualBlocksRenderer via the customBlocks prop.

<VisualBlocksRenderer
  blocks={blocks}
  customBlocks={{
    'my-custom-block': MyCustomBlock.Component
  }}
/>

Configuration

Plugin Options

interface VisualBlocksOptions {
  /**
   * Collections to apply the visual block picker to.
   * @default [] (Applies to all collections with blocks fields)
   */
  collections?: string[]

  /**
   * Include built-in shadcn blocks automatically.
   * @default true
   */
  includeBuiltInBlocks?: boolean

  /**
   * Additional custom blocks to inject.
   * @default []
   */
  additionalBlocks?: Block[]
}

Contributing

We welcome contributions! Please see our Contributing Guide for details on how to:

  • Set up your development environment
  • Add new blocks
  • Run tests
  • Publish updates (for maintainers)

License

MIT © Innoveapp.com

About

Elevate your Payload CMS editor experience. This plugin provides a massive quality-of-life improvement by adding a block selector with a visually rich, categorized thumbnail gallery. Make content creation faster and more intuitive for your clients. Includes ready-to-use blocks built with modern shadcn/ui patterns.

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages