# How Blocks Work Blocks are Puck visual components that merchants can add and customize in the editor. Each block defines: 1. **Editor configuration** - UI controls for customization 2. **Render function** - How to display the block on the storefront 3. **Category** - Where it appears in the editor sidebar ## Simple Block Example ```tsx // blocks/hero.puck.tsx import { type ComponentConfig } from '@puckeditor/core'; interface HeroProps { title: string; subtitle: string; backgroundImage?: string; } export const category = 'Marketing'; export const config: ComponentConfig = { label: 'Hero Banner', fields: { title: { type: 'text', label: 'Title', }, subtitle: { type: 'textarea', label: 'Subtitle', }, backgroundImage: { type: 'text', label: 'Background Image URL (optional)', }, }, defaultProps: { title: 'Welcome to our store', subtitle: 'Discover amazing products', }, render: ({ title, subtitle, backgroundImage }) => (

{title}

{subtitle}

), }; ``` ## Complex Block with Editor & Render Separation For blocks that need interactive features in the editor (like product selection), split them into two files: ``` blocks/product-grid/ ├── product-grid.edit.puck.tsx # Editor mode with interactivity ├── product-grid.render.puck.tsx # Render mode with data fetching ├── shared.ts # Shared utilities └── index.ts # Exports ``` ### Edit Mode (`*.edit.puck.tsx`) Edit mode runs in the editor UI (`'use client'` component): - Runs in the editor UI - Can have interactive features (pickers, dialogs) - Stores lightweight data (IDs, not full objects) - Has access to editor context ```tsx 'use client'; import { type ComponentConfig } from '@puckeditor/core'; interface ProductGridProps { selectedProductIds?: number[]; // Lightweight data selectedProducts?: Product[]; // For preview title: string; } export const config: ComponentConfig = { label: 'Product Grid', fields: { selectedProductIds: { type: 'custom', render: ({ value, onChange }) => , }, title: { type: 'text', label: 'Section Title', }, }, defaultProps: { selectedProductIds: [], title: 'Featured Products', }, render: ({ selectedProductIds, title }) => { return ; }, }; ``` ### Render Mode (`*.render.puck.tsx`) Render mode runs on the published storefront (Server component): - Runs on the published storefront - Fetches fresh data from the API - Uses Suspense for streaming - No interactive features ```tsx // Server component by default import { Suspense } from 'react'; import { type ComponentConfig } from '@puckeditor/core'; interface ProductGridProps { selectedProductIds?: number[]; title: string; } export const config: ComponentConfig = { label: 'Product Grid', defaultProps: { selectedProductIds: [], title: 'Featured Products', }, render: ({ selectedProductIds, title }) => ( }> ), }; ``` ## Block Field Types The `fields` object in block config defines customizable properties: ```tsx fields: { // Text input title: { type: 'text', label: 'Title', }, // Multi-line text description: { type: 'textarea', label: 'Description', }, // Select dropdown alignment: { type: 'radio', label: 'Text Alignment', options: [ { label: 'Left', value: 'left' }, { label: 'Center', value: 'center' }, { label: 'Right', value: 'right' }, ], }, // Number input columns: { type: 'number', label: 'Grid Columns', }, // Custom component field products: { type: 'custom', render: ({ value, onChange }) => ( ), }, } ``` ## Auto-Generated Puck Config The `.storefront/` directory contains auto-generated Puck editor configurations: - **`.storefront/puck.edit.config.tsx`** - Editor configuration (all blocks in edit mode) - **`.storefront/puck.render.config.tsx`** - Render configuration (all blocks in render mode) **⚠️ Never edit these files manually.** They're regenerated by the build process. To regenerate them: ```bash pnpm dev # Runs finqu storefront dev --components=./blocks ``` The CLI scans the `blocks/` directory for files matching `*.puck.tsx` and generates the configs automatically. ## Building Custom Blocks ### Block Checklist - [ ] Create file in `blocks/` matching pattern `*.puck.tsx` - [ ] Export `category` string - [ ] Export `config: ComponentConfig` - [ ] Define `fields` for editor controls - [ ] Provide `defaultProps` - [ ] Implement `render` function - [ ] Rebuild to generate `.storefront/` configs ### Best Practices 1. **Keep blocks focused** - One responsibility per block 2. **Store lightweight data** - Use IDs instead of full objects 3. **Use Suspense in render mode** - For streaming and loading states 4. **Provide good defaults** - Make blocks usable immediately 5. **Add helpful labels** - Make editor UI self-documenting 6. **Test in the editor** - Verify behavior at different viewports ## Next Steps - [Visual Editing](./visual-editing) - Use blocks in the editor - [Templates](./templates) - Use blocks in templates - [Data Fetching](./data-fetching) - Fetch data in blocks