<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Puck</title>
    <description>The latest articles on DEV Community by Puck (@puckeditor).</description>
    <link>https://dev.to/puckeditor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F10097%2F188f3367-b6ae-4526-b765-576622cf29c2.jpg</url>
      <title>DEV Community: Puck</title>
      <link>https://dev.to/puckeditor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/puckeditor"/>
    <language>en</language>
    <item>
      <title>Building a plugin for a React visual editor with Puck</title>
      <dc:creator>Astrodevil</dc:creator>
      <pubDate>Thu, 12 Mar 2026 16:38:54 +0000</pubDate>
      <link>https://dev.to/puckeditor/building-a-plugin-for-a-react-visual-editor-with-puck-4igh</link>
      <guid>https://dev.to/puckeditor/building-a-plugin-for-a-react-visual-editor-with-puck-4igh</guid>
      <description>&lt;p&gt;Page builders and visual editors have become central to modern product development. Frontend engineers, product teams, and content operations groups rely on them to build landing pages, dashboards, documentation systems, and internal tools quickly and consistently.&lt;/p&gt;

&lt;p&gt;As adoption grows, expectations grow with it. Editors must support customization, structured content, automation, and domain-specific workflows without increasing development and maintenance overhead.&lt;/p&gt;

&lt;p&gt;The market reflects this demand. The global low-code development platform market is projected to reach &lt;a href="https://www.psmarketresearch.com/market-analysis/low-code-development-platform-market" rel="noopener noreferrer"&gt;$167 billion by 2030&lt;/a&gt;. Organizations continue to invest in visual development systems that enable faster iteration and broader collaboration. As these systems expand in scope and adoption, the architectural responsibility placed on editor frameworks also increases.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gl7a82j7wd95bl9e7jd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5gl7a82j7wd95bl9e7jd.png" alt="Low-code development platform market" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Growth introduces complexity. Every feature added directly to the core increases surface area and long-term maintenance costs. As teams introduce metadata panels, validation rules, workflow controls, and UI extensions, the editor becomes tightly coupled and difficult to evolve without clear extension boundaries.&lt;/p&gt;

&lt;p&gt;Plugin systems create those boundaries. They define controlled integration points, isolate functionality, and protect the editor’s foundation.&lt;/p&gt;

&lt;p&gt;In this article, we examine how to design plugin systems for visual editors and build a working example using &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;Puck&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is a Plugin System: Architecture Deep Dive
&lt;/h2&gt;

&lt;p&gt;A plugin system defines a controlled way to extend software without modifying its core. It exposes extension points through a stable contract and allows external modules to register new behavior, UI, or logic.&lt;/p&gt;

&lt;p&gt;The core remains responsible for orchestration, lifecycle management, and state ownership. Plugins operate within boundaries defined by that core.&lt;/p&gt;

&lt;p&gt;At an architectural level, a plugin system introduces three primary layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Core Engine&lt;/strong&gt;: Owns state, rendering, persistence, and lifecycle management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Extension API (Plugin Contract)&lt;/strong&gt;: Defines how plugins register, what hooks they can access, and what capabilities they receive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plugin Modules&lt;/strong&gt;: Independent units that implement features through the exposed contract.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qq6p55e5zljf5yg8x06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qq6p55e5zljf5yg8x06.png" alt="Architecture" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This separation enforces control. The core decides when a plugin runs, where it renders, and what data it can access. The plugin does not directly mutate internal systems. It communicates through defined interfaces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Architectural Properties
&lt;/h3&gt;

&lt;p&gt;A well-designed plugin system should provide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Isolation&lt;/strong&gt;: Plugins cannot corrupt the global state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic Lifecycle&lt;/strong&gt;: Mount, update, and unmount phases are predictable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit Extension Points&lt;/strong&gt;: UI slots, event hooks, and state access are intentional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encapsulation&lt;/strong&gt;: Editor state and internal systems remain protected behind defined APIs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composable Registration&lt;/strong&gt;: Multiple plugins can coexist without conflict.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture scales by allowing new capabilities to be introduced without changing the foundation. Features can be enabled or removed independently, while the core remains lean, stable, and protected.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use Plugins vs Core Features
&lt;/h2&gt;

&lt;p&gt;Extensible editors require deciding which capabilities belong in the core and which should be implemented as plugins. Core features define the editor’s fundamental architecture, such as rendering, state management, and persistence. Plugins extend the editor through defined extension points without modifying that foundation.&lt;/p&gt;

&lt;p&gt;The distinction becomes clearer when viewed side by side.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0mb6ss1hz7d82ln9r1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0mb6ss1hz7d82ln9r1y.png" alt="distinction becomes clearer when viewed side by side in table" width="729" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Puck Implements the Plugin Contract
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is a React-based page builder that lets users create pages by dragging and dropping their own components. Developers register these components through configuration objects, and the editor uses them to build and render pages. Puck is fully customizable and extensible, providing APIs to extend editor behavior or package additional functionality as plugins.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtwjna3l3efuynewr2f7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frtwjna3l3efuynewr2f7.png" alt="Puck Editor Interface" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plugin integration in Puck is straightforward. Plugins can register sidebar panels, add controls, and respond to editor events through clear extension surfaces. They interact with the editor state through documented APIs without reaching into internal systems or modifying rendering logic directly.&lt;/p&gt;

&lt;p&gt;The plugin contract in Puck focuses on three responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Registration&lt;/strong&gt;: A plugin declares its identity and attaches to the editor during initialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI Injection&lt;/strong&gt;: The plugin connects to defined surfaces such as sidebars or inspector regions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lifecycle Participation&lt;/strong&gt;: Plugins can hook into editor behavior such as loading, saving, or validation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once registered, the plugin runs as part of your core editor integration. This keeps the editor implementation stable while allowing additional functionality to be added through independent plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an Author Info Plugin using Puck
&lt;/h2&gt;

&lt;p&gt;We will build a simple Author Info plugin that demonstrates plugin registration, UI injection, and lifecycle participation inside Puck. The plugin will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a panel to the left sidebar&lt;/li&gt;
&lt;li&gt;Capture author name, role, and avatar&lt;/li&gt;
&lt;li&gt;Store this data alongside the page state&lt;/li&gt;
&lt;li&gt;Validate the metadata before publishing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Create a New Puck App
&lt;/h3&gt;

&lt;p&gt;Start by generating a new app using the official &lt;a href="https://github.com/puckeditor/puck?tab=readme-ov-file#recipes?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;Puck starter&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-puck-app author-info-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose the &lt;strong&gt;Next.js&lt;/strong&gt; option when prompted. After the setup completes, navigate to the project directory and run the application in development mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;author-info-plugin
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F471ooabis96z54hqwdrr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F471ooabis96z54hqwdrr.png" alt="Terminal" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open your browser at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000/edit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see the Puck editor interface for the homepage.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Understand the Plugin API
&lt;/h3&gt;

&lt;p&gt;Puck plugins can extend the editor interface through the &lt;a href="https://puckeditor.com/docs/extending-puck/plugins#rendering-ui-in-the-plugin-rail?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;Plugin Rail&lt;/a&gt; on the left. Plugins may render UI in this rail, but they can also extend editor behavior through &lt;a href="https://puckeditor.com/docs/extending-puck/ui-overrides?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;overrides&lt;/a&gt; and other integrations.&lt;/p&gt;

&lt;p&gt;A plugin object looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;my-plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Plugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Icon&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;My UI&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plugins are wired into the editor by passing them to the &lt;code&gt;plugins&lt;/code&gt; prop of the &lt;a href="https://puckeditor.com/docs/api-reference/components/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;Puck /&amp;gt;&lt;/code&gt;&lt;/a&gt; component. &lt;/p&gt;

&lt;h3&gt;
  
  
  3. Create Your Plugin File
&lt;/h3&gt;

&lt;p&gt;Inside your project, create a file at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;app/puck/plugins/AuthorInfoPlugin.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can create it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; app/puck/plugins
&lt;span class="nb"&gt;touch &lt;/span&gt;app/puck/plugins/AuthorInfoPlugin.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since this example uses icons from &lt;strong&gt;lucide-react&lt;/strong&gt;, install it first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;lucide-react
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open the file you created and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createUsePuck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Plugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lucide-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usePuck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUsePuck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AuthorInfoPlugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Plugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author-info&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Author Info&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;avatar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateAuthor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replaceRoot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Author Info&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Author Name"&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateAuthor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Author Role"&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateAuthor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Avatar URL"&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateAuthor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;avatar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This plugin renders a component that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reads the current page data via &lt;a href="https://puckeditor.com/docs/extending-puck/internal-puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;&lt;code&gt;usePuck&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Updates author metadata via &lt;a href="https://puckeditor.com/docs/api-reference/puck-api#dispatchaction?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;Puck’s dispatcher&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This demonstrates how plugins can integrate with the editor state.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Register the Plugin
&lt;/h3&gt;

&lt;p&gt;Open the route where the editor is rendered:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;puckPath&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the &lt;code&gt;&amp;lt;Puck /&amp;gt;&lt;/code&gt; component and update the &lt;code&gt;plugins&lt;/code&gt; prop:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AuthorInfoPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../plugins/AuthorInfoPlugin&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;onPublish&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/pages&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AuthorInfoPlugin&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your plugin will appear in the Plugin Rail.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Test the Plugin UI
&lt;/h3&gt;

&lt;p&gt;Reload the editor at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://localhost:3000/edit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the left Plugin Rail:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click &lt;strong&gt;Author Info&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter a name, role, and avatar URL in the fields&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Publish&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because of your &lt;code&gt;onPublish&lt;/code&gt; Implementation: the data will be sent to &lt;code&gt;/api/pages&lt;/code&gt; and saved to &lt;code&gt;database.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyx5hgsusmzp1qajpyx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzyx5hgsusmzp1qajpyx9.png" alt="Plugin UI" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Confirm Persistence
&lt;/h3&gt;

&lt;p&gt;After publishing, open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;database.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"root"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"author"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Your Author"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Writer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"avatar"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/avatar.png"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"zones"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This confirms the author data was correctly persisted.&lt;/p&gt;

&lt;p&gt;Note: In the starter project, this data is saved to a local file, but in a real application, &lt;a href="https://puckeditor.com/docs/api-reference/components/puck#onpublishdata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;it could be stored in any backend&lt;/a&gt;, such as a database or API service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c6e8ldl0eqe9zsyd72u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9c6e8ldl0eqe9zsyd72u.png" alt="Database json will appear like this" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  7. (Optional) Render Author Info on the Frontend
&lt;/h3&gt;

&lt;p&gt;To display author metadata on the frontend, update the page that renders your content. For example, open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;puckPath&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tsx&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the following below the &lt;code&gt;&amp;lt;Render /&amp;gt;&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Render&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Author&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;strong&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;avatar&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Avatar"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flm0ypemu7gc97uxf4ssc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flm0ypemu7gc97uxf4ssc.png" alt="Final demo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also place that in the &lt;a href="https://puckeditor.com/docs/integrating-puck/root-configuration#the-root-render-function?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;root configuration&lt;/a&gt; to display the saved metadata on published pages.&lt;/p&gt;

&lt;p&gt;That’s it. You now have a fully working &lt;strong&gt;Author Info plugin&lt;/strong&gt; that integrates with the editor state, renders a sidebar panel in the Plugin Rail, and persists metadata through the publishing flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Plugin systems give visual editors a structured path for growth. With clear contracts and defined extension points, teams can introduce new capabilities without reshaping the underlying architecture. This keeps responsibilities separated, reduces risk, and allows the platform to adapt as product requirements expand.&lt;/p&gt;

&lt;p&gt;The Author Info plugin we built using &lt;a href="https://puckeditor.com?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; shows how an extension can be registered independently, integrate with the editor state, and persist structured metadata. The core remains stable while the plugin delivers focused functionality, demonstrating how modular design supports scalable and maintainable editor systems.&lt;/p&gt;

&lt;p&gt;For deeper implementation details, refer to the official documentation: &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=building_plugins_with_puck" rel="noopener noreferrer"&gt;https://puckeditor.com/docs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>AI Slop vs Constrained UI: Why Most Generative Interfaces Fail</title>
      <dc:creator>Astrodevil</dc:creator>
      <pubDate>Tue, 24 Feb 2026 11:52:06 +0000</pubDate>
      <link>https://dev.to/puckeditor/ai-slop-vs-constrained-ui-why-most-generative-interfaces-fail-pm9</link>
      <guid>https://dev.to/puckeditor/ai-slop-vs-constrained-ui-why-most-generative-interfaces-fail-pm9</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;p&gt;AI can generate structured interface layouts and assemble component hierarchies from natural language prompts. When generation is unconstrained, the output diverges from design systems, lacks determinism, and requires downstream refactoring before deployment. Production-grade generative UI requires predefined component registries, validated schemas, and explicit architectural constraints, which is the model implemented by &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; and its constrained generation layer, &lt;a href="https://puckeditor.com/docs/ai/overview?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;Puck AI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faws6m1ko2u1fc2f41li8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faws6m1ko2u1fc2f41li8.png" alt="Puck AI" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;AI systems can now generate complete interface layouts from natural language prompts. Tasks that previously required manual section planning, component selection, and layout composition can now start from a single instruction.&lt;/p&gt;

&lt;p&gt;Yet when these generated interfaces are evaluated against real production standards, limitations become clear. Outputs often conflict with established design systems, introduce structural inconsistencies, and produce layouts that require refactoring before integration.&lt;/p&gt;

&lt;p&gt;What appears efficient at the prompt layer frequently shifts complexity downstream into engineering workflows. This gap between demo output and deployable software has led to growing skepticism around generative UI, sometimes informally labeled as “AI slop” to describe output that appears complete but fails architectural validation.&lt;/p&gt;

&lt;p&gt;In this article, we will examine where AI meaningfully supports interface generation, where it breaks down in production environments, and why constrained, schema-driven systems are essential for making generative UI operational at scale.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI Is Actually Good At in UI Workflows
&lt;/h2&gt;

&lt;p&gt;Before evaluating the limitations of generative UI, it is important to isolate where it provides measurable value. AI is effective at accelerating early-stage interface assembly, particularly when the objective is to convert high-level intent into an initial layout draft.&lt;/p&gt;

&lt;p&gt;Consider a product team building a new SaaS landing page. A prompt such as “Create a landing page for an AI analytics platform with a hero section, feature highlights, pricing tiers, and customer testimonials” can reliably produce a logically organized page structure within seconds. The output may not be production-ready, but it establishes a usable structural baseline.&lt;/p&gt;

&lt;p&gt;AI performs well in the following areas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k4rvau8ysli5k5xcaki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k4rvau8ysli5k5xcaki.png" alt="UI workflows" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Translating Intent into Layout Structure&lt;/strong&gt;: AI can map abstract, well-known requirements into recognizable interface patterns. For example, it understands that a landing page typically includes a hero, feature highlights, social proof, and a call to action. This reduces the cognitive load of structuring the page from scratch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generating First-Pass Scaffolding&lt;/strong&gt;: AI can assemble a preliminary hierarchy of sections and placeholder content. This enables teams to visualize information architecture quickly before investing time in refinement.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Automating Repetitive Component Assembly&lt;/strong&gt;: When working with predefined components, AI can configure repeated structures such as feature cards, pricing tiers, or testimonial blocks with consistent prop patterns. This is particularly useful in systems with modular design libraries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Supporting Rapid Experimentation&lt;/strong&gt;: AI allows teams to generate multiple layout variations in minutes, enabling faster exploration of structural alternatives without manual reconfiguration.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;AI is therefore effective at structural acceleration, particularly in early-stage development where system rules, design boundaries, and composition patterns are still being defined.&lt;/p&gt;

&lt;p&gt;In established product environments, however, structural acceleration must operate within existing architectural constraints to remain viable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Unbounded UI Generation Breaks Down
&lt;/h2&gt;

&lt;p&gt;Many unbounded generation tools produce raw code that must be reviewed, integrated, and redeployed before it can be used in production. Even when the output appears complete, it is not directly executable within an existing system. This shifts responsibility to engineering teams and introduces friction into workflows that are intended to be self-service.&lt;/p&gt;

&lt;p&gt;For non-technical users such as marketers or content authors, this model is impractical. Page updates require developer involvement, slowing content publishing and limiting autonomy. Instead of enabling cross-functional workflows, generative UI becomes a developer-only tool.&lt;/p&gt;

&lt;p&gt;Below are a few additional architectural limitations to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Design System Violations&lt;/strong&gt;: Unbounded generation does not inherently respect spacing scales, typography tokens, or component composition rules. It may introduce arbitrary margins, inconsistent heading hierarchies, or layout patterns that are not part of the approved system. Even if visually acceptable, these deviations fragment the design language and undermine maintainability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inconsistent Component Usage&lt;/strong&gt;: In systems with established component libraries, specific components are intended for specific contexts. Free-form generation may misuse primitives, duplicate existing abstractions, or bypass higher-level components entirely. This creates parallel patterns that increase technical debt and weaken reuse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Non-Deterministic Outputs&lt;/strong&gt;: Identical prompts can yield structurally different layouts across executions. In production environments, this lack of determinism complicates testing, review processes, and content governance. Predictability is a requirement for scalable systems.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Brand and Compliance Drift&lt;/strong&gt;: Without embedded context, models default to generic language and layout conventions. They lack awareness of regulatory constraints, accessibility standards, and brand-specific positioning. This introduces risk in industries where messaging and structure must adhere to policy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Output Requiring Engineering Cleanup&lt;/strong&gt;: Generated code or markup frequently requires normalization before integration. Engineers must refactor styles, align components with existing abstractions, and correct structural inconsistencies. The perceived acceleration at generation time is offset by downstream rework.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Free-form UI generation, therefore, conflicts with production architecture. Systems designed for reliability, reuse, and governance require structured constraints, not unconstrained synthesis.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Missing Layer: Constraints
&lt;/h2&gt;

&lt;p&gt;In this context, constraints define what can be generated, how it can be configured, how components can be composed, and how the result is represented at runtime. In real systems, those constraints are implemented through concrete mechanisms like component registry boundaries, schema validation, composition rules, and structured runtime output.&lt;/p&gt;

&lt;p&gt;Component registry boundaries limit generation to approved React components. Instead of synthesizing arbitrary markup, the model assembles interfaces from predefined primitives such as &lt;code&gt;Hero&lt;/code&gt;, &lt;code&gt;FeatureGrid&lt;/code&gt;, or &lt;code&gt;PricingTable&lt;/code&gt;. Prop schema enforcement validates inputs against typed definitions, ensuring that required fields, enumerations, and data shapes conform to system expectations. For example, a pricing component may require a structured array of tiers with defined attributes rather than free-form content.&lt;/p&gt;

&lt;p&gt;Layout rules and composition limits restrict how components can be nested, preventing structurally invalid trees. Business context injection embeds brand, regulatory, or domain constraints directly into the generation process. Deterministic output structures, typically expressed as structured JSON, ensure predictable rendering and traceable state transitions across environments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj060lzeprx47gh3il62d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj060lzeprx47gh3il62d.png" alt="Missing Layer: Constraints" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How Puck AI Implements Architectural Constraints
&lt;/h2&gt;

&lt;p&gt;With Puck and Puck AI, the constraints we discussed above are implemented and enforced at the system level rather than inferred at prompt time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/ai/getting-started?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;Puck AI is a generative UI layer&lt;/a&gt; built on top of Puck’s React visual editor that enables page generation within predefined architectural boundaries.&lt;/p&gt;

&lt;p&gt;It operates by assembling interfaces exclusively from registered React components instead of generating code. When a user requests something like “a landing page for an AI analytics platform,” the system composes that page from the components already configured in the application. The result is a deterministic component tree interpreted by Puck at runtime, keeping everything aligned with the existing design system and application logic.&lt;/p&gt;

&lt;p&gt;In this workflow, generation is an orchestration process over defined primitives. The AI behavior is shaped by the editor configuration and the components supplied by the development team.&lt;/p&gt;


  
  Your browser does not support the video tag.


&lt;h3&gt;
  
  
  Puck AI Characteristics
&lt;/h3&gt;

&lt;p&gt;Puck AI demonstrates bounded generation through the following characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Generation from Registered React Components&lt;/strong&gt;: The AI selects and composes only those components explicitly registered in the Puck &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;configuration&lt;/a&gt;. If the system exposes Hero, FeatureGrid, and PricingTable, those are the only structural primitives available. No new layout elements are introduced outside the approved library.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structured Page Schema Output&lt;/strong&gt;: The result of the generation is a &lt;a href="https://puckeditor.com/docs/api-reference/data-model/data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;structured page definition&lt;/a&gt; that maps component types to their configured props and hierarchical placement. This schema is interpreted by Puck at runtime to render the interface. The AI does not directly control rendering logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://puckeditor.com/docs/ai/business-context?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;Business Context and Brand Rules&lt;/a&gt;&lt;/strong&gt;: Configuration layers allow teams to define tone, domain context, and structural expectations. These parameters influence how sections are assembled and how content fields are populated, ensuring alignment with product positioning and organizational standards.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Design System Preservation&lt;/strong&gt;: Because rendering occurs through predefined components, spacing, typography, and layout behavior remain governed by the existing design system. Visual consistency is enforced by the component implementation, not by prompt phrasing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic Behavior via Configuration&lt;/strong&gt;: Through controlled configuration of available components, fields, and generation parameters, teams can narrow variability in output. The same structural intent produces predictable component trees aligned with system rules.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Decision Framework: When AI Should and Shouldn’t Generate UI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3cb37ejytdez0297gb9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft3cb37ejytdez0297gb9.png" alt="When AI Should and Shouldn’t Generate UI" width="731" height="682"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;Generative UI is effective when it operates within defined architectural boundaries and established component systems. When generation bypasses those constraints, inconsistency and downstream rework become unavoidable. Production-grade outcomes require schema enforcement, controlled composition, and system-level governance.&lt;/p&gt;

&lt;p&gt;To see this model in practice, &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;explore Puck&lt;/a&gt; for structured visual editing and &lt;a href="https://cloud.puckeditor.com/sign-up?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai_slop_vs_constrained_ui" rel="noopener noreferrer"&gt;try Puck AI&lt;/a&gt; for constrained page generation workflows. Puck AI is currently available in beta for teams evaluating controlled generative UI in production environments.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Top AI Libraries for React Developers in 2026</title>
      <dc:creator>Astrodevil</dc:creator>
      <pubDate>Mon, 09 Feb 2026 14:00:48 +0000</pubDate>
      <link>https://dev.to/puckeditor/top-ai-libraries-for-react-developers-in-2026-nmb</link>
      <guid>https://dev.to/puckeditor/top-ai-libraries-for-react-developers-in-2026-nmb</guid>
      <description>&lt;h3&gt;
  
  
  TL;DR
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;AI libraries used in React apps serve different purposes. Some run directly in the browser, while others work through backend or API-based workflows.&lt;/li&gt;
&lt;li&gt;Many AI tools focus on specific problems like UI integration, data retrieval, or structured content generation, not just chat interfaces or code generation.&lt;/li&gt;
&lt;li&gt;Choosing the right AI library comes down to understanding where AI fits into your React app and what role it needs to play.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, let us explore 8 key AI libraries and tools React developers use in 2026, grouped by how they fit into real-world React workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Where AI Fits in a React Application
&lt;/h2&gt;

&lt;p&gt;AI tools for React are not all designed to solve the same problem. Some run directly in the browser, others live in backend services, and some operate at the UI layer. Understanding where an AI library fits in your application helps you choose tools that work well with React instead of fighting against it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client-side machine learning&lt;/strong&gt;: These libraries run directly in the browser and handle tasks like image recognition or simple predictions. They reduce server dependency and can improve privacy, but they are limited by device performance and browser resources.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM and AI backends&lt;/strong&gt;: These tools handle large language models, agents, and data-heavy workflows. They usually run on the server and are accessed from React through APIs. This layer is best suited for reasoning, retrieval, and complex AI logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UI and content generation&lt;/strong&gt;: Some AI tools focus on generating or assisting with user-facing content and interfaces. These tools work closely with React components and state, making them useful for editors, design systems, and structured UI workflows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why AI Tooling Works Better in React Today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Edge runtimes&lt;/strong&gt; make it easier to run AI logic closer to users with lower latency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming UI&lt;/strong&gt; allows React apps to display AI responses progressively instead of waiting for full results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full-stack React frameworks&lt;/strong&gt; simplify connecting frontend components with backend AI services.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Top 8 AI Libraries for React Developers
&lt;/h2&gt;

&lt;p&gt;Below are the top 8 AI libraries for React developers in 2026:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Puck AI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fekqkpn6mh93idep52c3i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fekqkpn6mh93idep52c3i.png" alt="Puck AI" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;Puck AI&lt;/a&gt; lets you create an AI page builder for non-technical end users to generate landing pages from a predefined set of components. It produces predictable, production-ready pages instead of raw or ad hoc code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Deterministic UI generation&lt;/strong&gt;: Generates pages that conform to existing &lt;a href="https://puckeditor.com/docs/api-reference/components?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;React component definitions&lt;/a&gt; instead of producing free-form code or markup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;Configuration-driven behavior&lt;/a&gt;&lt;/strong&gt;: AI output is constrained by schemas, rules, business context, and &lt;a href="https://puckeditor.com/docs/ai/tools?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;tools&lt;/a&gt;, allowing teams to control not only what the model can generate, but also what data it can access and which actions it is allowed to perform when generating UI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Component-native integration&lt;/strong&gt;: Puck AI uses your existing &lt;a href="https://puckeditor.com/docs/integrating-puck/server-components?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;React components&lt;/a&gt; as the building blocks for AI output, enabling AI-assisted page creation that results in real, renderable UI rather than design mockups or intermediate formats.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Editor and CMS workflows&lt;/strong&gt;: Designed for visual editors, &lt;a href="https://puckeditor.com/blog/puck-ai-03?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;page builders&lt;/a&gt;, and structured content systems rather than conversational interfaces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Low-friction AI adoption&lt;/strong&gt;: Makes it possible to experiment with &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;AI-assisted UI generation&lt;/a&gt; and content creation without setting up full agent architectures, custom prompt pipelines, or model lifecycle management.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Tensorflow.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqlv77neroomk2742xcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmqlv77neroomk2742xcy.png" alt="Tensorflow" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.tensorflow.org/js" rel="noopener noreferrer"&gt;TensorFlow.js&lt;/a&gt; allows machine learning models to run directly inside the browser using JavaScript. It enables React applications to perform inference, and in some cases training, without sending data to a backend server.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Browser-based model execution&lt;/strong&gt;: Runs ML models using WebGL, WebGPU, or CPU backends, allowing inference to happen entirely on the client side.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common ML tasks&lt;/strong&gt;: Supports use cases such as image recognition, object classification, basic predictive modeling, and simple neural networks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privacy-preserving inference&lt;/strong&gt;: Since data does not need to leave the user’s device, it is well-suited for applications with strict privacy or compliance requirements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low-latency interactions&lt;/strong&gt;: Eliminates network round-trip times for inference, enabling faster responses for real-time features like camera-based recognition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript-first integration&lt;/strong&gt;: Works naturally with React and other frontend frameworks without requiring Python-based tooling.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. ML5.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvwlhb3x2i70kk9z5jf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvwlhb3x2i70kk9z5jf3.png" alt="ML5" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://ml5js.org/" rel="noopener noreferrer"&gt;ML5.js&lt;/a&gt; is a high-level JavaScript library built on top of TensorFlow.js that makes machine learning more accessible in the browser. It abstracts away low-level model handling and provides simple APIs for common ML tasks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Built on TensorFlow.js&lt;/strong&gt;: Uses TensorFlow.js under the hood while hiding most of the complexity involved in loading and running models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplified APIs&lt;/strong&gt;: Provides easy-to-use functions for common machine learning tasks without requiring deep knowledge of neural networks or model architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pretrained model support&lt;/strong&gt;: Includes ready-to-use models for vision, text, and audio tasks, allowing developers to get results quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser-first design&lt;/strong&gt;: Runs entirely in the browser and integrates smoothly with React and other frontend frameworks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fast experimentation&lt;/strong&gt;: Well-suited for trying ideas quickly without setting up backend infrastructure or ML pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. LlamaIndex.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5z08wn7wym5hnf8l4j25.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5z08wn7wym5hnf8l4j25.png" alt="LlamaIndex" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developers.llamaindex.ai/typescript/framework/" rel="noopener noreferrer"&gt;LlamaIndex.js&lt;/a&gt; is a framework focused on connecting large language models to custom data sources. It provides the tooling needed to ingest, index, and retrieve application-specific data so that AI systems can generate responses grounded in real information.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data ingestion and indexing&lt;/strong&gt;: LlamaIndex allows developers to load structured and unstructured data from files, databases, APIs, and document stores into searchable indexes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval-Augmented Generation (RAG)&lt;/strong&gt;: Instead of relying only on a model’s training data, LlamaIndex retrieves relevant context from indexed sources and injects it into prompts at query time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible data connectors&lt;/strong&gt;: Supports a variety of data formats and storage backends, making it suitable for real-world application data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LLM-agnostic design&lt;/strong&gt;: Works with multiple language model providers and can be combined with different inference backends.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend-oriented integration&lt;/strong&gt;: Typically runs on servers or API layers, with React applications consuming results via endpoints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Vercel AI SDK
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68pc310k77q465xhj8nz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F68pc310k77q465xhj8nz.png" alt="Vercel AI SDK" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://vercel.com/docs/ai-sdk" rel="noopener noreferrer"&gt;Vercel AI SDK&lt;/a&gt; is designed to help React developers build AI-powered user interfaces with streaming responses. It focuses on the frontend experience, making it easier to display partial AI output as it is generated rather than waiting for a complete response.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Streaming responses&lt;/strong&gt;: Supports token-by-token or chunked streaming from language models, allowing AI output to appear progressively in the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React and Next.js integration&lt;/strong&gt;: Provides hooks and utilities that work naturally with React components and Next.js app and server components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model-agnostic design&lt;/strong&gt;: Works with multiple AI providers, allowing developers to switch models without changing UI logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in state handling&lt;/strong&gt;: Manages loading, streaming, and completion states so developers do not need to implement custom streaming logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Edge and server compatibility&lt;/strong&gt;: Designed to work with serverless and edge runtimes commonly used in modern React deployments.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. OpenAI JavaScript SDK
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3c3jgvkmxvife6yqbzf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3c3jgvkmxvife6yqbzf4.png" alt="OpenAI Js SDK" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://platform.openai.com/docs/guides/agents-sdk" rel="noopener noreferrer"&gt;OpenAI JavaScript SDK&lt;/a&gt; provides direct programmatic access to OpenAI’s language and generative models from JavaScript and TypeScript environments. It serves as a low-level interface for applications that want full control over how models are used and integrated.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Direct model access&lt;/strong&gt;: Enables applications to call language, image, and other generative models through well-defined APIs without additional abstraction layers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No enforced architecture&lt;/strong&gt;: The SDK does not impose opinions on UI, workflows, or application structure, leaving design decisions entirely to the developer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible deployment options&lt;/strong&gt;: Commonly used in server environments, edge functions, or API routes that React applications communicate with.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fine-grained control&lt;/strong&gt;: Developers manage prompts, inputs, outputs, error handling, and retries directly, allowing precise control over model behavior.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composable with other tools&lt;/strong&gt;: Often used as a foundation beneath higher-level frameworks such as LangChain, LlamaIndex, or custom orchestration logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Brain.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s9j8yxqk2qbzfl84y0o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2s9j8yxqk2qbzfl84y0o.png" alt="Brain Js" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/brainjs" rel="noopener noreferrer"&gt;Brain.js&lt;/a&gt; is a lightweight JavaScript library for building and running neural networks in browser and Node.js environments. It focuses on simplicity and ease of use rather than large-scale or complex machine learning workflows.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple neural network support&lt;/strong&gt;: Provides straightforward APIs for training and running basic neural networks such as feedforward and recurrent models.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JavaScript-first design&lt;/strong&gt;: Written entirely in JavaScript, making it easy to integrate into React applications or Node.js services without additional tooling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser and server compatibility&lt;/strong&gt;: Can run in the browser for client-side inference or on the server for lightweight backend predictions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low setup overhead&lt;/strong&gt;: Requires minimal configuration, making it suitable for quick experiments and small-scale ML tasks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focused scope&lt;/strong&gt;: Designed for simple predictive problems rather than deep learning or large language models.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Brain.js has not seen an active release in recent years. While it remains usable for simple tasks and learning purposes, it may not be suitable for systems that require long-term maintenance or rapid ecosystem evolution.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What React Developers Should Pay Attention To
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt;: Whether AI runs in the browser or through APIs directly affects speed and resource usage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User experience&lt;/strong&gt;: Streaming responses and responsive UI updates matter more than raw model capability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictability and control&lt;/strong&gt;: AI output is structured and constrained so it aligns with application state, design systems, and business rules rather than producing free-form or hard-to-integrate results.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration with existing components&lt;/strong&gt;: The best AI tools work with your current React components instead of replacing them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are building visual editors, page builders, or structured content systems in React, &lt;a href="https://puckeditor.com?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_ai_libraries_react_2026" rel="noopener noreferrer"&gt;Puck AI&lt;/a&gt; offers a unique approach to AI-assisted UI that prioritizes predictability, structure, and integration over free-form generation.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Building a Rich Text Editor in React</title>
      <dc:creator>Astrodevil</dc:creator>
      <pubDate>Thu, 29 Jan 2026 20:03:13 +0000</pubDate>
      <link>https://dev.to/puckeditor/building-a-rich-text-editor-in-react-43h2</link>
      <guid>https://dev.to/puckeditor/building-a-rich-text-editor-in-react-43h2</guid>
      <description>&lt;p&gt;&lt;a href="https://puckeditor.com/docs/integrating-puck/rich-text-editing" rel="noopener noreferrer"&gt;Rich text editing&lt;/a&gt; is a common requirement in modern React applications, but rarely simple to implement. Allowing users to format text with headings, lists, and emphasis introduces challenges around state, content consistency, and maintainability.&lt;/p&gt;

&lt;p&gt;Many applications rely on raw HTML strings or direct use of &lt;code&gt;contenteditable&lt;/code&gt;. While this can work at first, it often leads to unpredictable behavior as the application grows. Content becomes harder to validate and version, and easy to break when multiple users or automated systems are involved.&lt;/p&gt;

&lt;p&gt;A more reliable approach is to treat rich text as structured data instead of markup. Explicit content models give applications control over content creation, rendering, and extensibility, while aligning naturally with React’s declarative, state-driven model.&lt;br&gt;
In this article, we will explore why rich text editing is hard in React, how structured content helps address these challenges, and how extension-based editors enable safe customization. We will also walk through a step-by-step example using &lt;a href="https://puckeditor.com/" rel="noopener noreferrer"&gt;Puck&lt;/a&gt;’s rich text editor to demonstrate these concepts in practice.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Rich Text Is Hard in React
&lt;/h2&gt;

&lt;p&gt;Rich text editing conflicts with several core principles of how React applications are designed. Many traditional solutions rely on browser-level APIs rather than explicit React state, which makes them harder to control as complexity increases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Many implementations depend on &lt;code&gt;contentEditable&lt;/code&gt; and browser-managed HTML mutations. These operate outside React’s state model and make editor behavior difficult to reason about.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Rendering user-generated HTML without an explicit content model limits an application’s ability to validate, transform, or reason about content reliably over time, which in turn makes it difficult to diff, version, or audit, especially in collaborative or frequently edited environments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Small markup changes can easily break layouts or violate design and accessibility rules without providing any clear signal to the application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Raw HTML alone does not convey intent, which makes it unsafe for automated transformations, programmatic editing, or AI-driven workflows without an additional layer of structure and control.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, many editors expose HTML as an interface while managing structured state internally. The core challenge is not the presence of HTML itself, but whether applications retain control and intent over how that HTML is produced and evolved.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Use Puck for Rich Text Editing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3zfmr12t5f592rdd692.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa3zfmr12t5f592rdd692.png" alt="Puck" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/" rel="noopener noreferrer"&gt;Puck is an open source visual editor for React&lt;/a&gt; that lets teams build custom page builders using their own components. The core of Puck is simply a React editor and a renderer, making it easy to integrate into any React application and define editing behavior through configuration.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Schema-driven fields&lt;/strong&gt;: Rich text in Puck is defined through explicit field schemas, making the allowed structure and behavior clear and enforceable at the application level.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Built on TipTap&lt;/strong&gt;: Puck’s rich text editor is powered by &lt;a href="https://tiptap.dev/docs" rel="noopener noreferrer"&gt;TipTap&lt;/a&gt;, a widely adopted editor engine known for its extensible and state-driven architecture.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Customizable&lt;/strong&gt;: Formatting options, heading levels, and editor capabilities can be enabled or disabled.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extensible by design&lt;/strong&gt;: New formatting features and behaviors can be added using extensions, without modifying or forking the editor core.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Natural fit for React&lt;/strong&gt;: Puck’s configuration model aligns with &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration" rel="noopener noreferrer"&gt;React’s component-driven approach&lt;/a&gt;, making rich text editing easier to reason about and maintain.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Step-by-Step Tutorial: Rich Text Editing with Puck
&lt;/h2&gt;

&lt;p&gt;A complete working demo is available in this &lt;a href="https://github.com/Studio1HQ/puck_demo_richtext" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;. You are encouraged to clone the repository and run the project locally to explore the full implementation, or follow the instructions below to set it up from scratch.&lt;/p&gt;

&lt;p&gt;To run the demo locally, use the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/Studio1HQ/puck_demo_richtext.git
cd puck_demo_richtext
npm install
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then navigate to &lt;a href="http://localhost:3000/edit" rel="noopener noreferrer"&gt;http://localhost:3000/edit&lt;/a&gt; and experiment with the rich text editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setting Up Puck
&lt;/h3&gt;

&lt;p&gt;First up, let’s install Puck. If you’re adding it to an existing project, install it directly via npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @puckeditor/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can scaffold a new project using the &lt;a href="https://github.com/puckeditor/puck/blob/main/README.md#recipes" rel="noopener noreferrer"&gt;official recipe&lt;/a&gt;. This sets up a working React and Next.js application with the editor and rendering pipeline already configured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-puck-app rich-text-demo
cd rich-text-demo
npm run dev

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the development server is running, navigate to &lt;a href="http://localhost:3000/edit" rel="noopener noreferrer"&gt;http://localhost:3000/edit&lt;/a&gt; to edit a page and &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; to view it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv1f68k5get8vvbhxv0u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv1f68k5get8vvbhxv0u.png" alt="Localhost" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Next.js Puck recipe includes the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Editor&lt;/strong&gt;: A visual editor interface where content blocks can be added, configured, and edited.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Renderer&lt;/strong&gt;: A rendering layer that takes saved editor data and outputs a live page using the same component configuration.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Configuration-driven setup&lt;/strong&gt;: All editor behavior, available blocks, and field types are defined through a single configuration file, rather than custom editor logic.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this article, we focus only on the editor configuration and how rich text behavior is defined. Routing, persistence, and deployment details are intentionally kept minimal to keep attention on the core concepts.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Adding a Rich Text Editor to Puck
&lt;/h3&gt;

&lt;p&gt;Rich text editing in Puck is defined declaratively through &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration#adding-fields" rel="noopener noreferrer"&gt;configuration&lt;/a&gt;. Instead of manually wiring an editor, handling DOM updates, or managing selection state, rich text behavior is enabled by adding a dedicated field type to a &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config" rel="noopener noreferrer"&gt;component definition&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To set it up, replace the contents of &lt;code&gt;/puck.config.tsx&lt;/code&gt; with the following Puck &lt;a href="https://puckeditor.com/docs/api-reference/configuration/config" rel="noopener noreferrer"&gt;configuration object&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import type { Config } from "@puckeditor/core";

type Props = {
  HeadingBlock: { title: string };
  RichTextBlock: { content: string };
};

export const config: Config&amp;lt;Props&amp;gt; = {
  components: {
    // Simple heading block for comparison
    HeadingBlock: {
      fields: {
        title: { type: "text" },
      },
      defaultProps: {
        title: "Heading",
      },
      render: ({ title }) =&amp;gt; (
        &amp;lt;div style={{ padding: 64 }}&amp;gt;
          &amp;lt;h1&amp;gt;{title}&amp;lt;/h1&amp;gt;
        &amp;lt;/div&amp;gt;
      ),
    },

    // Basic rich text editor block
    RichTextBlock: {
      label: "Rich Text",
      fields: {
        content: {
          type: "richtext",
        },
      },
      render: ({ content }) =&amp;gt; (
        &amp;lt;div style={{ padding: 64, maxWidth: 700, margin: "0 auto" }}&amp;gt;
          {content}
        &amp;lt;/div&amp;gt;
      ),
    },
  },
};

export default config;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file serves as the baseline for all upcoming sections, where we incrementally add control and extensibility.&lt;/p&gt;

&lt;p&gt;To enable inline editing in Puck, the &lt;code&gt;[contentEditable](https://puckeditor.com/docs/api-reference/fields/richtext#contenteditable)&lt;/code&gt; option is added to the rich text field definition.&lt;/p&gt;

&lt;p&gt;Add the following line &lt;strong&gt;to the &lt;code&gt;content&lt;/code&gt; field&lt;/strong&gt; of the &lt;code&gt;RichTextBlock&lt;/code&gt; in &lt;code&gt;puck.config.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RichTextBlock: {
  fields: {
    content: {
      type: "richtext",
      contentEditable: true,
    },
  },
  render: ({ content }) =&amp;gt; (
    &amp;lt;div style={{ padding: 64, maxWidth: 700, margin: "0 auto" }}&amp;gt;
      {content}
    &amp;lt;/div&amp;gt;
  ),
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1jhhadt98ll4doeis8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm1jhhadt98ll4doeis8q.png" alt="Editor" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inline editing works well for simple content updates, quick adjustments, and editorial workflows where users benefit from editing content in its final visual context.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Customizing Editor Behavior (Control)
&lt;/h3&gt;

&lt;p&gt;Rich text editors are often expected to give users complete freedom, but in most real applications, this leads to inconsistent content and broken design systems. Teams usually need to restrict formatting options to ensure visual consistency, accessibility, and predictable rendering across the application.&lt;/p&gt;

&lt;p&gt;Puck lets you selectively enable or disable formatting options based on the needs of the product or team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disabling a Formatting Option (Bold Example)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The example below shows how to disable the bold formatting option in a rich text field.&lt;/p&gt;

&lt;p&gt;Add the following &lt;code&gt;[options](https://puckeditor.com/docs/api-reference/fields/richtext#options)&lt;/code&gt; configuration &lt;strong&gt;to the &lt;code&gt;content&lt;/code&gt; field&lt;/strong&gt; of the &lt;code&gt;RichTextBlock&lt;/code&gt; in &lt;code&gt;puck.config.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RichTextBlock: {
  fields: {
    content: {
      type: "richtext",
      options: {
        bold: false,
      },
    },
  },
  render: ({ content }) =&amp;gt; (
    &amp;lt;div style={{ padding: 64, maxWidth: 700, margin: "0 auto" }}&amp;gt;
      {content}
    &amp;lt;/div&amp;gt;
  ),
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration removes the bold formatting capability from the editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nkgurrva2x6775rqx7c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nkgurrva2x6775rqx7c.png" alt="Puckeditor" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restricting Heading Levels (Structure)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In rich text editing, allowing unrestricted heading levels often leads to inconsistent content hierarchy and poor accessibility. Most applications only need a small, well-defined set of heading levels to maintain clarity and visual consistency.&lt;/p&gt;

&lt;p&gt;Puck also lets you constrain heading behavior through configuration, ensuring that content follows a predictable structure.&lt;/p&gt;

&lt;p&gt;The example below restricts headings to &lt;strong&gt;H1 and H2 only&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Add the following heading configuration &lt;strong&gt;to the &lt;code&gt;options&lt;/code&gt; object&lt;/strong&gt; of the &lt;code&gt;content&lt;/code&gt; field in &lt;code&gt;puck.config.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RichTextBlock: {
  fields: {
    content: {
      type: "richtext",
      options: {
        heading: {
          levels: [1, 2],
        },
      },
    },
  },
  render: ({ content }) =&amp;gt; (
    &amp;lt;div style={{ padding: 64, maxWidth: 700, margin: "0 auto" }}&amp;gt;
      {content}
    &amp;lt;/div&amp;gt;
  ),
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once this is applied, the editor will only allow users to select H1 and H2. All other heading levels are removed from the editor interface and cannot be applied through shortcuts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10tpceaxpkml7tz752j2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10tpceaxpkml7tz752j2.png" alt="Basic Puck editor interface hiding heading levels other than H1 and H2" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customizing the Menu Bar (UI Control)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Default editor toolbars are designed to cover a wide range of use cases, but in real applications, they often expose more options than necessary. This can overwhelm users and lead to inconsistent content. Most teams benefit from presenting a simplified toolbar that reflects only the formatting options they want to support.&lt;/p&gt;

&lt;p&gt;Puck lets you customize the rich text menu bar and decide which controls and components to render, making it possible to control the editor user experience without modifying the editor engine or writing custom UI code.&lt;/p&gt;

&lt;p&gt;The example below shows how to define a custom menu bar that exposes only selected controls.&lt;/p&gt;

&lt;p&gt;Add the &lt;code&gt;renderMenu&lt;/code&gt; function &lt;strong&gt;to the &lt;code&gt;content&lt;/code&gt;&lt;/strong&gt; field of the &lt;code&gt;RichTextBlock&lt;/code&gt; in &lt;code&gt;puck.config.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { RichTextMenu } from "@puckeditor/core";

content: {
  type: "richtext",
  renderMenu: () =&amp;gt; (
    &amp;lt;RichTextMenu&amp;gt;
      &amp;lt;RichTextMenu.Group&amp;gt;
        &amp;lt;RichTextMenu.Bold /&amp;gt;
      &amp;lt;/RichTextMenu.Group&amp;gt;
    &amp;lt;/RichTextMenu&amp;gt;
  ),
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration replaces the default toolbar with a minimal menu that includes only the bold control. All other formatting options are hidden from the interface.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7elahaqrtxjje80jltrx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7elahaqrtxjje80jltrx.png" alt="Puck editor interface showing only the bold control in the rich text editor toolbar" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Adding Custom Extensions (Extensibility)
&lt;/h2&gt;

&lt;p&gt;Modern rich text editors are not monolithic systems. Instead of baking every feature into the core editor, advanced functionality is added by &lt;a href="https://puckeditor.com/docs/extending-puck/" rel="noopener noreferrer"&gt;extending the editor engine itself&lt;/a&gt;. This approach keeps the editor lightweight while allowing applications to introduce new capabilities only when needed.&lt;/p&gt;

&lt;p&gt;Puck’s rich text editor follows this model. It is built on TipTap, which allows new formatting behaviors to be added by registering extensions through configuration.&lt;/p&gt;

&lt;p&gt;The example below adds support for superscript formatting by registering the &lt;a href="https://puckeditor.com/docs/integrating-puck/rich-text-editing#configure-the-extension" rel="noopener noreferrer"&gt;Superscript&lt;/a&gt; extension.&lt;/p&gt;

&lt;p&gt;First, install the Superscript extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @tiptap/extension-superscript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, add the extension to the rich text field configuration in &lt;code&gt;puck.config.tsx&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Superscript from "@tiptap/extension-superscript";

content: {
  type: "richtext",
  tiptap: {
    extensions: [Superscript],
  },
},

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration enables superscript functionality at the editor engine level. The editor now understands superscript formatting and can apply it internally, but users do not yet see a toolbar button or control to trigger it.&lt;/p&gt;

&lt;p&gt;Separating editor capability from editor UI is a key design principle. By adding functionality through extensions first, applications can control when and how features are exposed to users. This makes the editor easier to evolve, safer to customize, and better aligned with product requirements.&lt;/p&gt;

&lt;p&gt;In the next step, this capability will be surfaced through a custom toolbar control.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exposing the Extension via a Custom Control
&lt;/h3&gt;

&lt;p&gt;To make the feature usable, the editor needs to expose this capability through the user interface.&lt;/p&gt;

&lt;p&gt;This is done by connecting the editor state to the toolbar using a &lt;a href="https://puckeditor.com/docs/api-reference/fields/richtext#tiptapselector" rel="noopener noreferrer"&gt;selector&lt;/a&gt; and rendering a custom control that responds to that state.&lt;/p&gt;

&lt;p&gt;A selector allows the editor to expose information about its current state, such as whether a formatting option is active or whether it can be applied at a given cursor position. This state is then consumed by the menu bar to control button behavior.&lt;/p&gt;

&lt;p&gt;The example below shows how to expose the &lt;a href="https://tiptap.dev/docs/editor/extensions/overview" rel="noopener noreferrer"&gt;Superscript extension&lt;/a&gt; through a custom toolbar button.&lt;/p&gt;

&lt;p&gt;Update the &lt;code&gt;content&lt;/code&gt; field in &lt;code&gt;puck.config.tsx&lt;/code&gt; as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    // Rich text block demonstrating structure, control, extensibility
    RichTextBlock: {
      label: "Rich Text",
      fields: {
        content: {
          type: "richtext",

          // Add custom TipTap extension
          tiptap: {
            extensions: [Superscript],
            selector: ({ editor }) =&amp;gt; ({
              isSuperscript: editor?.isActive("superscript"),
              canSuperscript: editor
                ?.can()
                .chain()
                .toggleSuperscript()
                .run(),
            }),
          },

          // Custom toolbar with a Superscript control
          renderMenu: ({ children, editor, editorState }) =&amp;gt; (
            &amp;lt;RichTextMenu&amp;gt;
              {children}

              &amp;lt;RichTextMenu.Group&amp;gt;
                &amp;lt;RichTextMenu.Control
                  title="Superscript"
                  icon={&amp;lt;SuperscriptIcon size={16} /&amp;gt;}
                  onClick={() =&amp;gt;
                    editor?.chain().focus().toggleSuperscript().run()
                  }
                  active={editorState?.isSuperscript}
                  disabled={!editorState?.canSuperscript}
                /&amp;gt;
              &amp;lt;/RichTextMenu.Group&amp;gt;
            &amp;lt;/RichTextMenu&amp;gt;
          ),
        },
      },
      render: ({ content }) =&amp;gt; (
        &amp;lt;div style={{ padding: 64, maxWidth: 700, margin: "0 auto" }}&amp;gt;
          {content}
        &amp;lt;/div&amp;gt;
      ),
    },
  },
};

export default config;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lui21xs2mfm9di5brsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8lui21xs2mfm9di5brsp.png" alt="This configuration connects the Superscript extension to the editor interface" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This configuration connects the Superscript extension to the editor interface. The selector exposes whether superscript is active and whether it can be applied, and the custom control uses that state to render an interactive toolbar button.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Exploration
&lt;/h2&gt;

&lt;p&gt;You can extend this demo by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding more &lt;a href="https://puckeditor.com/docs/api-reference/fields/richtext#tiptapextensions" rel="noopener noreferrer"&gt;TipTap extensions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Introducing additional &lt;a href="https://puckeditor.com/docs/api-reference/fields/richtext#options" rel="noopener noreferrer"&gt;editor constraints&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Experimenting with different &lt;a href="https://puckeditor.com/docs/api-reference/fields/richtext#rendermenuprops" rel="noopener noreferrer"&gt;toolbar layouts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Integrating persistence or collaboration features&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;p&gt;Rich text editing works best when it is treated as a structured system rather than a free form input. By modeling content intentionally, enforcing clear rules, and extending behavior through configuration, teams can build editors that scale reliably with their applications.&lt;/p&gt;

&lt;p&gt;Puck fits naturally into this model by combining a structured, rich text engine with a configuration-driven approach that aligns well with modern React development.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structure over uncontrolled markup&lt;/strong&gt;: Rich text should encode intent and hierarchy through explicit structure, not rely solely on browser-managed HTML.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Control over free-form editing&lt;/strong&gt;: Editors should enforce design and content rules instead of relying on manual cleanup.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Extensibility through configuration&lt;/strong&gt;: New capabilities can be added safely without modifying core editor logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Separation of concerns&lt;/strong&gt;: Editing behavior, user interface, and rendering remain clearly decoupled.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Explore the &lt;a href="https://github.com/Studio1HQ/puck_demo_richtext" rel="noopener noreferrer"&gt;full demo repository&lt;/a&gt; to see these concepts in action, experiment with editor constraints, and extend the rich text editor to fit your own use cases.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Puck 0.21: AI beta, rich text editing &amp; new plugin rail</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Wed, 14 Jan 2026 13:07:00 +0000</pubDate>
      <link>https://dev.to/puckeditor/puck-021-ai-beta-rich-text-editing-new-plugin-rail-36jl</link>
      <guid>https://dev.to/puckeditor/puck-021-ai-beta-rich-text-editing-new-plugin-rail-36jl</guid>
      <description>&lt;p&gt;Puck 0.21 introduces AI page generation, rich text editing, a new interface for Plugins, and more.&lt;/p&gt;

&lt;p&gt;
  
&lt;/p&gt;

&lt;p&gt;In this post, we’ll go over everything new in Puck 0.21 and how you can start using it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's new in Puck 0.21&lt;/li&gt;
&lt;li&gt;Other changes&lt;/li&gt;
&lt;li&gt;How to upgrade&lt;/li&gt;
&lt;li&gt;Full changelog&lt;/li&gt;
&lt;li&gt;Contributors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re upgrading from an earlier version, make sure to review the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-021?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt; for any breaking changes and migration tips.&lt;/p&gt;

&lt;p&gt;You can also find more in-depth documentation for each new feature in our &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in Puck 0.21
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Puck has moved
&lt;/h3&gt;

&lt;p&gt;All packages have moved to the &lt;strong&gt;@puckeditor&lt;/strong&gt; scope:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@puckeditor/core" rel="noopener noreferrer"&gt;&lt;strong&gt;@puckeditor/core&lt;/strong&gt;&lt;/a&gt; (from &lt;strong&gt;@measured/puck&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@puckeditor/field-contentful" rel="noopener noreferrer"&gt;&lt;strong&gt;@puckeditor/field-contentful&lt;/strong&gt;&lt;/a&gt; (from &lt;strong&gt;@measured/puck-field-contentful&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@puckeditor/plugin-emotion-cache" rel="noopener noreferrer"&gt;&lt;strong&gt;@puckeditor/plugin-emotion-cache&lt;/strong&gt;&lt;/a&gt; (from &lt;strong&gt;@measured/puck-plugin-emotion-cache&lt;/strong&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.npmjs.com/package/@puckeditor/plugin-heading-analyzer" rel="noopener noreferrer"&gt;&lt;strong&gt;@puckeditor/plugin-heading-analyzer&lt;/strong&gt;&lt;/a&gt; (from &lt;strong&gt;@measured/puck-plugin-heading-analyzer&lt;/strong&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For step-by-step migration instructions, see the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-021?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI page generation
&lt;/h3&gt;

&lt;p&gt;Puck 0.21 adds support for &lt;a href="https://puckeditor.com/docs/ai/overview?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;strong&gt;Puck AI&lt;/strong&gt;&lt;/a&gt;, letting you generate pages from your existing Puck config.&lt;/p&gt;

&lt;p&gt;
  
&lt;/p&gt;

&lt;p&gt;Puck AI is now in open beta. &lt;a href="https://cloud.puckeditor.com/sign-up?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;Sign up now&lt;/a&gt; to get started.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rich text editing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/fields/richtext?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;richtext&lt;/code&gt;&lt;/a&gt; is a new field type that provides a WYSIWYG editor with formatting options like bold, italic, alignment, and more.&lt;/p&gt;

&lt;p&gt;To use it, add the &lt;code&gt;richtext&lt;/code&gt; field to your component config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;richtext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;p&gt;You can also enable inline editing in the canvas by setting &lt;code&gt;contentEditable&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; in the field config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;richtext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;contentEditable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;p&gt;The field is built with &lt;a href="https://tiptap.dev/docs/editor/getting-started/overview" rel="noopener noreferrer"&gt;TipTap&lt;/a&gt; and is fully customizable. You can adjust the menus, formatting behavior, or introduce new &lt;a href="https://tiptap.dev/docs/editor/core-concepts/extensions" rel="noopener noreferrer"&gt;TipTap extensions&lt;/a&gt;. See the &lt;a href="https://puckeditor.com/docs/integrating-puck/rich-text-editing?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;docs&lt;/a&gt; for more details.&lt;/p&gt;

&lt;h3&gt;
  
  
  New UI: The Plugin Rail
&lt;/h3&gt;

&lt;p&gt;After updating, you’ll see a new vertical navigation bar on the left-hand side of the editor. This bar is called the &lt;strong&gt;Plugin Rail&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;
  
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/extending-puck/plugins?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;Plugins&lt;/a&gt; can optionally appear as buttons in the rail and, when selected, render UI in the left sidebar.&lt;/p&gt;

&lt;p&gt;By default, Puck includes three loaded plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/api-reference/plugins/blocks-plugin?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;strong&gt;Blocks&lt;/strong&gt;&lt;/a&gt;, which renders the component drawer&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/api-reference/plugins/outline-plugin?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;strong&gt;Outline&lt;/strong&gt;&lt;/a&gt;, which renders the page structure outline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/api-reference/plugins/fields-plugin?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;strong&gt;Fields&lt;/strong&gt;&lt;/a&gt;, which renders the fields for the currently selected component&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer the previous UI, you can opt out of the Plugin Rail by following the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-021?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;migration guide&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Add your own plugin UI
&lt;/h4&gt;

&lt;p&gt;To &lt;a href="https://puckeditor.com/docs/extending-puck/plugins#rendering-ui-in-the-plugin-rail?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;render UI&lt;/a&gt; in the Plugin Rail, define a plugin object with the following parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author-info&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Globally unique identifier&lt;/span&gt;
  &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Label shown in the rail&lt;/span&gt;
  &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Icon shown in the rail (lucide recommended)&lt;/span&gt;
  &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Component to render in the sidebar&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Page Author: John Doe&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Last updated: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLocaleDateString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;h4&gt;
  
  
  New mobile interface
&lt;/h4&gt;

&lt;p&gt;On mobile, the sidebars are removed, and the editor now displays the Plugin Rail and plugin panel at the bottom of the screen.&lt;/p&gt;

&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  New API: Change slot element with &lt;code&gt;as&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;By default, Puck renders a &lt;code&gt;div&lt;/code&gt; for every &lt;a href="https://puckeditor.com/docs/api-reference/fields/slot?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;slot&lt;/code&gt;&lt;/a&gt; used in the page.&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;as&lt;/code&gt; prop lets you replace that with any HTML element or component you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Table&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Renders as &amp;lt;main&amp;gt;children&amp;lt;/main&amp;gt;&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Content&lt;/span&gt; &lt;span class="na"&gt;as&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Main&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New hotkeys: Delete and backspace
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/SilasRopp1012" rel="noopener noreferrer"&gt;SilasRopp1012&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can now press &lt;strong&gt;delete&lt;/strong&gt; or &lt;strong&gt;backspace&lt;/strong&gt; to remove a selected component from the page.&lt;/p&gt;

&lt;h3&gt;
  
  
  New component: &lt;code&gt;Puck.Layout&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://puckeditor.com/docs/api-reference/components/puck-layout?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;Puck.Layout&lt;/code&gt;&lt;/a&gt; component renders the default Puck UI.&lt;/p&gt;

&lt;p&gt;This lets you use the &lt;code&gt;Puck&lt;/code&gt; component as a provider of the &lt;a href="https://puck-docs-ezoa67dsr-puck-9db9778b.vercel.app/docs/extending-puck/internal-puck-api" rel="noopener noreferrer"&gt;internal Puck API&lt;/a&gt; without replacing the default editor layout.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Puck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createUsePuck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usePuck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUsePuck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SelectedItemIndicator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;selectedItem&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Editor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SelectedItemIndicator&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Layout&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  New field override: &lt;code&gt;custom&lt;/code&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by &lt;a href="https://github.com/shannonhochkins" rel="noopener noreferrer"&gt;shannonhochkins&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://puckeditor.com/docs/api-reference/overrides/field-types?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;fieldTypes.custom&lt;/code&gt;&lt;/a&gt; override lets you customize how all custom fields are rendered. This is useful for wrapping them in a component or adding common logic or styles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overrides&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fieldTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Wrap all custom fields in a green border&lt;/span&gt;
      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1px green solid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;field&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx0p7ffckhbjottw3ef3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwx0p7ffckhbjottw3ef3.png" alt="Image showing a custom field surrounded by a green border" width="646" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Declaration merging for the &lt;code&gt;ComponentConfig&lt;/code&gt; type
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentConfig&lt;/code&gt;&lt;/a&gt; type can now be extended using &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation" rel="noopener noreferrer"&gt;declaration merging&lt;/a&gt;. This is useful for adding strictly typed custom properties to your component configs.&lt;/p&gt;

&lt;p&gt;To use it, augment and use the interface &lt;code&gt;ComponentConfigExtensions&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentConfigExtensions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ComponentConfigExtensions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myComponentConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentConfigExtensions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Declaration merging for the &lt;code&gt;Metadata&lt;/code&gt; type
&lt;/h3&gt;

&lt;p&gt;You can now type Puck metadata using &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation" rel="noopener noreferrer"&gt;declaration merging&lt;/a&gt;. This is useful for working with strictly typed metadata at any level.&lt;/p&gt;

&lt;p&gt;To use it, augment the following interfaces depending on where the metadata applies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PuckMetadata&lt;/code&gt; – for metadata passed to the &lt;a href="https://puckeditor.com/docs/api-reference/components/puck#metadata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;Puck&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://puckeditor.com/docs/api-reference/components/render#metadata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;Render&lt;/code&gt;&lt;/a&gt; component.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ComponentMetadata&lt;/code&gt; – for metadata used in a &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#metadata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentConfig&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FieldMetadata&lt;/code&gt; – for metadata used in a &lt;a href="https://puckeditor.com/docs/api-reference/fields/base#metadata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;field config&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Puck&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ComponentConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PuckMetadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ComponentMetadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;componentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;FieldMetadata&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;fieldType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt; &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ExampleComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;componentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Example&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fieldConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fieldType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Declaration merging for Field types
&lt;/h3&gt;

&lt;p&gt;All &lt;a href="https://github.com/puckeditor/puck/blob/79a2684949b6aaa98cd85c4ca6ed13e3e585124f/packages/core/types/Fields.ts" rel="noopener noreferrer"&gt;field types&lt;/a&gt; can now be extended using &lt;a href="https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation" rel="noopener noreferrer"&gt;declaration merging&lt;/a&gt;. This is useful for adding strictly typed custom properties to your field configs, such as when using &lt;a href="https://puckeditor.com/docs/api-reference/overrides/field-types?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;fieldTypes&lt;/code&gt;&lt;/a&gt; overrides.&lt;/p&gt;

&lt;p&gt;To use it, augment the field type you need to extend:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NumberField&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;declare&lt;/span&gt; &lt;span class="kr"&gt;module&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;NumberField&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;isFloat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;myComponentConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NumberField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isFloat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;See the &lt;a href="https://github.com/puckeditor/puck/blob/79a2684949b6aaa98cd85c4ca6ed13e3e585124f/packages/core/types/Fields.ts" rel="noopener noreferrer"&gt;codebase&lt;/a&gt; for a list of all field types that can be augmented.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Other changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  New interaction: Deselect components on second click
&lt;/h3&gt;

&lt;p&gt;A selected component can now be deselected by clicking it a second time.&lt;/p&gt;

&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  New component: &lt;code&gt;ActionBar.Separator&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://puckeditor.com/docs/api-reference/components/action-bar-separator?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ActionBar.Separator&lt;/code&gt;&lt;/a&gt; component lets you render a separator inside the &lt;a href="https://puckeditor.com/docs/api-reference/components/action-bar?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ActionBar&lt;/code&gt;&lt;/a&gt; when using the &lt;a href="https://puckeditor.com/docs/api-reference/overrides/action-bar?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;actionBar&lt;/code&gt;&lt;/a&gt; override.&lt;br&gt;
This is useful when you need visual separation between actions without grouping them with &lt;a href="https://puckeditor.com/docs/api-reference/components/action-bar-group?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ActionBar.Group&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overrides&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;actionBar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Actions"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Separator&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clicked!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Click me
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwd1fvk1jzmgg5k3wvff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwd1fvk1jzmgg5k3wvff.png" alt="Image of an ActionBar with a separator before a " width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  New &lt;code&gt;Puck&lt;/code&gt; prop: &lt;code&gt;height&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You can now control the editor height by passing the &lt;a href="https://puckeditor.com/docs/api-reference/components/puck#height?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;height&lt;/code&gt;&lt;/a&gt; prop to the &lt;code&gt;Puck&lt;/code&gt; component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;
  &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"100%"&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New &lt;code&gt;Puck&lt;/code&gt; prop: &lt;code&gt;_experimentalFullScreenCanvas&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Setting &lt;a href="https://puckeditor.com/docs/api-reference/components/puck#_experimentalfullscreencanvas?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;_experimentalFullScreenCanvas&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;true&lt;/code&gt; on the &lt;code&gt;Puck&lt;/code&gt; component removes the empty space around the editor canvas and displays the viewport switcher as a floating button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;
  &lt;span class="na"&gt;_experimentalFullScreenCanvas&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This prop is experimental and might change in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  New default viewport: Full-width
&lt;/h3&gt;

&lt;p&gt;Puck now includes a Full-width &lt;a href="https://puckeditor.com/docs/api-reference/components/puck#default-viewports?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;default viewport&lt;/a&gt; that uses the full width of the canvas container without scaling the resolution.&lt;/p&gt;

&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  Make viewports fill the container
&lt;/h3&gt;

&lt;p&gt;Custom viewports can now fill the full width of the canvas container without scaling the resolution by setting the viewport &lt;a href="https://puckeditor.com/docs/api-reference/components/puck#width?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;width&lt;/code&gt;&lt;/a&gt; to &lt;code&gt;100%&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;
  &lt;span class="na"&gt;viewports&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;100%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;FullWidth&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New &lt;code&gt;ActionBar.Action&lt;/code&gt; prop: &lt;code&gt;disabled&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;ActionBar.Action&lt;/code&gt; component can now be disabled by setting the &lt;a href="https://puckeditor.com/docs/api-reference/components/action-bar-action#disabled?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;disabled&lt;/code&gt;&lt;/a&gt; prop to true.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;overrides&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;actionBar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Actions"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Clicked!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Click me
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fba3u256txozntrbazeh4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fba3u256txozntrbazeh4.png" alt="Image of an ActionBar with a disabled " width="749" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  New Puck API function: &lt;code&gt;resolveDataById&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The internal &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; (accessed with &lt;code&gt;usePuck&lt;/code&gt; or &lt;code&gt;useGetPuck&lt;/code&gt;) now includes a utility to run a component’s &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt; by id. This is useful for triggering resolve data on the selected item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headerActions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolveDataById&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolveDataById&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectedItem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selectedItem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolveDataById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectedItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Refresh component
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New Puck API function: &lt;code&gt;resolveDataBySelector&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The internal &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; (accessed with &lt;code&gt;usePuck&lt;/code&gt; or &lt;code&gt;useGetPuck&lt;/code&gt;) now includes a utility to run a component’s &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt; for a given item selector. This is useful when inserting or updating components programmatically using the dispatcher.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;
  &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headerActions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolveDataBySelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolveDataBySelector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;itemSelector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

      &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;resolveDataBySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;itemSelector&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Refresh component
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New Puck API function: &lt;code&gt;getParentById&lt;/code&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/eyueldk" rel="noopener noreferrer"&gt;eyueldk&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The internal &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; (accessed with &lt;code&gt;usePuck&lt;/code&gt; or &lt;code&gt;useGetPuck&lt;/code&gt;) now includes a utility to get an item’s parent &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentData&lt;/code&gt;&lt;/a&gt; from the &lt;a href="https://puckeditor.com/docs/api-reference/data-model/data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;data tree&lt;/a&gt;. This is useful when working with slots.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getParentById&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getParentById&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;getParentById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HeadingBlock-123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Returns: { type: "Flex-123", props: { ... } }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Access &lt;code&gt;parent&lt;/code&gt; data in &lt;code&gt;resolvePermissions&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvepermissionsdata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;resolvePermissions&lt;/code&gt;&lt;/a&gt; now receives the component’s parent &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentData&lt;/code&gt;&lt;/a&gt;. This is useful for applying permissions conditionally based on where components are dropped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolvePermissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;drag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;drag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  Access &lt;code&gt;parent&lt;/code&gt; data in &lt;code&gt;resolveData&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt; now receives the component’s parent &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentData&lt;/code&gt;&lt;/a&gt;. This is useful for setting props conditionally based on where components are dropped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolveData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello from Grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  Access &lt;code&gt;metadata&lt;/code&gt; in &lt;code&gt;resolveFields&lt;/code&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/DamianKocjan" rel="noopener noreferrer"&gt;DamianKocjan&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvefieldsdata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;resolveFields&lt;/code&gt;&lt;/a&gt; now receives the component &lt;code&gt;metadata&lt;/code&gt;. This is useful for defining fields conditionally based on data external to Puck.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;resolveFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Show an "id" field if the user is admin&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New trigger for &lt;code&gt;resolveData&lt;/code&gt;: &lt;code&gt;move&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Previously, &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt; only ran when a component was first inserted, one of its fields changed, the Puck editor loaded, or &lt;a href="https://puckeditor.com/docs/api-reference/functions/resolve-all-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;resolveAllData&lt;/code&gt;&lt;/a&gt; was called.&lt;/p&gt;

&lt;p&gt;Starting in Puck 0.21, &lt;code&gt;resolveData&lt;/code&gt; also runs when a component is moved to a different slot.&lt;/p&gt;

&lt;p&gt;To detect when this happens in the function, use the new &lt;code&gt;"move"&lt;/code&gt; trigger parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;resolveData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Reset the column span if the component moved to a different Grid&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trigger&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;move&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;columnSpan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use CSS units in Slot &lt;code&gt;minEmptyHeight&lt;/code&gt; prop
&lt;/h3&gt;

&lt;p&gt;Previously, the Slot render prop &lt;a href="https://puckeditor.com/docs/api-reference/fields/slot#minemptyheight?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;minEmptyHeight&lt;/code&gt;&lt;/a&gt; only supported pixel values. Starting in Puck 0.21.0, it now supports all CSS units.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;PageContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Content&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Content&lt;/span&gt; &lt;span class="na"&gt;minEmptyHeight&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"50dvh"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlucw2vsf9w2fymf8ndo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjlucw2vsf9w2fymf8ndo.png" alt="GIF showing a slot with a min empty height of 50dvh" width="800" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable cache in &lt;code&gt;external&lt;/code&gt; fields
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/matthewlynch" rel="noopener noreferrer"&gt;matthewlynch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;By default, the &lt;a href="https://puckeditor.com/docs/api-reference/fields/external?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;external&lt;/code&gt;&lt;/a&gt; field caches and reuses the first output of the &lt;a href="https://puckeditor.com/docs/api-reference/fields/external#fetchlistqueryparams?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;fetchList&lt;/code&gt;&lt;/a&gt; function in memory.&lt;/p&gt;

&lt;p&gt;The new &lt;a href="https://puckeditor.com/docs/api-reference/fields/external#cache?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;cache.enabled&lt;/code&gt;&lt;/a&gt; config parameter lets you disable this behavior so that &lt;code&gt;fetchList&lt;/code&gt; always runs and retrieves the latest data when the component is selected in the canvas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;external&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;fetchList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Post&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  Use functions for &lt;code&gt;defaultItemProps&lt;/code&gt; in array fields
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/puckeditor/puck/issues?q=is%3Apr+author%3ALengYXin" rel="noopener noreferrer"&gt;LengYXin&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/fields/array#defaultitemprops?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;defaultItemProps&lt;/code&gt;&lt;/a&gt; now accepts a function to set default props when a new item is added to the array field. This is useful for generating dynamic defaults based on the item’s insertion order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;arrayFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;defaultItemProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Item &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  Use JSX for &lt;code&gt;getItemSummary&lt;/code&gt; in array fields
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/DamianKocjan" rel="noopener noreferrer"&gt;DamianKocjan&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/fields/array#getitemsummaryitem-index?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;getItemSummary&lt;/code&gt;&lt;/a&gt; in array fields now accepts any &lt;a href="https://react.dev/learn/typescript#typing-children" rel="noopener noreferrer"&gt;&lt;code&gt;ReactNode&lt;/code&gt;&lt;/a&gt; as a return type.&lt;/p&gt;

&lt;p&gt;This lets you use JSX for richer item summaries that include your own components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;TodoList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;arrayFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// ...&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;finished&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="c1"&gt;// ...&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;getItemSummary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; –
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finished&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;finished&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;✓&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;
  
&lt;/p&gt;

&lt;h3&gt;
  
  
  Use JSX for &lt;code&gt;getItemSummary&lt;/code&gt; in external fields
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by &lt;a href="https://github.com/DamianKocjan" rel="noopener noreferrer"&gt;DamianKocjan&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/fields/external#getitemsummaryitem?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;&lt;code&gt;getItemSummary&lt;/code&gt;&lt;/a&gt; in external fields now accepts any &lt;a href="https://react.dev/learn/typescript#typing-children" rel="noopener noreferrer"&gt;&lt;code&gt;ReactNode&lt;/code&gt;&lt;/a&gt; as a return type.&lt;/p&gt;

&lt;p&gt;This lets you use JSX for richer item summaries that include your own components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Books&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;external&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;fetchList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Raven&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;author&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;E. A. Poe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}];&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="na"&gt;getItemSummary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; –&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gray&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;author&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqsb97yaka6oupg0x039.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feqsb97yaka6oupg0x039.png" alt="Image showing an external field where each item summary shows a title and author in gray" width="676" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to upgrade
&lt;/h2&gt;

&lt;p&gt;To upgrade your Puck application to 0.21, follow the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-021?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=puck_021_release" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt; for step-by-step instructions. It covers deprecated APIs, breaking changes, and common pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full changelog
&lt;/h2&gt;

&lt;p&gt;You can find the full changelog, including bug fixes and known issues, in the &lt;a href="https://github.com/measuredco/puck/releases/tag/v0.21.0" rel="noopener noreferrer"&gt;GitHub release&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/asterixcapri" rel="noopener noreferrer"&gt;asterixcapri&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DamianKocjan" rel="noopener noreferrer"&gt;DamianKocjan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/eyueldk" rel="noopener noreferrer"&gt;eyueldk&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/fero46" rel="noopener noreferrer"&gt;fero46&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/ffffranklin" rel="noopener noreferrer"&gt;ffffranklin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/LengYXin" rel="noopener noreferrer"&gt;LengYXin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/matthewlynch-wise" rel="noopener noreferrer"&gt;matthewlynch-wise&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/michaelmccord" rel="noopener noreferrer"&gt;michaelmccord&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rilrom" rel="noopener noreferrer"&gt;rilrom&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rogerogers" rel="noopener noreferrer"&gt;rogerogers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/shannonhochkins" rel="noopener noreferrer"&gt;shannonhochkins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SilasRopp1012" rel="noopener noreferrer"&gt;SilasRopp1012&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/themre" rel="noopener noreferrer"&gt;themre&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Puck 0.19: Slots API &amp; performance gains</title>
      <dc:creator>Chris Villa</dc:creator>
      <pubDate>Tue, 10 Jun 2025 10:17:15 +0000</pubDate>
      <link>https://dev.to/puckeditor/puck-019-slots-api-performance-gains-1eik</link>
      <guid>https://dev.to/puckeditor/puck-019-slots-api-performance-gains-1eik</guid>
      <description>&lt;p&gt;Puck 0.19 introduces the &lt;a href="https://puckeditor.com/docs/api-reference/fields/slot?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;Slots API&lt;/a&gt;, the powerful successor to DropZones that lets you nest components using a field. This new approach allows you to define drop areas and their content programmatically using &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#defaultprops?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;defaultProps&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt;, enabling sophisticated patterns like component templates:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh579sasp9okvv0cs04s.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvh579sasp9okvv0cs04s.gif" alt="The Puck demo showcasing the template pattern" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In addition to Slots, this release brings major performance improvements, a new metadata API for passing data into every component, and many quality-of-life upgrades.&lt;/p&gt;

&lt;p&gt;In this post, we'll walk through everything new in Puck 0.19 and how to start using it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's new in Puck 0.19&lt;/li&gt;
&lt;li&gt;Other changes&lt;/li&gt;
&lt;li&gt;How to upgrade&lt;/li&gt;
&lt;li&gt;Full changelog&lt;/li&gt;
&lt;li&gt;Contributors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're upgrading from a previous version, be sure to check the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-019?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt; for breaking changes and migration tips. &lt;/p&gt;

&lt;p&gt;You can also find detailed docs for each new feature in our &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in Puck 0.19
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Slots API
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://puckeditor.com/docs/api-reference/fields/slot?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;slots API&lt;/a&gt; is a new field type you can use to render drop zones and nest components, replacing the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;DropZone API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It works like any other field: you define it in your &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;component config&lt;/a&gt;, and get access to its value in the component &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#renderprops?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;render&lt;/code&gt;&lt;/a&gt; function. The slot field is converted to a component that you can use to render the drop zone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Flexbox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Items&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4gekk8md9flj1on7f2kn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4gekk8md9flj1on7f2kn.gif" alt="Flexbox component with slots" width="899" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The slot component provided to the render function accepts most of the same props as the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;DropZone /&amp;gt;&lt;/code&gt;&lt;/a&gt; component, making migration straightforward. See the &lt;a href="https://puckeditor.com/docs/api-reference/fields/slot#render-function?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;slots documentation&lt;/a&gt; for a full breakdown of all available render props.&lt;/p&gt;

&lt;p&gt;The components inside a slot are stored within the props of the parent component as an array of &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentData&lt;/code&gt;&lt;/a&gt;, making slots completely portable and straightforward to work with for data transformation or schema validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flexbox"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Flexbox-1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Header"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"props"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Header-2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nested header"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since slots are regular fields, you can take full advantage of other field APIs, like &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#defaultprops?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;defaultProps&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt;, to programmatically set the components they contain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Flexbox&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Include a Header in this slot when the Flexbox is added to the page&lt;/span&gt;
      &lt;span class="na"&gt;defaultProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Items&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Items&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr0kosd1wf0madz4skhs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkr0kosd1wf0madz4skhs.gif" alt="Flexbox component with default props for slot fields" width="899" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Slots are now the recommended way to handle nested components in Puck. The &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt; component has been deprecated and will be removed in a future release. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Keep in mind: &lt;em&gt;slots introduce a new model for representing components&lt;/em&gt;. Existing &lt;code&gt;DropZone&lt;/code&gt; data, and advanced use-cases that parse or manipulate the Puck &lt;a href="https://puckeditor.com/docs/api-reference/data-model/data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;data payload&lt;/a&gt; may need to be updated. You can find guidance on how to migrate from DropZones to Slots in the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-019?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  New function: &lt;code&gt;walkTree&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The new &lt;a href="https://puckeditor.com/docs/api-reference/functions/walk-tree?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;walkTree&lt;/code&gt;&lt;/a&gt; utility recursively walks all slots in the entire tree of the &lt;a href="https://puckeditor.com/docs/api-reference/data-model/data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;data payload&lt;/a&gt; or a single &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentData&lt;/code&gt;&lt;/a&gt; node, and optionally modifies the nodes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;walkTree&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transformedData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;walkTree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nestedComponents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Add the "example" prop to all children&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;nestedComponents&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Selectors for &lt;code&gt;usePuck&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Puck 0.19 introduces &lt;a href="https://puckeditor.com/docs/api-reference/functions/use-puck#selectordata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;selectors&lt;/a&gt; for &lt;a href="https://puckeditor.com/docs/api-reference/functions/use-puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;usePuck&lt;/code&gt;&lt;/a&gt;, letting you subscribe to the parts of the internal &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; you need in order to avoid unnecessary re-renders. To use selectors, use the new &lt;code&gt;createUsePuck&lt;/code&gt; helper, and pick which part of the &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;API&lt;/a&gt; you want to listen to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createUsePuck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;usePuck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createUsePuck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LeftSideBarButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Will only re-render when closing or opening the left sidebar&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePuck&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;leftSideBarVisible&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isOpen&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Close&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original &lt;code&gt;usePuck&lt;/code&gt; hook is still available and won’t be deprecated.&lt;/p&gt;

&lt;p&gt;For a breakdown of how selectors compare with the original &lt;code&gt;usePuck&lt;/code&gt;, check out the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-019?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  New hook: &lt;code&gt;useGetPuck()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;To make it possible to access the internal &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; outside of the render lifecycle, we've added a new hook called &lt;a href="https://puckeditor.com/docs/api-reference/functions/use-get-puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;useGetPuck()&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useGetPuck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SaveDataButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPuck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGetPuck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Access the latest appState only when the button gets clicked&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;appState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPuck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nf"&gt;saveData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;getPuck&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Save your page&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useGetPuck&lt;/code&gt; returns a function that can be called to fetch the latest &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; without triggering re-renders.&lt;/p&gt;

&lt;p&gt;For a breakdown of how &lt;code&gt;useGetPuck&lt;/code&gt; compares with the original &lt;code&gt;usePuck&lt;/code&gt;, check out the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-019?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Metadata API
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/jsjexpert" rel="noopener noreferrer"&gt;@jsjexpert&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#puckmetadata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;metadata API&lt;/a&gt; lets you inject data into every component within your config, without relying on context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Page ID: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Metadata can also be accessed within &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;resolveData&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1234&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolveData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Page ID: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pageId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New recipe: &lt;code&gt;react-router&lt;/code&gt;
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/matthewlynch" rel="noopener noreferrer"&gt;@matthewlynch&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This version also includes a recipe for using Puck with the &lt;a href="https://reactrouter.com/start/framework/installation" rel="noopener noreferrer"&gt;react-router framework&lt;/a&gt;, so you can scaffold new projects with everything pre-configured.&lt;/p&gt;

&lt;p&gt;To use it, run &lt;code&gt;create-puck-app&lt;/code&gt; and enter &lt;code&gt;react-router&lt;/code&gt; when asked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npx create-puck-app my-app

&lt;span class="c"&gt;# Type "react-router"&lt;/span&gt;
? Which recipe would you like to use? react-router
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Improved performance
&lt;/h3&gt;

&lt;p&gt;A big focus for this release was performance. Puck 0.19 drastically reduces the number of unnecessary re-renders, making the editor significantly faster and smoother, especially in larger projects.&lt;/p&gt;

&lt;p&gt;To demonstrate this, we compared rendering times for common actions in 0.19 vs 0.18.3 using Puck with a test page containing 20 &lt;code&gt;HeadingBlock&lt;/code&gt; components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;HeadingBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These were the results:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobbokdoiw4kfeewn1d7x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fobbokdoiw4kfeewn1d7x.png" alt="0.19 vs 0.18.3 rendering times comparison for standard puck actions in milliseconds" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inserting a &lt;code&gt;HeadingBlock&lt;/code&gt; was &lt;strong&gt;82% faster&lt;/strong&gt; in 0.19.&lt;/li&gt;
&lt;li&gt;Replacing a &lt;code&gt;HeadingBlock&lt;/code&gt; prop was &lt;strong&gt;91% faster&lt;/strong&gt; in 0.19.&lt;/li&gt;
&lt;li&gt;Reordering a &lt;code&gt;HeadingBlock&lt;/code&gt; was &lt;strong&gt;79% faster&lt;/strong&gt; in 0.19.&lt;/li&gt;
&lt;li&gt;Deleting a &lt;code&gt;HeadingBlock&lt;/code&gt; was &lt;strong&gt;63% faster&lt;/strong&gt; in 0.19.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Getters for item selectors
&lt;/h3&gt;

&lt;p&gt;The internal &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; (accessed with &lt;code&gt;usePuck&lt;/code&gt; and &lt;code&gt;useGetPuck&lt;/code&gt;) now includes a set of utilities to get &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;component data&lt;/a&gt; from within the tree. These are useful when working with slots.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/api-reference/puck-api#getitembyselectorselector?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;getItemBySelector&lt;/code&gt;&lt;/a&gt; gets an item’s &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentData&lt;/code&gt;&lt;/a&gt; by &lt;a href="https://puckeditor.com/docs/api-reference/data-model/item-selector?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;selector&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/api-reference/puck-api#getitembyidid?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;getItemById&lt;/code&gt;&lt;/a&gt; gets an item’s &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentData&lt;/code&gt;&lt;/a&gt; by &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data#props?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;component id&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/api-reference/puck-api#getselectorforidid?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;getSelectorForId&lt;/code&gt;&lt;/a&gt; gets an item’s &lt;a href="https://puckeditor.com/docs/api-reference/data-model/item-selector?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;selector&lt;/a&gt; by &lt;a href="https://puckeditor.com/docs/api-reference/data-model/component-data#props?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;component id&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;getItemBySelector&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;// The item is in the "children" slot field of the component with id "Flex-123"&lt;/span&gt;
  &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Flex-123:children&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Returns: { type: "HeadingBlock", props: {...} }&lt;/span&gt;

&lt;span class="nf"&gt;getItemById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HeadingBlock-123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Returns: { type: "HeadingBlock", props: {...} }&lt;/span&gt;

&lt;span class="nf"&gt;getSelectorForId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HeadingBlock-123&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Returns: { index: 0, zone: "Flex-123:children" }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Trigger event in resolveData
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt; now receives a trigger parameter that tells you why it ran, whether it was because Puck was loaded ("load"), the component was dropped in the canvas (”insert”), props were updated ("replace"), or you forced it via &lt;a href="https://puckeditor.com/docs/api-reference/functions/resolve-all-data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;resolveAllData&lt;/code&gt;&lt;/a&gt; ("force").&lt;/p&gt;

&lt;p&gt;This gives you more control over how and when your data is resolved, so you can skip unnecessary fetches or run specific logic depending on the event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolveData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Resolve and add the title only when the Header is first dropped in&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trigger&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;insert&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resolvedTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getTitle&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resolvedTitle&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom label icons for fields
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/DamianKocjan" rel="noopener noreferrer"&gt;@DamianKocjan&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can now provide your own icons for field labels by passing a React node to the new &lt;a href="https://puckeditor.com/docs/api-reference/fields/base#labelicon?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;labelIcon&lt;/code&gt;&lt;/a&gt; config parameter. If you're using the &lt;a href="https://puckeditor.com/docs/api-reference/components/field-label?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;FieldLabel&lt;/code&gt;&lt;/a&gt; component directly, you can provide the icon via the &lt;a href="https://puckeditor.com/docs/api-reference/components/field-label#icon?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;icon&lt;/code&gt;&lt;/a&gt; prop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TextCursor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lucide-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;labelIcon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextCursor&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u4q4v1kolocvd5y8gov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8u4q4v1kolocvd5y8gov.png" alt="A Puck text field with a custom Lucide " width="362" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Field placeholders
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/DamianKocjan" rel="noopener noreferrer"&gt;@DamianKocjan&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fields now support &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/placeholder" rel="noopener noreferrer"&gt;placeholders&lt;/a&gt; for &lt;a href="https://puckeditor.com/docs/api-reference/fields/text#placeholder?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;text&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://puckeditor.com/docs/api-reference/fields/textarea#placeholder?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;textarea&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://puckeditor.com/docs/api-reference/fields/number#placeholder?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;number&lt;/code&gt;&lt;/a&gt; fields.&lt;/p&gt;

&lt;p&gt;To provide a placeholder, define the &lt;code&gt;placeholder&lt;/code&gt; config parameter with the text you want to show.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;CompanyInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your company name here...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5ba3jxj62t2gopuv1o9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5ba3jxj62t2gopuv1o9.png" alt="A Puck text field with the placeholder: " width="361" height="273"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step size for number fields
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;This was a contribution made by: &lt;a href="https://github.com/shannonhochkins" rel="noopener noreferrer"&gt;@shannonhochkins&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can now define a step value for &lt;a href="https://puckeditor.com/docs/api-reference/fields/number#step?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;number&lt;/code&gt;&lt;/a&gt; fields to control how much the value increases or decreases when using the input or keyboard arrow buttons. See &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/step" rel="noopener noreferrer"&gt;&lt;code&gt;step&lt;/code&gt;&lt;/a&gt; on MDN.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;EmptySpace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscvwl3a4n2vlxyt1bump.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fscvwl3a4n2vlxyt1bump.gif" alt="A Puck number field with a step size of two" width="363" height="363"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Hiding fields
&lt;/h3&gt;

&lt;p&gt;It's now possible to hide fields from the UI by setting the &lt;a href="https://puckeditor.com/docs/api-reference/fields/base#visible?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;visible&lt;/code&gt;&lt;/a&gt; field parameter to &lt;code&gt;false&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;hiddenField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;visible&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;defaultProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;hiddenField&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The field of this value is hidden&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hiddenField&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hiddenField&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcwgkyakmfcv56iyq3cq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmcwgkyakmfcv56iyq3cq.gif" alt="A Puck component with a hidden text field" width="805" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  New &lt;code&gt;RootConfig&lt;/code&gt; type
&lt;/h3&gt;

&lt;p&gt;The new &lt;a href="https://github.com/measuredco/puck/blob/638e066fb655bc3e258093bf2428189f3afd88f9/packages/core/types/Config.tsx#L69" rel="noopener noreferrer"&gt;&lt;code&gt;RootConfig&lt;/code&gt;&lt;/a&gt; type lets you type your &lt;a href="https://puckeditor.com/docs/api-reference/configuration/config#root?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;root configuration&lt;/a&gt; with its expected props when using TypeScript. If you've broken up your config, this'll help you keep everything type-safe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RootConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rootConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RootConfig&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;defaultProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;My Page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  New &lt;code&gt;replaceRoot&lt;/code&gt; action
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;replaceRoot&lt;/code&gt; action is now available in the &lt;a href="https://puckeditor.com/docs/api-reference/puck-api?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;Puck API&lt;/a&gt; &lt;a href="https://puckeditor.com/docs/api-reference/puck-api#dispatchaction?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;&lt;code&gt;dispatcher&lt;/code&gt;&lt;/a&gt;, making it possible to update only the &lt;a href="https://puckeditor.com/docs/api-reference/data-model/data#root?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;root&lt;/a&gt; data without using an expensive &lt;code&gt;set&lt;/code&gt; action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useGetPuck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@measured/puck&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RootTitleSetter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getPuck&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useGetPuck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleClick&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the dispatcher&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPuck&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Dispatch the action to update the root&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;replaceRoot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;rootData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;New Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;getPuck&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Set Title&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to upgrade
&lt;/h2&gt;

&lt;p&gt;Check out the &lt;a href="https://puckeditor.com/blog/upgrading-to-puck-019?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=0_19_release_post" rel="noopener noreferrer"&gt;upgrade guide&lt;/a&gt; for step-by-step instructions on upgrading to 0.19. It includes deprecated APIs, breaking changes, and common pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full changelog
&lt;/h2&gt;

&lt;p&gt;See the full changelog for all changes via the &lt;a href="https://github.com/measuredco/puck/releases/tag/0.19.0" rel="noopener noreferrer"&gt;GitHub release&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Contributors
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/artemisclyde" rel="noopener noreferrer"&gt;artemisclyde&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/camhammel" rel="noopener noreferrer"&gt;camhammel&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/chillenberger" rel="noopener noreferrer"&gt;chillenberger&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/christian-tchaikovsky" rel="noopener noreferrer"&gt;christian-tchaikovsky&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/DamianKocjan" rel="noopener noreferrer"&gt;DamianKocjan&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/leweyse" rel="noopener noreferrer"&gt;leweyse&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/lukstei" rel="noopener noreferrer"&gt;lukstei&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/matthewlynch" rel="noopener noreferrer"&gt;matthewlynch&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/shannonhochkins" rel="noopener noreferrer"&gt;shannonhochkins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/theophilhenry" rel="noopener noreferrer"&gt;theophilhenry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/tlahmann" rel="noopener noreferrer"&gt;tlahmann&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>puck</category>
    </item>
    <item>
      <title>How to Build a React Page Builder: Puck and Tailwind v4.0</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Wed, 26 Mar 2025 13:56:12 +0000</pubDate>
      <link>https://dev.to/puckeditor/how-to-build-a-react-page-builder-puck-and-tailwind-v40-5e3o</link>
      <guid>https://dev.to/puckeditor/how-to-build-a-react-page-builder-puck-and-tailwind-v40-5e3o</guid>
      <description>&lt;p&gt;If you’ve worked on content-heavy projects or low code tools before—especially alongside a headless CMS—you’ve probably faced this challenge: at some point, content teams want the ability to build and update pages without needing to go through a developer. That’s where website builders usually come in, but most of the ones out there are tied to a specific CMS or force you (and your built pages) into their ecosystem, making editor customization limited and migration hard, or expensive.&lt;/p&gt;

&lt;p&gt;That’s exactly the problem Puck was built to solve. &lt;strong&gt;Puck is an open source, React-first visual editor that works in any project, with &lt;em&gt;any&lt;/em&gt; CMS or back-end.&lt;/strong&gt; This means no vendor lock-in or forced decisions on your stack, it’s just a simple package you install, import, and render in your app like any other React component.&lt;/p&gt;

&lt;p&gt;And, because it’s just a component, you can use it alongside whatever you’re already using in your project—including Tailwind.&lt;/p&gt;

&lt;p&gt;In this tutorial, I’ll walk you through how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔨 Set up a page builder with Puck and Next.js&lt;/li&gt;
&lt;li&gt;🔌 Integrate Tailwind v4.0 for interactive styling&lt;/li&gt;
&lt;li&gt;📢 Create and publish web pages with it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  What Makes Puck Different
&lt;/h2&gt;

&lt;p&gt;If this is the first time you’re hearing about Puck, there are a few reasons why you might consider using it. &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/p/puck?tab=MIT-1-ov-file#readme" rel="noopener noreferrer"&gt;Open-source under an MIT license&lt;/a&gt;—You can use it in any project for free, personal or commercial.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/getting-started#render-the-editor?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;Self-hosted&lt;/a&gt;—It’s just a React component you can embed directly into your app, so Puck lives wherever your app does.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/api-reference/fields/external?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;Compatible with any headless CMS or backend&lt;/a&gt;—It renders any component you give it and provides external fields to connect to whatever content repository you’re using.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/extending-puck/custom-interfaces?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;Customizable and overridable&lt;/a&gt;—You can use the editor as-is, replace parts of the UI, or build your own custom interface on top of it.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://puckeditor.com/docs/extending-puck/plugins?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;Extensible with plugins&lt;/a&gt;—You can add official plugins, community ones, or build your own to create reusable fields, behaviors, or integrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core idea is simple: Puck gives you a visual editor to design and publish pages by dragging and dropping your React components. When a page is published, Puck outputs it as a &lt;a href="https://puckeditor.com/docs/api-reference/data?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;JSON object&lt;/a&gt;. You can then pass that to the &lt;a href="https://puckeditor.com/docs/api-reference/components/render?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;&lt;code&gt;Render&lt;/code&gt; component&lt;/a&gt; to render that page wherever you want.&lt;/p&gt;

&lt;p&gt;If you want to get a feel for how this setup works, you can try out the &lt;a href="https://demo.puckeditor.com/edit?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;live demo&lt;/a&gt; yourself.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltz2q6o68in3z9ewb3l4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fltz2q6o68in3z9ewb3l4.gif" alt="Puck editor demo" width="700" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s what that looks like in code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Editor.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Puck&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core/puck.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Define the components users can drag and drop in the editor&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;HeadingBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Define the editable fields for the component&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Render the component with the field values&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Provide an initial page to load into the editor (empty for new pages)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

&lt;span class="c1"&gt;// Save the page when the user clicks on Publish&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;save&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Replace this with a call to your backend&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Render the Puck editor&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Editor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Puck&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;initialData&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onPublish&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;save&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And to render a saved page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Page.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt; &lt;span class="c1"&gt;// Load this from your database&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Render&lt;/span&gt; &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you know what Puck is, why it’s worth your while, and how it works, let’s actually start building the page builder!&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Add Puck
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installing Puck
&lt;/h3&gt;

&lt;p&gt;First up, let’s install Puck. If you’re adding it to an existing project, you can install it directly via npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @puckeditor/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re starting from scratch, you can also use one of the &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes" rel="noopener noreferrer"&gt;Puck recipes&lt;/a&gt; to spin up a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-puck-app my-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will scaffold a new project called &lt;code&gt;my-app&lt;/code&gt;, feel free to swap that name for whatever fits your project.&lt;/p&gt;

&lt;p&gt;After running that command, you’ll be prompted to choose a recipe. Type &lt;code&gt;next&lt;/code&gt; for the &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; recipe, or &lt;code&gt;remix&lt;/code&gt; if you prefer &lt;a href="https://remix.run/" rel="noopener noreferrer"&gt;Remix&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this guide, I’m using the &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes/next" rel="noopener noreferrer"&gt;Next.js recipe&lt;/a&gt;, but if you’re adding Puck to an existing project, or using the Remix recipe, the next steps should still apply. You might just need to adjust the file paths to match your project folder structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Puck
&lt;/h3&gt;

&lt;p&gt;With Puck installed, let’s get the app running. If you used the recipe, you can start your development server using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Navigate into the new project folder&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app

&lt;span class="c"&gt;# Start the development server&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’ll spin up the app at &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. When you first open it, you’ll see a message prompting you to navigate to the editor to edit that specific page. To do that, just head over to: &lt;a href="http://localhost:3000/edit" rel="noopener noreferrer"&gt;http://localhost:3000/edit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now see the Puck editor, with a single &lt;code&gt;HeadingBlock&lt;/code&gt; component already in the canvas.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxizeiag61t2ec2mewh35.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxizeiag61t2ec2mewh35.png" alt="Editor with just the header in the canvas" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you click on the &lt;code&gt;HeadingBlock&lt;/code&gt; in the canvas, modify its title field, and hit Publish in the top-right corner, you’ll update your homepage at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; instantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q5sm2f5jol1g61pnfzh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7q5sm2f5jol1g61pnfzh.gif" alt="Updating the header and publishing the page" width="900" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also add &lt;code&gt;/edit&lt;/code&gt; at the end of any page URL to create or edit that page visually—whether it already exists or not. This works thanks to the Next.js &lt;a href="https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes#catch-all-segments" rel="noopener noreferrer"&gt;catch-all route&lt;/a&gt; (&lt;code&gt;app/[...puckPath]&lt;/code&gt;) that comes pre-configured with the Puck Next.js recipe.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;⚠️ &lt;strong&gt;Heads up:&lt;/strong&gt; By default, the Next.js recipe saves page data to your file system and leaves the editor open to anyone. That’s fine for local development, but if you’re deploying this to production, you’ll want to check the &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes/next#using-this-recipe" rel="noopener noreferrer"&gt;recipe docs&lt;/a&gt; for tips on authorizing your API routes and saving pages to a real database&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Typescript to Puck
&lt;/h3&gt;

&lt;p&gt;Before we start adding custom components, let’s take a quick look at the configuration object that comes with the recipe in &lt;code&gt;./puck.config.tsx&lt;/code&gt; to understand how Puck gets integrated with TypeScript.&lt;/p&gt;

&lt;p&gt;At the top of the file, you'll see a &lt;code&gt;Props&lt;/code&gt; type definition. This tells Puck’s &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration#typescript?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;&lt;code&gt;Config&lt;/code&gt;&lt;/a&gt; type what props each of your draggable components expects. In this case, we only have a single component—&lt;code&gt;HeadingBlock&lt;/code&gt;—and it just expects a &lt;code&gt;title&lt;/code&gt; string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// puck.config.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;HeadingBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is the actual Puck config object. The only difference with the previous JavaScript setup here is that we’re typing it with &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration#typescript?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;&lt;code&gt;Config&lt;/code&gt;&lt;/a&gt; to let TypeScript know which &lt;a href="https://puckeditor.com/docs/api-reference/configuration/config#components?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;components&lt;/a&gt; it expects and which &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#fields?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;fields&lt;/a&gt; each one of those components should define. It also uses &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#defaultprops?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;defaultProps&lt;/a&gt;, to define a default value for the title field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;HeadingBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;defaultProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s the basic setup with TypeScript! &lt;/p&gt;

&lt;p&gt;Keep in mind that, as your editor grows, you’ll definitely want to pull these nested component configuration objects to their own files, when you do that, you can type them by using the &lt;a href="https://github.com/puckeditor/puck/blob/0ea6ce41281710ceecdc5dee8a632c06004244f8/packages/core/types/Config.tsx#L14" rel="noopener noreferrer"&gt;&lt;code&gt;ComponentConfig&lt;/code&gt;&lt;/a&gt; type Puck exposes with the props you expect for the component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding Components to Puck
&lt;/h3&gt;

&lt;p&gt;Now that we’ve got Puck setup and ready, it’s time to make things more interesting. Let’s add a couple of new components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Card&lt;/code&gt;: to present information about a particular topic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Grid&lt;/code&gt;: to display a list of components in a grid, using CSS grid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this tutorial, the &lt;code&gt;Grid&lt;/code&gt; itself won’t need any props—it’s just going to be a layout container—but each &lt;code&gt;Card&lt;/code&gt; will allow users to input the title and description of the topic it's introducing, as well as the amount of padding it should have around its content.&lt;/p&gt;

&lt;p&gt;Here’s how to set this up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Add the new Grid and Card components to the &lt;code&gt;Props&lt;/code&gt; type&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;HeadingBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nl"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt; &lt;span class="c1"&gt;// No props needed for the grid itself&lt;/span&gt;
  &lt;span class="nl"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;//... existing setup&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Add the Grid component to the &lt;code&gt;config&lt;/code&gt; object&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To do this, we’ll use the &lt;a href="https://puckeditor.com/docs/integrating-puck/multi-column-layouts?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt; component. This component allows you to nest components within other components, which is useful for creating &lt;a href="https://puckeditor.com/docs/integrating-puck/multi-column-layouts?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;multi-column layouts&lt;/a&gt; using CSS Grid or Flexbox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DropZone&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Render a Grid DropZone where users are able to drag and drop components&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-grid"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;repeat(3, minmax(0, 1fr))&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;16px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Add the &lt;code&gt;Card&lt;/code&gt; component to the &lt;code&gt;config&lt;/code&gt; object&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DropZone&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

    &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Add the fields for the title, description and padding&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;textarea&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;min&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;64&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Add default values for each field&lt;/span&gt;
      &lt;span class="na"&gt;defaultProps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Topic Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Topic description...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Render the card using the values from its fields&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! If you head back to &lt;a href="http://localhost:3000/edit" rel="noopener noreferrer"&gt;http://localhost:3000/edit&lt;/a&gt;, you’ll now see &lt;code&gt;Grid&lt;/code&gt; and &lt;code&gt;Card&lt;/code&gt; in your component list. Go ahead and drop a Grid into the page and add a few Cards inside it, adjusting their titles, descriptions, and padding to see how it all comes together.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vnorh4lcmuzkfb23og2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1vnorh4lcmuzkfb23og2.gif" alt="Adding cards and grids to the editor" width="800" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Integrate Tailwind
&lt;/h2&gt;

&lt;p&gt;At this point, we’ve got a working page builder—components can be dragged in, props can be edited, and pages can be published. But visually, things are still pretty basic. Let’s fix that by adding Tailwind to the mix.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Tailwind
&lt;/h3&gt;

&lt;p&gt;Adding Tailwind to a project with Puck is the same as adding it to any React app. Since this tutorial uses Next.js, I’ll walk you through that setup, but if you’re using a different meta framework, you can follow the &lt;a href="https://tailwindcss.com/docs/installation/using-vite" rel="noopener noreferrer"&gt;official Tailwind instructions&lt;/a&gt; for your particular stack.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Install Tailwind and its dependencies&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;tailwindcss @tailwindcss/postcss postcss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Create a &lt;code&gt;./postcss.config.mjs&lt;/code&gt; file in the root of your project and add the Tailwind plugin to it&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./postcss.config.mjs&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tailwindConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@tailwindcss/postcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;tailwindConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Import Tailwind in &lt;code&gt;./app/styles.css&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/** ./app/styles.css **/&lt;/span&gt;

&lt;span class="k"&gt;@import&lt;/span&gt; &lt;span class="s1"&gt;"tailwindcss"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with that, Tailwind is now part of your project. Next, we’ll wire it up to your Puck components so you can start using it inside the editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Migrating to Tailwind
&lt;/h3&gt;

&lt;p&gt;Once Tailwind is installed, you can start swapping out the existing styles in your Puck components for Tailwind’s utility classes. Let’s do a quick cleanup by migrating the &lt;strong&gt;static inline styles&lt;/strong&gt; we were using in &lt;code&gt;HeadingBlock&lt;/code&gt; and &lt;code&gt;Grid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To do that, open &lt;code&gt;./puck.config.tsx&lt;/code&gt; and make the following changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;HeadingBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing configuration&lt;/span&gt;

      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="c1"&gt;// Replace the inline styles to make the text bigger and bold&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-4xl font-bold p-8"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="c1"&gt;// Replace the inline styles with Tailwind's equivalent&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt; &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-grid"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid grid-cols-3 gap-4 p-4"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;//... existing card configuration&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that’s done, you can navigate back to the editor and check that everything is styled as expected, if this doesn’t seem to be working you might need to reset your development server and invalidate your browser cache.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5c7hvw02hhht2qm286s.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5c7hvw02hhht2qm286s.gif" alt="Puck editor with tailwind styled components" width="1201" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Applying Tailwind Dynamically
&lt;/h3&gt;

&lt;p&gt;With Puck, you often want to change the appearance of a component based on the value of a field controlled by the user, just like we did earlier with the card padding.&lt;/p&gt;

&lt;p&gt;Doing this with Tailwind works great in most cases, but there’s one important thing to keep in mind: Tailwind doesn’t generate styles at runtime. Instead, &lt;strong&gt;it scans your code at build time&lt;/strong&gt;, finds any utility classes you’re using (like &lt;code&gt;text-center&lt;/code&gt; or &lt;code&gt;bg-red-500&lt;/code&gt;), and then includes only those in your final CSS bundle. &lt;a href="https://tailwindcss.com/docs/detecting-classes-in-source-files" rel="noopener noreferrer"&gt;Tailwind’s docs explain this in more detail&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That means if you try to do something like this using a field value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`grid-cols-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…it won’t work unless the class for every possible value of &lt;code&gt;columns&lt;/code&gt; is already present somewhere in your source code. That’s because Tailwind won’t recognize the dynamic class when it builds your CSS.&lt;/p&gt;

&lt;p&gt;Luckily, there are a few ways to work around this depending on how dynamic you want your styling to be. Let’s go through the options so you can pick the one that fits your setup best.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Predefined static classes and inline styling (simplest)
&lt;/h3&gt;

&lt;p&gt;If all you want to do is give users a set of predefined styles they can pick from, for example through a select or radio field, this is the simplest way to integrate Tailwind. To achieve this, &lt;strong&gt;hard-code the fixed list of classes&lt;/strong&gt; your components can use, the same way you would in any other project. This would make it so all the classes exist at build time, making it the safest and most performant option. &lt;/p&gt;

&lt;p&gt;Any other fully dynamic values the user controls directly—like the padding around the Card’s content—can still be passed using inline styling.&lt;/p&gt;

&lt;p&gt;To demonstrate this, let’s add a &lt;code&gt;variant&lt;/code&gt; field to the Card component so users can choose between an “outlined” or a “floating” card. The padding around the card content will stay as inline styles, so users can still enter any values they want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Add the variant prop in the Card type definition&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... existing components&lt;/span&gt;

  &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing props&lt;/span&gt;

    &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Add a &lt;a href="https://puckeditor.com/docs/api-reference/fields/select?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;select field&lt;/a&gt; for the Card variants with their corresponding Tailwind classes as values&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing components&lt;/span&gt;

    &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing configuration&lt;/span&gt;

      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//... existing fields&lt;/span&gt;

        &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shadow-md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Floating&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;border rounded-md&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Outlined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Add the variant prop to the Card class names&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing components&lt;/span&gt;

    &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing configuration&lt;/span&gt;

      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;      
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Pass in the variant prop here&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you now navigate to the editor and drag and drop a Card, you’ll be able to switch between an outlined Card or a floating one by using the new variant field.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1dbuyyptgbs7wvxh4f6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl1dbuyyptgbs7wvxh4f6.gif" alt="Switching outlined cards to floating ones in the editor" width="800" height="340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can repeat this setup with any other component you want. &lt;strong&gt;The key is always making sure the full class names exist somewhere in your code&lt;/strong&gt; so Tailwind can pick them up. &lt;strong&gt;Anything that’s truly dynamic&lt;/strong&gt;—like widths, heights, or grid spans—&lt;strong&gt;can stay inline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The only downside to this approach is that you won’t be able to use selectors, design tokens, or target states with the fully dynamic inline values.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Predefined dynamic classes (longer boilerplate)
&lt;/h3&gt;

&lt;p&gt;The previous setup works great for static class names, but it doesn’t solve the problem when you need to generate classes dynamically, which might be a requirement for doing things like selecting a specific color for a background like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`bg-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;colorOption&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Tailwind v3, you could achieve this by &lt;a href="https://v3.tailwindcss.com/docs/content-configuration#safelisting-classes" rel="noopener noreferrer"&gt;safelisting&lt;/a&gt; the class names you wanted in the final CSS bundle so that they were always included. You could even use &lt;a href="https://v3.tailwindcss.com/docs/content-configuration#using-regular-expressions" rel="noopener noreferrer"&gt;regular expressions&lt;/a&gt; to safelist whole sets of possible class variants in one line, making it easier to cover dynamic class names.&lt;/p&gt;

&lt;p&gt;In Tailwind v4.0, however, &lt;a href="https://tailwindcss.com/blog/tailwindcss-v4-alpha#:~:text=Safelists%20and%20blocklists%20%E2%80%94%20can%27t%20force%20Tailwind%20to%20generate%20certain%20classes%20or%20prevent%20it%20from%20generating%20other%20classes%20yet." rel="noopener noreferrer"&gt;doing that is no longer possible&lt;/a&gt;. To do this now, you need to &lt;strong&gt;safelist all the exact class names&lt;/strong&gt; you’ll be generating dynamically in a &lt;code&gt;.txt&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Here’s how you’d do that for adding background colors to the Card component in our setup:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Create a &lt;code&gt;./safelist.txt&lt;/code&gt; file that contains all the Tailwind classes you need to generate dynamically&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bg-inherit
bg-red-300
bg-yellow-100

...any other class names you need
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2: Import this file into your main stylesheet using &lt;code&gt;@source&lt;/code&gt; so Tailwind knows to include these classes in the final build&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ./app/styles.css */&lt;/span&gt;
&lt;span class="k"&gt;@source&lt;/span&gt; &lt;span class="s1"&gt;"../safelist.txt"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3: Add a &lt;code&gt;background&lt;/code&gt; field to the Card component, so users can choose a background color in the editor&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ./puck.config.tsx&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... existing components&lt;/span&gt;

  &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing props&lt;/span&gt;

    &lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing components&lt;/span&gt;

    &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing configuration&lt;/span&gt;

      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;//... existing fields&lt;/span&gt;

        &lt;span class="na"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;inherit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Inherit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yellow-100&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yellow&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red-300&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="c1"&gt;//... default props configuration&lt;/span&gt;

      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;background&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;padding&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Add the background class dynamically&lt;/span&gt;
            &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bg-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;background&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text-xl font-bold"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;article&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you now refresh your editor, drag and drop a card, and select a red or yellow background, you should see your card changing colors. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febxt2wvf5x6ov7y7tu6k.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Febxt2wvf5x6ov7y7tu6k.gif" alt="Switching card backgrounds" width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key of this approach is making sure &lt;strong&gt;all your dynamic class names always resolve to those defined inside the &lt;code&gt;safelist.txt&lt;/code&gt; file&lt;/strong&gt;. Here I showed you how to do it manually, but you could write scripts to generate some repetitive ones, like a range of paddings and margins, automatically.&lt;/p&gt;

&lt;p&gt;The downside of this approach is that every new dynamic class must be manually added, slowing down updates and increasing the risk of missing a class, which could lead to styling bugs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus Option: Import all classes by using the CDN (Heaviest)
&lt;/h3&gt;

&lt;p&gt;If you want to give users full control over things like padding, margin, colors, and fonts—purely using dynamic Tailwind classes—the previous option will quickly become unmanageable. You’d need to add hundreds of class names to your safelist just to support a handful of design fields.&lt;/p&gt;

&lt;p&gt;The only real workaround is to make &lt;strong&gt;every Tailwind class available at runtime&lt;/strong&gt;, and the way to do that is by loading Tailwind via its CDN. With the CDN, Tailwind doesn’t need to scan your files at build time—it ships all the styles up front and generates them on the fly in the browser. That means you can do things like &lt;code&gt;"p-${padding}"&lt;/code&gt; freely, without worrying about safelists or build time scans.&lt;/p&gt;

&lt;p&gt;The main disadvantage of this approach is that you’re importing all of Tailwind, all the time. Which adds around 220 KB to your pages, just for styling, most of which you’ll probably never use. It also makes your styling runtime-dependent, which means worse performance, especially on slower connections or less powerful devices.&lt;/p&gt;

&lt;p&gt;That’s why the &lt;a href="https://tailwindcss.com/docs/installation/play-cdn" rel="noopener noreferrer"&gt;Tailwind team doesn’t recommend this setup&lt;/a&gt;, and why I’m not going to cover it in depth here.&lt;/p&gt;

&lt;p&gt;If you still want to go for it, you can follow the &lt;a href="https://tailwindcss.com/docs/installation/play-cdn" rel="noopener noreferrer"&gt;official instructions here&lt;/a&gt;. Just be sure to remove Tailwind as an npm dependency, since you won’t be using it at build time anymore.&lt;/p&gt;

&lt;p&gt;Use this approach carefully, it gives you complete freedom, but you’ll pay for it in performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;This tutorial walked through how to combine Tailwind with Puck to build a flexible, component-based page builder. We covered different ways to handle component styling—from simple predefined classes and inline styling to fully dynamic values—so you can pick the right balance between flexibility, maintainability, and performance.&lt;/p&gt;

&lt;p&gt;Hopefully, this guide not only helped you set up Tailwind in Puck but also sparked some ideas for building your own visual editors—whether that’s for website builders, image editors, PDF generators, or something else entirely. The best thing about Puck is that it’s not just a page builder—it’s a component-based editor, so what your components represent and what you build with it is completely up to you.&lt;/p&gt;

&lt;p&gt;If you’re exploring this setup, working on something similar, or tackling something completely different, I’d love to chat and answer any questions you might have:&lt;/p&gt;

&lt;p&gt;👾 Join the conversation on &lt;a href="https://discord.gg/V9mDAhuxyZ" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🐦 Follow along for updates and sneak peeks on &lt;a href="https://x.com/puckeditor?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;X&lt;/a&gt; and &lt;a href="https://bsky.app/profile/puckeditor.com?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=page_builder_with_tailwind" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;⭐ Found this useful? Support Puck by giving us a star on &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=tailwind_integration" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;—it really helps!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>frontend</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Integrating a Page Builder with Contentful</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Thu, 20 Mar 2025 10:33:03 +0000</pubDate>
      <link>https://dev.to/puckeditor/integrating-a-page-builder-with-contentful-3ik</link>
      <guid>https://dev.to/puckeditor/integrating-a-page-builder-with-contentful-3ik</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is the open-source visual editor for React that you can embed in any application to create the next generation of page builders and no-code products. Give us a star on &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;! ⭐️&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Headless CMS platforms like &lt;a href="https://www.contentful.com/" rel="noopener noreferrer"&gt;Contentful&lt;/a&gt; have changed how we think about content management. Instead of locking you into a specific frontend, like traditional CMSs do, they let you store structured content and deliver it anywhere—websites, mobile apps, even IoT devices.&lt;/p&gt;

&lt;p&gt;But that flexibility comes with a trade-off: you don’t really know how your content will be presented. Unlike traditional CMSs that include WYSIWYG editors, headless CMSs require developers to build the frontend experience from scratch. This means content teams often need to rely on engineers just to tweak layouts, adjust styling, or add new sections to pages.&lt;/p&gt;

&lt;p&gt;What’s the solution to this? A dedicated page builder. A good page builder gives content teams the freedom to structure pages visually, while still keeping the content store separate, ensuring that every component in the system remains dynamic and modular. There are plenty of proprietary ones out there, but if you want full control over your tech stack, a CMS-agnostic page builder is the way to go.&lt;/p&gt;

&lt;p&gt;That’s where &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; comes in. Puck is a flexible, open-source page builder that you can embed directly into your React app. It’s completely unopinionated about how you store and retrieve content, making it compatible with virtually any backend (not just CMSs), including Contentful. This makes it a great fit if you want a dynamic editor that doesn’t tie you to a single platform.&lt;/p&gt;

&lt;p&gt;In this tutorial, I’ll show you how to integrate Puck with Contentful to bridge the gap between reusable structured content and a user-friendly page-building experience. By the end, you’ll learn how to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set up a React project with Puck&lt;/li&gt;
&lt;li&gt;Connect it to Contentful to import your content (blog posts)&lt;/li&gt;
&lt;li&gt;Build and publish pages directly within Puck so they can be rendered in your application &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By the time we're done, you’ll have a fully functional visual editor + headless CMS setup where users can write blog posts and build and edit pages dynamically—without writing a single line of code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwwlivcgkh69rys83ivt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhwwlivcgkh69rys83ivt.gif" alt="Demo of the final application" width="701" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before we get started, I’ll assume you have a basic understanding of Contentful. If you're new here, no worries—you’re welcome to follow along! However, I’d recommend checking out the &lt;a href="https://www.contentful.com/developers/" rel="noopener noreferrer"&gt;Contentful developer portal&lt;/a&gt; first to get familiar with the basics.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Set up Contentful&lt;/li&gt;
&lt;li&gt;Add Puck&lt;/li&gt;
&lt;li&gt;Connect Puck with Contentful&lt;/li&gt;
&lt;li&gt;Edit and Publish your Pages&lt;/li&gt;
&lt;li&gt;Taking your Page Builder Further&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  📝 1. Set up Contentful
&lt;/h2&gt;

&lt;p&gt;To begin, we need to define our content structure in Contentful. Since this tutorial focuses on integrating a page builder rather than setting up Contentful from scratch, I’ll keep this section brief. If you're new to Contentful, I recommend checking out their &lt;a href="https://www.contentful.com/help/getting-started/contentful-101/" rel="noopener noreferrer"&gt;official getting started guide&lt;/a&gt; to get familiar with creating &lt;a href="https://www.contentful.com/help/spaces/" rel="noopener noreferrer"&gt;spaces&lt;/a&gt;, &lt;a href="https://www.contentful.com/help/content-types/" rel="noopener noreferrer"&gt;content types&lt;/a&gt;, and &lt;a href="https://www.contentful.com/developers/docs/references/authentication/#:~:text=API%20keys%20in%20the%20Contentful%20web%20app" rel="noopener noreferrer"&gt;obtaining your API keys&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this simple blog app, you only need to define a single &lt;a href="https://www.contentful.com/help/content-types/" rel="noopener noreferrer"&gt;content type&lt;/a&gt; named &lt;strong&gt;Blog&lt;/strong&gt;, which represents the collection of blog posts you’ll publish and manage in your app. Here are the &lt;a href="https://www.contentful.com/help/fields/" rel="noopener noreferrer"&gt;fields&lt;/a&gt; you should add to it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Title&lt;/code&gt;: A required &lt;a href="https://www.contentful.com/help/fields/#:~:text=without%20any%20limitations.-,Text,-Plain%20text.%20You" rel="noopener noreferrer"&gt;text field&lt;/a&gt; for the blog post title&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Body&lt;/code&gt;: A required &lt;a href="https://www.contentful.com/developers/docs/concepts/rich-text/" rel="noopener noreferrer"&gt;rich text field&lt;/a&gt; for the blog content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9zg3kca5j7y823k263x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs9zg3kca5j7y823k263x.png" alt="The blog model in the contentful dashboard" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you create that content type you’ll be able to navigate to the “Content” section of your Contentful space to create new Blog posts based on this model:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F796pkzaejsm02199f1x8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F796pkzaejsm02199f1x8.gif" alt="Creating a new blog on contentful" width="760" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  👩‍💻 2. Add Puck
&lt;/h3&gt;

&lt;p&gt;After setting up Contentful, we can now set up the web application to build and render pages with our content. To do this, we’ll use &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; both for page building and rendering.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're completely new to Puck and want a broader introduction before diving in, check out our &lt;a href="https://puckeditor.com/docs/getting-started?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Getting Started&lt;/a&gt; guide.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Puck
&lt;/h3&gt;

&lt;p&gt;If you’re adding Puck to an existing project, you can install it with npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @puckeditor/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you’d rather start fresh, you can use one of the &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Puck recipes&lt;/a&gt; to quickly set up a new project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx create-puck-app my-blog
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new Puck project named my-blog (feel free to rename it). After running the command, you’ll be prompted to choose a recipe—type &lt;code&gt;next&lt;/code&gt; for the Next.js recipe or &lt;code&gt;remix&lt;/code&gt; for the Remix recipe.&lt;/p&gt;

&lt;p&gt;For this guide, I’ll assume you’ve used the generator and chosen the &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes/next?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Next.js recipe&lt;/a&gt;, however if you’re integrating Puck into an existing project, the next steps should still apply. Puck works with any React application, so you might just need to tweak file names and folder structures based on your preferred setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running Puck
&lt;/h3&gt;

&lt;p&gt;Once Puck is installed, you can start your development server. To do this run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Navigate to your new project&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;my-blog

&lt;span class="c"&gt;# Run the application&lt;/span&gt;
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will start a local development server on &lt;a href="http://localhost:3000/" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;. If you navigate to it, you’ll be shown a sample message that will prompt you to navigate to the editor. To access the Puck editor, head over to: &lt;a href="http://localhost:3000/edit" rel="noopener noreferrer"&gt;http://localhost:3000/edit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should now see the Puck editor, rendering a single component: &lt;code&gt;HeadingBlock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrwfvrz2uabfbihqb7q2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxrwfvrz2uabfbihqb7q2.png" alt="Puck editor showing the default home page" width="800" height="259"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;code&gt;HeadingBlock&lt;/code&gt; in the canvas, modify it’s content to whatever you want and hit Publish in the top-right corner to update your homepage at &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; instantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fluazxi6okpsg8dkrh5ct.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fluazxi6okpsg8dkrh5ct.gif" alt="Changing the default header in the home page" width="900" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also add &lt;code&gt;/edit&lt;/code&gt; at the end of any page URL to edit that page visually—whether it already exists or not. This works thanks to the &lt;a href="https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes" rel="noopener noreferrer"&gt;Next.js catch-all route&lt;/a&gt; (&lt;code&gt;app/[...puckPath]&lt;/code&gt;) that comes pre-configured with the Puck Next.js recipe.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;⚠️ &lt;strong&gt;Important:&lt;/strong&gt; By default, Puck’s Next.js recipe saves your page data in the file system and makes all pages editable by anyone. If you plan to deploy this app to production, be sure to check out the &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes/next#using-this-recipe?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;recipe documentation&lt;/a&gt; for best practices on securing API routes and storing page data in a real database.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Great, now we have our client app and our CMS ready! Next, we'll connect Contentful to Puck so we can create pages using the content we created in step one. &lt;/p&gt;

&lt;h2&gt;
  
  
  🔗 3. Connect Puck with Contentful
&lt;/h2&gt;

&lt;p&gt;For this step, we'll use the &lt;a href="https://github.com/puckeditor/puck/tree/main/packages/field-contentful?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;@puckeditor/field-contentful&lt;/code&gt;&lt;/a&gt; package, which provides a &lt;a href="https://puckeditor.com/docs/api-reference/fields/external?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Puck field&lt;/a&gt; that allows you to select &lt;a href="https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/entries" rel="noopener noreferrer"&gt;Contentful entries&lt;/a&gt; directly inside the editor.  This package is a convenience wrapper around Puck's &lt;a href="https://puckeditor.com/docs/api-reference/fields/external?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;external&lt;/code&gt;&lt;/a&gt; field type, handling the Contentful API integration. You can see the source code &lt;a href="https://github.com/puckeditor/puck/blob/main/packages/field-contentful/index.ts?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ll also install Contentful’s &lt;a href="https://www.npmjs.com/package/@contentful/rich-text-react-renderer" rel="noopener noreferrer"&gt;&lt;code&gt;rich-text-react-renderer&lt;/code&gt;&lt;/a&gt; package to properly render Contentful’s rich text field data with HTML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Install Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Start by installing the dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @puckeditor/field-contentful @contentful/rich-text-react-renderer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Define an &lt;code&gt;Article&lt;/code&gt; Component&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, add an &lt;code&gt;Article&lt;/code&gt; component to allow users to select and display a blog post entry in the pages they’re building. Open &lt;code&gt;/puck.config.tsx&lt;/code&gt;, and modify the Puck &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration#typescript?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;props&lt;/code&gt;&lt;/a&gt; &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration#typescript?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;type definition&lt;/a&gt; and &lt;a href="https://puckeditor.com/docs/api-reference/configuration/config?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;config&lt;/code&gt;&lt;/a&gt; to define the new component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... existing props&lt;/span&gt;

  &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// TODO: add the Article props&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... TODO: add the component config&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Add the Field to Select Contentful Entries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside the &lt;code&gt;Article&lt;/code&gt; component definition, use the &lt;code&gt;createFieldContentful&lt;/code&gt; function from &lt;a href="https://github.com/puckeditor/puck/tree/main/packages/field-contentful?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;@puckeditor/field-contentful&lt;/code&gt;&lt;/a&gt; to create a field that lets users select a blog post from Contentful. Define this field under a &lt;code&gt;data&lt;/code&gt; prop in the &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#fields?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;Article&lt;/code&gt; component's fields&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createFieldContentful&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Entry&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/field-contentful&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Define the expected props for the Article component&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ArticleProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Expect contentful entries as article data&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... existing props&lt;/span&gt;

  &lt;span class="c1"&gt;// Replace the empty object definition with the actual type&lt;/span&gt;
  &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ArticleProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;createFieldContentful&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ArticleProps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-SPACE-ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-ACCESS-TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;🔹 &lt;strong&gt;Important:&lt;/strong&gt; Replace &lt;code&gt;YOUR-SPACE-ID&lt;/code&gt; and &lt;code&gt;YOUR-ACCESS-TOKEN&lt;/code&gt; with your actual Contentful &lt;a href="https://www.contentful.com/help/spaces/find-space-id/" rel="noopener noreferrer"&gt;space ID&lt;/a&gt; and &lt;a href="https://www.contentful.com/developers/docs/references/authentication/#:~:text=API%20keys%20in%20the%20Contentful%20web%20app" rel="noopener noreferrer"&gt;API token&lt;/a&gt;. Storing these in &lt;a href="https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; (&lt;code&gt;.env&lt;/code&gt;) is recommended.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Render the Selected Blog Post&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Once a blog post is selected via the &lt;code&gt;data&lt;/code&gt; field, the &lt;code&gt;data&lt;/code&gt; prop will be provided to the &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#renderprops?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;component’s &lt;code&gt;render&lt;/code&gt;&lt;/a&gt; function for rendering. Modify the &lt;code&gt;Article&lt;/code&gt;’s &lt;code&gt;render&lt;/code&gt; function to show the contents of the blog post in the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;documentToReactComponents&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@contentful/rich-text-react-renderer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing field setup&lt;/span&gt;

      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// If the user selected a blog entry,&lt;/span&gt;
        &lt;span class="c1"&gt;// render it&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Arial, sans-serif&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;hr&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
            &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;documentToReactComponents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;No selected content&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that everything is set up, navigate to &lt;a href="http://localhost:3000/edit" rel="noopener noreferrer"&gt;http://localhost:3000/edit&lt;/a&gt; and try the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Drag and drop an &lt;code&gt;Article&lt;/code&gt; component onto the editor&lt;/li&gt;
&lt;li&gt;Click the component and select a blog post from Contentful&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;title&lt;/code&gt; and &lt;code&gt;body&lt;/code&gt; of the selected blog post should now appear in the editor preview&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvtfpsr6d3auc179qay3.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffvtfpsr6d3auc179qay3.gif" alt="Puck editor fetching content from contentful through the Article component" width="1000" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reusing Components
&lt;/h3&gt;

&lt;p&gt;Let's extend the &lt;code&gt;Article&lt;/code&gt; component to support different types of content. To demonstrate this, we'll create a separate &lt;strong&gt;About&lt;/strong&gt; content type, and generalize the &lt;code&gt;Article&lt;/code&gt; component so that it works with multiple types. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Create the About content type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In Contentful, create an &lt;strong&gt;About&lt;/strong&gt; content type with the same fields as the &lt;strong&gt;Blog&lt;/strong&gt; type:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Title&lt;/code&gt;: a required text field for the section title&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Body&lt;/code&gt;: a required rich text field for the section content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;💡 &lt;strong&gt;Pro Tip:&lt;/strong&gt; While you can handle different content type structures using the &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt; API, keeping &lt;strong&gt;consistent field names&lt;/strong&gt; across related content types—like in this example—makes components in Puck &lt;strong&gt;instantly reusable&lt;/strong&gt;. Without any extra implementation, new content types will work automatically with your existing components.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Define your own Contentful Client&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Previously, the &lt;code&gt;@puckeditor/field-contentful&lt;/code&gt; package created the Contentful client internally for us. However, since we now need to retrieve the available content types in our space dynamically, we’ll need to create the client manually and use it for fetching them.&lt;/p&gt;

&lt;p&gt;To do this, modify your &lt;code&gt;createFieldContentful&lt;/code&gt; setup to explicitly create the Contentful client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createFieldContentful&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Import the client builder&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/field-contentful&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Explicitly create the Contentful client&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;contentfulClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-SPACE-ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR-ACCESS-TOKEN-HERE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;createFieldContentful&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ArticleProps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Pass the previously created client&lt;/span&gt;
          &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentfulClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Move the Contentful Field Definition Inside &lt;code&gt;resolveFields&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Next, you'll want to make the fields dynamic, so that the content type passed to the Contentful field definition can be changed based on the value of another field.&lt;/p&gt;

&lt;p&gt;To do this, migrate your field definition to the &lt;a href="https://puckeditor.com/v/canary/docs/api-reference/configuration/component-config#resolvefieldsdata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;resolveFields&lt;/code&gt;&lt;/a&gt; API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import the Fields type&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Fields&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@puckeditor/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolveFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;newFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fields&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ArticleProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;createFieldContentful&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ArticleProps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentfulClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newFields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Let Users Select a Content Type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, add a &lt;code&gt;contentType&lt;/code&gt; prop to the Article component config, and configure it with a &lt;a href="https://puckeditor.com/docs/api-reference/fields/select?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;select&lt;/code&gt;&lt;/a&gt; field that's populated with a list of all the available content types from Contentful.&lt;/p&gt;

&lt;p&gt;When the user changes the value of the &lt;code&gt;contentType&lt;/code&gt; select field, pass it to the &lt;code&gt;createFieldContentful&lt;/code&gt; call in &lt;code&gt;resolveFields&lt;/code&gt;, so the &lt;code&gt;data&lt;/code&gt; field now points to a different content type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ArticleProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;Entry&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Add a prop for selecting a content type&lt;/span&gt;
  &lt;span class="nl"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Config&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;resolveFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// Fetch all available content types from Contentful&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contentfulClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContentTypes&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;newFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Fields&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ArticleProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;// Create a dropdown field for selecting a content type&lt;/span&gt;
          &lt;span class="na"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
              &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Select a content type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
              &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;types&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
                &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="p"&gt;})),&lt;/span&gt;
            &lt;span class="p"&gt;],&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="c1"&gt;// If a content type is selected, add an entry picker for it&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;newFields&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createFieldContentful&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ArticleProps&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;contentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;contentfulClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newFields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="c1"&gt;//... existing render function&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, navigate to the editor in the browser, drag and drop an article, choose a content type, and then select the actual entry of that content type you want to render:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61tn306r32fi5xz8jlt7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61tn306r32fi5xz8jlt7.gif" alt="Article component being reused for both Blog content type entries and About ones" width="760" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  📰 4. Edit and Publish your Pages
&lt;/h2&gt;

&lt;p&gt;Now that everything is set up, you can start building and publishing blog posts directly from your app.&lt;/p&gt;

&lt;p&gt;For example, if you want to add a new post about, say, the &lt;a href="https://dev.to/puckeditor/top-5-drag-and-drop-libraries-for-react-24lb"&gt;Top 5 Drag and Drop Tools for React&lt;/a&gt;, all you need to do is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new blog entry in Contentful for it&lt;/li&gt;
&lt;li&gt;Navigate to &lt;code&gt;http://localhost:3000/top-5-drag-and-drop-libraries-for-react/edit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Drag and drop an &lt;code&gt;Article&lt;/code&gt; component onto the page&lt;/li&gt;
&lt;li&gt;Select the “Blog” content type and pick your new post from the Contentful entries&lt;/li&gt;
&lt;li&gt;Preview the page, tweak the layout if needed, and hit "Publish" in the top-right corner&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That’s it! Your new page is now live and accessible at &lt;code&gt;http://localhost:3000/top-5-drag-and-drop-libraries-for-react&lt;/code&gt; 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Taking your Page Builder Further
&lt;/h2&gt;

&lt;p&gt;If you want to expand this setup, here are a few ideas to take it to the next level:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Add a styling library&lt;/strong&gt; – For larger editors, using a styling library like &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; or &lt;a href="https://emotion.sh/" rel="noopener noreferrer"&gt;Emotion&lt;/a&gt; can streamline your workflow, reduce boilerplate, and help maintain a consistent design system. Puck even has an &lt;a href="https://puckeditor.com/docs/extending-puck/plugins#official-plugins?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Emotion plugin&lt;/a&gt; that makes integration seamless if you’re using that library&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build dynamic blog post listing&lt;/strong&gt; – While this tutorial covered rendering a single blog post page, you’ll probably want to let users browse all posts. For that, you can apply the same reusability concepts from this tutorial to create a &lt;code&gt;ContentList&lt;/code&gt; component that dynamically lists entries from any content type&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enable content authoring in Puck&lt;/strong&gt; – Instead of switching between Puck and Contentful, you could integrate &lt;a href="https://www.contentful.com/developers/docs/references/content-management-api/" rel="noopener noreferrer"&gt;Contentful’s Content Management API&lt;/a&gt; with Puck’s &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#resolvedatadata-params?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;&lt;code&gt;resolveData&lt;/code&gt;&lt;/a&gt; function to allow users to author content directly inside the page builder&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Republish the page if the content changes&lt;/strong&gt; – One limitation of the current setup is that, if you change your content on Contentful, that won’t be reflected on your published Pages. This is because, by default, Puck fetches and copies the content when you first select it in the editor. To fix this, you can use the &lt;code&gt;resolveData&lt;/code&gt; function in the Article component to fetch the latest content from Contentful whenever the component renders. Check out our guide on how to implement this &lt;a href="https://puckeditor.com/docs/integrating-puck/external-data-sources#data-syncing?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔚 Wrapping up
&lt;/h2&gt;

&lt;p&gt;In this tutorial, I walked you through a simple way to integrate Puck with Contentful, but this is just one of many possible approaches. Since Puck is unopinionated, you have full control over how you structure your page management. Maybe you’d prefer a centralized admin dashboard instead of an &lt;code&gt;/edit&lt;/code&gt; URL, or maybe you need to separate your editing and rendering environments entirely across two different applications and domains. It’s completely up to you.&lt;/p&gt;

&lt;p&gt;Either way, I hope this guide helped you get started with Puck + Contentful—and maybe even sparked some ideas for your next project.&lt;/p&gt;

&lt;p&gt;I’d love to hear what you’re building! Whether you have questions, feedback, or just want to bounce around some ideas, here’s how you can connect with me and the Puck team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Found Puck exciting and useful? Show your support with a 🌟 on &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;—it helps us keep building the tools you love&lt;/li&gt;
&lt;li&gt;Share feedback, explore community plugins, or just hang out with us on &lt;a href="https://discord.gg/V9mDAhuxyZ?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; 💬&lt;/li&gt;
&lt;li&gt;Follow along for updates, development sneak-peeks, and events on &lt;a href="https://bsky.app/profile/puckeditor.com?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt; and &lt;a href="https://x.com/puckeditor?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=integrating_page_builder_with_contentful" rel="noopener noreferrer"&gt;X&lt;/a&gt; 👀&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And of course, if you have any questions or comments, drop them below. I’m always happy to chat!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cms</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Top 5 Drag-and-Drop Libraries for React in 2025</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Thu, 20 Feb 2025 13:09:17 +0000</pubDate>
      <link>https://dev.to/puckeditor/top-5-drag-and-drop-libraries-for-react-24lb</link>
      <guid>https://dev.to/puckeditor/top-5-drag-and-drop-libraries-for-react-24lb</guid>
      <description>&lt;p&gt;Drag-and-drop is one of those features that seems simple until you actually have to build it. Then, you will suddenly find yourself dealing with event listeners, state management, performance issues, accessibility concerns, and the infamously inconsistent HTML5 drag-and-drop API. All that effort just to let users move something from point A to B—an interaction that's been around since the '80s.&lt;/p&gt;

&lt;p&gt;Thankfully, if you're working in React, you don’t have to start from scratch. You’ve got a ton of packages available on NPM to choose from, each with its own trade-offs. Some make drag-and-drop a breeze, while others demand more setup but give you fine-grained control.&lt;/p&gt;

&lt;p&gt;We've done more than our fair share of exploring solutions for &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_dnd_tools" rel="noopener noreferrer"&gt;Puck&lt;/a&gt;, our drag-and-drop page builder for React. We needed something flexible, performant, and easy to work with—but finding the right fit wasn’t straightforward. We tried using existing libraries, forking them, and even building our own before settling on an approach with support for CSS grid and flexbox:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77s433kbj4vi4tvjlxuy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F77s433kbj4vi4tvjlxuy.gif" alt="demo" width="750" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you're looking for a drag-and-drop library to build a visual editor, give &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_dnd_tools" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; a try, and if you like it, we'd love a star on &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_dnd_tools" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After going through that process, we thought: &lt;em&gt;why not share what we learned?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So here’s a breakdown of the top five React drag-and-drop libraries we explored, their pros and cons, and when you should (or shouldn’t) use them. Whether you're building a no-code editor, a layout system, or a kanban board, this list should help you pick the right tool for the job.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;If you’re looking for a quick comparison, here’s how each of these libraries stack up against one another:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Library&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Customizability&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Ease of Use&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Maintenance Status&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Community Support&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;dnd-kit&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;td&gt;Maintained&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;hello-pangea/dnd&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Maintained&lt;/td&gt;
&lt;td&gt;Large&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pragmatic-drag-and-drop&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Intermediate&lt;/td&gt;
&lt;td&gt;Maintained&lt;/td&gt;
&lt;td&gt;Emerging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gridstack&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Challenging&lt;/td&gt;
&lt;td&gt;Maintained&lt;/td&gt;
&lt;td&gt;Niche&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;formkit/drag-and-drop&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Easy&lt;/td&gt;
&lt;td&gt;Experimental&lt;/td&gt;
&lt;td&gt;Niche&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🏗 Picking the right drag-and-drop library
&lt;/h2&gt;

&lt;p&gt;Before we get into the list, let’s talk about how to make the right choice. &lt;/p&gt;

&lt;p&gt;Engineering is all about trade-offs, and picking a drag-and-drop library is no different. Some libraries are lightweight and flexible, others offer plug-and-play simplicity, and then there are those that look great—until you realize they haven’t been updated in years.&lt;/p&gt;

&lt;p&gt;That’s why, to build this list, I focused on balancing four key factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; – Dragging should feel smooth, not laggy. Poor performance kills the whole experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ease of Integration&lt;/strong&gt; – Does it play well with React? How much setup does it need? Will I have to fight it to make it work in my project?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt; – Can it handle lists, grids, multiple containers? Does it support touch and accessibility? What if I need custom interactions—can I extend it?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance &amp;amp; Community&lt;/strong&gt; – Is it actively maintained, or am I about to commit to something that’s already a ghost town?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that let’s take a look at the best options available today for adding drag-and-drop functionality to your React app.&lt;/p&gt;

&lt;h2&gt;
  
  
  dnd-kit
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dndkit.com/" rel="noopener noreferrer"&gt;dnd-kit&lt;/a&gt; is a modern, lightweight toolkit for building drag-and-drop experiences in React. This library takes a unique approach because it doesn’t give you a pre-built DnD system—it's a framework you can use to build your own. This means you get full control over the DnD behavior, styling, and interactions, without being boxed into a rigid API. That flexibility is exactly why we decided to use it for Puck.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dczqvcanwqhcf7w93dg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0dczqvcanwqhcf7w93dg.gif" alt="dnd-kit.gif" width="600" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅&lt;/strong&gt; dnd-kit’s strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 Highly customizable and extensible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of dnd-kit's biggest strengths is it’s customizability. It’s not a one-size-fits-all solution—it’s a toolkit. This means you get solid defaults out of the box, while also having fine-grained control over almost every aspect of the drag-and-drop experience. You can tweak collision detection algorithms, define custom animations, handle transitions, and adjust behaviors to fit your exact needs. &lt;/p&gt;

&lt;p&gt;For example, in Puck, we needed to customize the DnD interactions and collision algorithms to make our experience usable for multi column drag-and-drop across flex and grid containers. We initially went with another library, but quickly realized we couldn’t customize it to the level we needed and switched to dnd-kit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Highly performant&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While other tools will sometimes drop FPS while dragging, dnd-kit, when used right, feels extremely smooth and satisfying. Animations and rearrangements behave as expected, reinforcing that sense of "physicality" modern DnD interactions rely on. &lt;/p&gt;

&lt;p&gt;This strength has made it particularly valuable for our use in Puck. This is because, with design tools, positioning performance and reliable previewing is extremely important, users will likely do this multiple times per minute and will require pixel level precision. Any delay or misplaced drop will frustrate them, break their immersion, and disrupt their workflow. Luckily with dnd-kit, you don’t need to worry about it too much.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Active maintenance and community support&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The community around dnd-kit is also something worth mentioning. With millions of downloads on NPM, it’s an actively maintained project with lots of community support. If you run into issues or need advice, chances are you’ll find answers pretty quickly. &lt;/p&gt;

&lt;p&gt;And since it’s actively maintained, you can rely on regular updates and fixes, you’re not going to be stuck with an outdated or abandoned library. Even better, the maintainers accept contributions, including several of our proposals to the experimental version during our Puck integration. That level of community support makes it a solid choice for long-term projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Accessible features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;dnd-kit comes with extensible built-in support for multiple input methods, including mouse, keyboard, touch, and pointer events. This means you’re not locked into just one way of interacting with drag-and-drop elements—users can use what they have and works best for them.&lt;/p&gt;

&lt;p&gt;It also provides extensible screen reader support, which is a big plus for accessibility. Many drag-and-drop libraries ignore this, but dnd-kit gives you the flexibility to improve usability for everyone, helping your project meet the quality standards you need without requiring you to build everything from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ dnd-kit’s limitations
&lt;/h3&gt;

&lt;p&gt;🔻 &lt;strong&gt;Requires more boilerplate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;dnd-kit gives you a lot of control, but that comes at a cost—&lt;em&gt;more setup&lt;/em&gt;. Unlike some drag-and-drop libraries that offer a quick plug-and-play experience, dnd-kit requires you to put the pieces together yourself. This is great when you need something highly customizable, but if you're just trying to build something simple, like a basic file uploader, it might feel like overkill. If you’re after a quick solution for a straightforward use case, you might want to consider whether the flexibility is worth the trade-off.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Moderate learning curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;dnd-kit gives you a solid foundation to build drag-and-drop interactions, but it doesn’t hold your hand. While it does provide sensible defaults to help you get started, understanding how it manages interactions and state can take some time—especially if you need to customize it and are new to React or haven’t worked with drag-and-drop libraries before. Having said that, the team is working on a new version that improves the learning experience and will come with a great new docs site.&lt;/p&gt;

&lt;p&gt;Since it’s more of a toolkit than a prebuilt solution, you’ll need to think through how you want to structure your drag-and-drop logic. If you're used to libraries that provide higher-level abstractions, dnd-kit might feel a bit lower-level than expected. &lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;No support for dragging from desktop or other windows&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since dnd-kit isn't built on top of the HTML5 drag-and-drop API, it doesn’t support dragging files or elements between different windows or from the desktop. If your use case involves something like dragging a file from your computer into the app or moving elements between separate browser windows, you’ll need to look into other solutions.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;dnd-kit is ideal for projects that need fine-grained control over drag-and-drop interactions. If you need to tweak DnD behaviors, implement custom animations, or optimize performance in complex UIs, dnd-kit is a solid choice. However, if your needs are simpler—like a basic file uploader or a to-do list where items just need to be rearranged—dnd-kit might feel like overkill. In those cases, a library with more built-in functionality or one that leverages the HTML5 API could be a better fit.&lt;/p&gt;

&lt;h2&gt;
  
  
  hello-pangea/dnd
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/hello-pangea/dnd" rel="noopener noreferrer"&gt;hello-pangea/dnd&lt;/a&gt; is a fork of &lt;a href="https://github.com/atlassian/react-beautiful-dnd" rel="noopener noreferrer"&gt;react-beautiful-dnd&lt;/a&gt;, the legendary DnD library originally developed by Atlassian. It was designed specifically for list-based drag-and-drop interactions, prioritizing accessibility and smooth animations in kanban style UIs, like Trello.&lt;/p&gt;

&lt;p&gt;After Atlassian stopped maintaining react-beautiful-dnd, the community created the hello-pangea/dnd fork. So, while it's still actively maintained, it’s worth noting that Atlassian is no longer involved, serving primarily as a stable fork and great stopgap while exploring other options.&lt;/p&gt;

&lt;p&gt;Atlassian has since released a new DnD library, pragmatic-drag-and-drop, which we’ll cover later, so for this section we’ll focus on hello-pangea/dnd.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fce9yvg2qmc1f6hny142u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fce9yvg2qmc1f6hny142u.gif" alt="hello-pangea.gif" width="391" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅&lt;/strong&gt; hello-pangea/dnd’s strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 Complete set of out-of-the-box features for lists&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The original library was built specifically to power drag-and-drop interactions in Atlassian’s products, so it excels at handling list-based DnD experiences. If you’re building a kanban board, task manager, or any kind of list-based UI, this library is one of the best fits you’ll find. &lt;/p&gt;

&lt;p&gt;Compared to other tools in this list, it provides a higher-level abstraction, taking care of all the heavy lifting so you don’t have to implement common behaviors from scratch. Right out of the box, it supports things like drag-and-drop within vertical and horizontal lists, multi item drag-and-drop, and the ability to move items between different lists. &lt;/p&gt;

&lt;p&gt;All this makes it a solid option for applications that require a straightforward DnD experience without a lot of customization for sortable lists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Feels natural and intuitive&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the standout aspects of hello-pangea/dnd is how polished and natural the drag-and-drop interactions feel. The library was designed around the idea of physicality—dragging an item should feel like moving a real object, with smooth animations, proper placeholders, and well-weighted collisions.&lt;/p&gt;

&lt;p&gt;A lot of work went into optimizing these interactions to feel predictable and satisfying. Items don't just teleport around the screen; they move in ways that respect momentum and spatial constraints, making the experience intuitive for users. For apps where drag-and-drop is a core interaction—like kanban boards or task management tools—this level of polish can go a long way in improving the overall UX.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Accessible out of the box&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;hello-pangea/dnd comes with built-in support for multiple input methods, including mouse, keyboard, and touch, making it easier to ensure an inclusive experience without extra work. It also includes screen reader support, which is essential if you're building something that needs to meet accessibility standards.&lt;/p&gt;

&lt;p&gt;Compared to other tools in this list, these features are available by default so you don’t have to spend time implementing or configuring them yourself. This can be a real time-saver if you're on a tight deadline, or don’t have experience building accessible interfaces and want a DnD library that handles it for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Gentle learning curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;hello-pangea/dnd is a higher-level abstraction compared to more flexible libraries like dnd-kit, which means you can get up and running faster with less configuration. If all you need is sortable lists, you don’t have to worry about setting up complex drag behaviors or handling reordering logic—it’s already built in. So, if you don’t want to spend time fine-tuning every aspect of the experience, this library can be a solid choice since it lets you focus on the core functionality without unnecessary complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Battle tested&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;hello-pangea/dnd is a well-established library with a solid foundation. Originally built as &lt;strong&gt;react-beautiful-dnd&lt;/strong&gt; by Atlassian, it has been used in production by large-scale applications for years. Since it’s been around for a while, you can expect &lt;strong&gt;stability&lt;/strong&gt; and a &lt;strong&gt;wealth of resources&lt;/strong&gt;—whether it’s community discussions, blog posts, or GitHub issues with solutions to common problems.&lt;/p&gt;

&lt;p&gt;If you're looking for a battle-tested solution for &lt;strong&gt;list-based drag-and-drop&lt;/strong&gt;, this is one of the most reliable options out there.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ hello-pangea/dnd’s limitations
&lt;/h3&gt;

&lt;p&gt;🔻 &lt;strong&gt;Limited Experience&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;hello-pangea/dnd is strictly designed for lists—and that’s where it shines. However, if your use case involves grids, multi-column layouts, or freeform drag-and-drop interactions, this library won’t cut it. There are no plans for grid support, so if you need that kind of flexibility, other tools in this list will probably be a better option.&lt;/p&gt;

&lt;p&gt;At Puck, we previously used a custom fork of hello-pangea/dnd with iframe support, but when we wanted to introduce multi-column support to allow users to drag components across CSS Grid and Flexbox layouts, we quickly ran into limitations that made it impractical for our needs.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Heavy weight and not as efficient as other options&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While hello-pangea/dnd is solid for list-based drag-and-drop, it comes with a performance trade-off. The original developers at Atlassian have acknowledged that some design choices—like its high-level abstraction and built-in features—added extra overhead, making it less efficient in certain cases. On top of that, unlike more modular libraries, hello-pangea/dnd requires you to import the entire package to use it. This can make it heavier than necessary, especially for larger applications. &lt;/p&gt;

&lt;p&gt;If your project involves complex, deeply nested lists or requires highly optimized page loading times to increase time to interactive (TTI), you might find pragmatic-drag-and-drop (the “official evolution” of this library) or other alternatives to be more lightweight and performant.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Higher-level abstraction with limited customization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because hello-pangea/dnd is designed to be easy to use, it trades off flexibility for simplicity. While this is great for straightforward use cases like Kanban boards, it doesn’t give you deep control over DnD interactions. If you need to modify drag behaviors beyond what the library allows, you might hit a wall. In our case at Puck, we needed a more flexible solution that could handle multi-column layouts and custom collision detection algorithms, which led us to explore other options.&lt;/p&gt;

&lt;p&gt;For basic list-based drag-and-drop, this isn’t a dealbreaker, but if you need fine-grained control, you may find yourself forking the repository or searching for another solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;hello-pangea/dnd is a solid choice if your use case revolves around lists and you want an easy-to-use solution that works with minimal setup. If you're building a kanban board, task manager, or any UI where users need to reorder items within and across lists, this library gets the job done without much friction.&lt;/p&gt;

&lt;p&gt;That said, if you need more flexibility—like grid support, custom animations, or fine-grained control over interactions—then dnd-kit (especially with its sortable package) or pragmatic-drag-and-drop might be better suited for your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  pragmatic-drag-and-drop
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://atlassian.design/components/pragmatic-drag-and-drop/about" rel="noopener noreferrer"&gt;pragmatic-drag-and-drop&lt;/a&gt; is Atlassian’s attempt at solving the problems they ran into with react-beautiful-dnd, their original drag-and-drop library. Unlike its predecessor, this new library takes a headless approach, meaning it doesn’t impose any UI constraints—you’re free to implement your own drag-and-drop interface however you see fit. It’s designed to be more flexible, performant, and framework-agnostic, so it works with any front-end stack, not just React.&lt;/p&gt;

&lt;p&gt;The biggest difference this library has with other tools is that it’s built on top of the native HTML5 drag-and-drop API. This is a bold move because the HTML5 DnD API has a reputation for being inconsistent and tricky to work with. Instead of avoiding it, Atlassian built a wrapper around it, abstracting away the common issues and providing a unified, cross-browser API that makes it much easier to work with.&lt;/p&gt;

&lt;p&gt;This approach makes pragmatic-drag-and-drop lightweight and efficient, but it also comes with some trade-offs—particularly in terms of user experience. If you’re considering this library, you’ll want to weigh those carefully.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06fuhe2ieh14ooo3h5fn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F06fuhe2ieh14ooo3h5fn.gif" alt="pragmatic.gif" width="600" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;✅&lt;/strong&gt; pragmatic-drag-and-drop’s strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 Highly customizable and extensible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because pragmatic-drag-and-drop is headless, you’re not locked into a specific UI or behavior—it just gives you the core drag-and-drop logic, and lets you decide how to present it. This makes it highly customizable since you can integrate it with any components or styles you already have in your code base.&lt;/p&gt;

&lt;p&gt;It also provides optional visual outputs as separate packages, meaning that while it doesn’t come with a built-in UI, it does offer some helper utilities for rendering drag previews or drop indicators if you need them. This balance between flexibility and convenience makes it a solid option if you want a lightweight library that doesn’t force you into a specific structure but still provides helpful tools to speed up development.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Framework agnostic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike most drag-and-drop libraries in this list, pragmatic-drag-and-drop works with any front-end framework. Since it’s based on the native HTML5 drag-and-drop API, it doesn’t rely on React’s internal state management or hooks, making it easy to integrate into Vue, Svelte, Angular, or any other environment.&lt;/p&gt;

&lt;p&gt;If you’re working on a multi-framework project or want the flexibility to switch frameworks down the road, this could be a major advantage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Highly performant and lightweight&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In terms of performance, pragmatic-drag-and-drop was built with special care for it to be fast and lightweight, introducing multiple techniques to bundle only the code you need, and avoid unnecessary rendering in server components. This is partly because it’s built on top of the native HTML5 drag-and-drop API and it doesn’t carry the overhead of other, more feature-heavy libraries. &lt;/p&gt;

&lt;p&gt;When compared with dnd-kit, for example, which is also built with high performance in mind, pragmatic-drag-and-drop does come ahead in terms of bundle size. So, if you are building a project where time to interactive is a priority, this option might be the best one for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Accessibility features with defaults&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As the official evolution of react-beautiful-dnd, this library also comes with extensible accessibility features right out of the box. The default assistive controls are based on the &lt;a href="https://atlassian.design/" rel="noopener noreferrer"&gt;Atlassian Design System&lt;/a&gt;, so if you’re already using that, integration will be seamless. But if you aren’t, you can easily replace those components with your own, or completely redefine how accessibility is provided and take a more customized approach based on your project's needs. This makes it a strong contender if your project has specific accessibility requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ pragmatic-drag-and-drop’s limitations
&lt;/h3&gt;

&lt;p&gt;🔻 &lt;strong&gt;Limited visual feedback&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because this library relies on the HTML5 drag-and-drop API, it doesn’t offer the same level of smooth, interactive feedback that other libraries do. There’s no live preview placeholder of the dragged element or snapping animations. Instead of a placeholder, it provides a basic drop indicator—a line showing where the item will land once dropped.&lt;/p&gt;

&lt;p&gt;For simpler use cases, this might not be a dealbreaker. But for highly interactive applications like page builders or design tools, where users expect to see real-time previews and fluid motion, this can feel limiting. If your project requires a more engaging and visually dynamic drag-and-drop experience, you may need to layer custom animations on top—or consider a different library altogether.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Lacks comprehensive documentation and community resources&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since pragmatic-drag-and-drop is still relatively new, its documentation and community support aren’t as mature as what you’ll find with more established libraries like dnd-kit or hello-pangea/dnd. While the core functionality is solid, there’s less guidance available—so if you hit an issue, you might find yourself digging through GitHub issues or reading through the source code to figure things out.&lt;/p&gt;

&lt;p&gt;If you’re comfortable troubleshooting on your own and don’t mind experimenting, this might not be a huge hurdle. But if you prefer a well-documented API with plenty of community discussions, tutorials, and Stack Overflow answers, you might find the lack of resources frustrating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Moderate learning curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Similar to dnd-kit, pragmatic-drag-and-drop is a headless and framework-agnostic library, which means there’s a bit more of a learning curve compared to plug-and-play solutions. The customization options are powerful, but you’ll need to spend some time getting familiar with its API and how to integrate it effectively into your project. &lt;/p&gt;

&lt;p&gt;Additionally, much of the official documentation and examples are built around Atlassian’s design system, so if you’re not using that, you might need to spend extra time adapting the examples to your own setup. Depending on your experience, this could be a minor inconvenience or a bigger hurdle when first getting started.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;License considerations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Apache 2.0 is a widely used and permissive license, it’s not as open-ended as the MIT license that some of the other libraries on this list use. If your project has specific licensing requirements, it's worth taking a moment to review the terms and ensure it aligns with your intended use.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Unconventional approach to open source&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike most open-source projects, Atlassian doesn’t fully embrace the traditional open-source model. Their development happens internally, with updates being synchronized to GitHub by a bot, meaning there’s no public roadmap or direct insight into upcoming changes. While the project is technically open-source, this lack of transparency could be a drawback if you’re looking for a library with active community involvement or want to contribute to its development.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;pragmatic-drag-and-drop shines in applications that require lightweight drag-and-drop functionality, especially in scenarios involving lists, trees, or container-based interactions. It’s perfect for use cases like task management boards or file organizers, where simple drag-and-drop is enough. However, the library’s limited visual feedback makes it less suitable for more complex use cases, particularly if they involve grid or flex based layouts. So, if your project relies heavily on complex multicolumn layouts or needs more interactive feedback (such as animating items as they are dragged), you might want to explore other options in this list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gridstack.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gridstackjs.com/" rel="noopener noreferrer"&gt;Gridstack.js&lt;/a&gt; is a drag-and-drop library built specifically for grid-based layouts. If you're building interactive dashboards or anything that requires draggable and resizable grid items, this library takes care of most of the heavy lifting right out of the box.&lt;/p&gt;

&lt;p&gt;This is also the oldest library on this list, having been around for nearly 11 years. It originally started as a jQuery-based project before migrating to vanilla JavaScript. That long history means it’s been used in a variety of real-world applications and is likely to stick around.&lt;/p&gt;

&lt;p&gt;That said, this also comes with some trade-offs. The transition from jQuery wasn’t entirely seamless, and some API design choices still carry legacy baggage. While the library is still actively maintained, parts of it might feel dated or tedious compared to more modern React-focused solutions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny8hfr9wirz94wwljihb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny8hfr9wirz94wwljihb.gif" alt="Gridstack.gif" width="899" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Gridstack’s strengths
&lt;/h3&gt;

&lt;p&gt;🔷  &lt;strong&gt;Complete set of out-of-the-box functionalities for grids&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Like hello-pangea/dnd, Gridstack.js is a higher-level abstraction, but instead of focusing on lists, it’s designed specifically for grid-based layouts. While tools like dnd-kit require additional work to support grid behavior, Gridstack handles all that for you, so you can focus on configuration rather than implementation.&lt;/p&gt;

&lt;p&gt;Right out of the box, it provides grid drop zones with configurable rows and columns, drag-and-drop positioning within a grid, and resizable grid items that snap to the layout structure. So, if your app depends on draggable grids and you’d rather not build that functionality from scratch, Gridstack is a solid choice that can save you a lot of development time.&lt;/p&gt;

&lt;p&gt;🔷 &lt;strong&gt;Mature library with long-term support&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gridstack.js has been around for over a decade, making it one of the most battle-tested libraries in this list. Its longevity means it's been used in a wide range of real-world applications, from internal tools to production dashboards, and it continues to receive updates and improvements. Unlike some libraries that pop up and disappear after a few years, Gridstack has shown long-term stability, making it a safer bet if you’re looking for a drag-and-drop solution that won’t suddenly be abandoned.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔷 Framework agnostic&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just like pragmatic-drag-and-drop, Gridstack.js is designed to work with any JavaScript framework—or even without one. This gives you flexibility if you're working in a mixed environment or need a solution that isn’t tightly coupled to React. It has official wrappers for Angular, Ember, Aurelia, and Rails but at its core, it's a vanilla JavaScript library. This means you can integrate it into different projects without being locked into a specific ecosystem. &lt;/p&gt;

&lt;p&gt;If you need a stable grid drag-and-drop experience, and you want a long-term solution that can be adapted across different frontend stacks, you should seriously consider using Gridstack.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Gridstack’s limitations
&lt;/h3&gt;

&lt;p&gt;🔻 &lt;strong&gt;Steep learning curve for React integration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While Gridstack.js is a powerful tool for grid-based drag-and-drop interactions, integrating it with React can feel unintuitive. The library was originally built with jQuery in mind, and while it has since transitioned to vanilla JavaScript, many of its API design choices still reflect those roots. Instead of working with standard React patterns like props and state, you have to inject the grid manually by manipulating the DOM directly using HTML IDs and selectors; and you’ll also have to use objects and classes to handle the grid state and its configurations.&lt;/p&gt;

&lt;p&gt;For React developers, this can feel out of place and lead to more complex, less idiomatic code. The team has discussed releasing an official React wrapper to improve the experience, but there has been community feedback suggesting that the current setup can be frustrating. If you're comfortable working closer to the DOM or have experience with non-React libraries, this might not be a dealbreaker. But if you’re looking for a more seamless React-first solution, Gridstack might require some extra work to integrate smoothly.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Sparse documentation and small community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gridstack.js has been around for a long time, but its documentation is relatively minimal. While there are some examples available, they don’t always cover more complex use cases, leaving you to figure things out through trial and error. This wouldn’t be as much of an issue if there were an active community to turn to, but Gridstack’s user base is fairly niche. That means fewer online discussions, tutorials, or Stack Overflow threads to help troubleshoot issues. If you're used to working with well-documented libraries with strong community support, integrating Gridstack into your project might feel like more of a solo effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Limited to grid-based layouts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gridstack.js is built specifically for grid-based drag-and-drop interactions, which means it doesn’t support other layout types like flexbox out of the box. If your project requires a mix of grid and flex layouts or more fluid positioning, you’ll need to work around these limitations or consider a different library. While this isn’t necessarily a dealbreaker if you’re working on a dashboard-style UI, it does mean you’re locked into a specific way of handling layouts. If you need more flexibility, other libraries might be a better fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;Gridstack.js is a solid choice if your project is heavily focused on grid-based layouts and you need out-of-the-box item resizing. It’s particularly useful for applications like dashboards, widget-based UIs, or any interface where users need to rearrange elements within a strict grid structure.&lt;/p&gt;

&lt;p&gt;However, if you're working in a React environment, integration can be a bit clunky due to its JavaScript-first design and reliance on selectors, classes, and objects. If you need a more seamless React experience with drag-and-drop inside grids, dnd-kit or pragmatic-drag-and-drop might be a better fit depending on your flexibility and performance needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  formkit/drag-and-drop
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://drag-and-drop.formkit.com/" rel="noopener noreferrer"&gt;formkit/drag-and-drop&lt;/a&gt; is a relatively new, lightweight library developed by the team behind &lt;a href="https://formkit.com/" rel="noopener noreferrer"&gt;FormKit&lt;/a&gt;, a form framework for Vue. Unlike traditional drag-and-drop libraries that manipulate the DOM directly, this one takes a different approach: instead of physically moving elements around, it updates a underlying reactive data model that you provide. This means that rather than worrying about manually reordering elements, you let the library handle updates to your data structure, making it feel more like a natural extension of reactive frameworks.&lt;/p&gt;

&lt;p&gt;It’s framework-agnostic but comes with thin wrappers for both React and Vue, making it easier to integrate. While it’s still in early stages and hasn’t hit a 1.0 release, it’s gaining traction for its simple, intuitive approach to drag-and-drop. If you’re looking for a minimal, easy-to-use solution that plays well with React state, this library might be worth keeping an eye on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb1j77grfmgrtcr8a3iyh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb1j77grfmgrtcr8a3iyh.gif" alt="formkit.gif" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ formkit/drag-and-drop’s strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 Lightweight&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest advantages of formkit/drag-and-drop is its size—it clocks in at just ~5KB gzipped. Compared to some of the heavier libraries on this list, this makes it one of the smallest options available. If bundle size is a priority for your project, this is a strong contender. Alongside pragmatic-drag-and-drop, it’s one of the most minimalistic options, helping to keep your app lean without unnecessary overhead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Plugin Support&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Despite being a minimal library, formkit/drag-and-drop includes a plugin system that lets you extend its functionality without adding unnecessary bloat. This means you can keep the core lightweight while selectively introducing features as needed.&lt;/p&gt;

&lt;p&gt;Out of the box, it provides plugins for customizing item previews, modifying drop selection behavior, and adding animations. These built-in extensions make it easier to tweak the drag-and-drop experience without writing custom logic from scratch. While the plugin ecosystem is still relatively small, it offers a solid foundation for modular extensions—helping bridge the gap between fully headless solutions and more opinionated, feature-rich libraries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Gentle learning curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Among all the libraries in this list, formkit/drag-and-drop has the simplest setup for a React application. Instead of manually handling drag-and-drop logic, you just use a hook that connects directly to your data model. The library then provides a state-driven way to render your items, along with a ref for the parent drop container.&lt;/p&gt;

&lt;p&gt;This approach keeps everything declarative and React-friendly, making it easy to integrate without a lot of boilerplate. If you just need a straightforward drag-and-drop experience, it works right out of the box. And if you need more complexity later on, you can introduce additional functionality gradually without refactoring your entire setup.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ formkit/drag-and-drop’s limitations
&lt;/h3&gt;

&lt;p&gt;🔻 &lt;strong&gt;Limited docs and unclear API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since formkit/drag-and-drop is still relatively new, its documentation is pretty minimal. While the basics are covered, there aren’t a lot of clear examples for handling more complex use cases. This means that certain features that are standard in other libraries, may require some extra trial and error to implement in this one.&lt;/p&gt;

&lt;p&gt;If you’re building something straightforward, this isn’t a huge issue. But if you need deeper customization, expect to spend some time digging through the source code or experimenting with different configurations. The library is evolving, so this might improve in the future, but for now, it’s something to keep in mind.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Small and niche community&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As of now, formkit/drag-and-drop is still in its early stages, currently at version 0.3. This means the community around it is relatively small, and finding discussions, third-party tutorials, or Stack Overflow answers can be a challenge.&lt;/p&gt;

&lt;p&gt;While FormKit itself has a solid reputation in the Vue ecosystem, this particular library is still growing. If you run into issues or need advanced support, you may have to rely on the official docs (which are limited) or experiment on your own. It’s not a dealbreaker, but it’s something to consider if you prefer libraries with a more established user base.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Limited accessibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While formkit/drag-and-drop does include some basic screen reader support, it lacks proper keyboard navigation and has some inconsistencies on mobile. For accessibility-focused projects, this could be a dealbreaker, especially if you need users to interact with drag-and-drop elements without a mouse.&lt;/p&gt;

&lt;p&gt;Since accessibility can be tricky to implement from scratch, you might find yourself needing to build custom workarounds to fill in the gaps. This is an area where more mature libraries like dnd-kit or hello-pangea/dnd have the upper hand, as they provide better and more stablished cross-device support.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;As of now, formkit/drag-and-drop is a solid choice if you need basic drag-and-drop functionality without a lot of setup. It works well for simple use cases like moving items between containers or reordering lists, especially if you’re looking for something lightweight and easy to integrate. However, for more complex interactions, like those required in design tools or highly customized layouts, it may still feel limited. If you need fine-grained control over DnD behaviors or a library with long-term stability, something like dnd-kit would be a better option.&lt;/p&gt;

&lt;p&gt;Also, since it’s still an early-stage library, the API is subject to change, and its promises around performance and bundle size could shift over time. If you need a stable solution with an established track record, this might not be the best choice—at least for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔚 Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Choosing the right drag-and-drop library isn’t as straightforward as it seems. As we saw in this list, each option comes with its own strengths, trade-offs, and best use cases, and like everything in engineering, the “best” choice ultimately depends on your specific needs.&lt;/p&gt;

&lt;p&gt;If you're exploring drag-and-drop solutions because you’re working on a page builder, a dashboard, or any app that needs a powerful visual editor, you might want to check out &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_dnd_tools" rel="noopener noreferrer"&gt;Puck&lt;/a&gt;—we built it to make embedding a flexible visual editor into your app as seamless as possible.&lt;/p&gt;

&lt;p&gt;🚀 We’d love to hear your thoughts! If you have feedback on this breakdown, know of other libraries we should check out, or just want to chat about drag-and-drop and visual editing, come say hi:&lt;/p&gt;

&lt;p&gt;⭐ Support us on GitHub by dropping a &lt;a href="https://github.com/puckeditor/puck" rel="noopener noreferrer"&gt;star&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💬 Join the conversation on &lt;a href="https://discord.gg/V9mDAhuxyZ" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🐦 Follow us for updates on &lt;a href="https://x.com/puckeditor" rel="noopener noreferrer"&gt;X&lt;/a&gt; &amp;amp; &lt;a href="https://bsky.app/profile/puckeditor.com" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And of course, if you have any questions or comments, drop them below—always happy to keep the conversation going!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>frontend</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Advanced Layouts with Puck 0.18: Harnessing Grid and Flex Containers</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Fri, 14 Feb 2025 14:35:12 +0000</pubDate>
      <link>https://dev.to/puckeditor/advanced-layouts-with-puck-018-harnessing-grid-and-flex-containers-o42</link>
      <guid>https://dev.to/puckeditor/advanced-layouts-with-puck-018-harnessing-grid-and-flex-containers-o42</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is the open-source visual editor for React, that you can embed in any application to create the next generation of page builders and no-code products. Give us a star on &lt;a href="https://github.com/puckeditor/puck" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;! ⭐️&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Drag-and-drop page-builders are everywhere, but getting them to work seamlessly across different layouts—especially complex &lt;strong&gt;CSS Grid&lt;/strong&gt; and &lt;strong&gt;Flexbox&lt;/strong&gt; structures—has always been a challenge. With &lt;a href="https://github.com/puckeditor/puck/releases/tag/v0.18.0" rel="noopener noreferrer"&gt;&lt;strong&gt;Puck 0.18&lt;/strong&gt;&lt;/a&gt;, that just got a lot easier.&lt;/p&gt;

&lt;p&gt;This update introduces a revamped &lt;strong&gt;drag-and-drop engine&lt;/strong&gt; designed to work naturally across any CSS layout. Instead of forcing elements into rigid structures, you now have &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;full control over the styles of DropZones and their children&lt;/a&gt; to fit your design needs, and let you do things like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv24akr76bho4dh4z9ujf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv24akr76bho4dh4z9ujf.gif" alt="1intro.gif" width="720" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the new drag-and-drop engine, you can add any CSS layout however you want to your editor—but some patterns can make the process much smoother. That’s why in this post, we’ll go over some common patterns I’ve found useful for adding CSS Grid and Flex support to your editors, along with best practices to help you get the most out of Puck’s new engine.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before we get started, I’ll assume you have a basic understanding of Puck and how it works. If you’re new here, no worries—you’re welcome to follow along! However, I’d recommend checking out the &lt;a href="https://puckeditor.com/docs/getting-started?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Getting Started guide&lt;/a&gt; first to familiarize yourself with the basics, as well as the &lt;a href="https://dev.to/puckeditor/revolutionizing-drag-and-drop-in-react-introducing-puck-018-1a68?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18"&gt;0.18 blog post&lt;/a&gt; to learn about the new features in this version.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Grid patterns

&lt;ol&gt;
&lt;li&gt;Grid container pattern&lt;/li&gt;
&lt;li&gt;Grid container - Grid Item pattern&lt;/li&gt;
&lt;li&gt;Grid container - Any item pattern&lt;/li&gt;
&lt;li&gt;Grid layout pattern&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Flex patterns

&lt;ol&gt;
&lt;li&gt;Flex container pattern&lt;/li&gt;
&lt;li&gt;Flex container - Flex item pattern&lt;/li&gt;
&lt;li&gt;Flex container - Any item pattern&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Best practices

&lt;ol&gt;
&lt;li&gt;Add dynamic padding for editing&lt;/li&gt;
&lt;li&gt;Prevent unwanted layout shifts&lt;/li&gt;
&lt;li&gt;Give hints with collisionAxis&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h2&gt;
  
  
  🍱 Grid patterns
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout" rel="noopener noreferrer"&gt;CSS Grids&lt;/a&gt; are built for structured, multi-column layouts with precise control over item placement. Unlike Flexbox, which distributes items dynamically based on content size, Grids let you define fixed or flexible tracks, making them a great fit for dashboards, forms, bento layouts, and any design where the number of rows and columns matters.&lt;/p&gt;

&lt;p&gt;With Puck’s drag-and-drop engine, you can give users full control over CSS rules so they can build grids however they like. But in most cases, you'll want to guide them through the process to keep things intuitive for all levels of experience. Sometimes that means letting them define the number of columns and rows in a grid freely; other times, it makes more sense to provide a set of predefined grid designs from which to choose from.&lt;/p&gt;

&lt;p&gt;With this in mind, in this section I’ve gathered four patterns I’ve found especially useful for adding grids to build intuitive and flexible page-building experiences across a variety of use cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grid container pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Grid container&lt;/em&gt; pattern is the foundation of all grid-based layouts in Puck. With this approach, the grid &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt; itself defines the grid layout, so that all elements inside follow a predefined number of rows or columns. This is useful when you want to give control over the layout container and need a regular arrangement of elements in columns and rows, so it’s ideal for things like lists and content grids.&lt;/p&gt;

&lt;p&gt;To set it up you need to follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define a new component in your &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck config&lt;/a&gt; for the grid&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   
    &lt;span class="c1"&gt;// Define your Grid component for grid layouts&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... next steps here&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. &lt;em&gt;Optionally&lt;/em&gt;, add fields to let users configure the grid—for example, you could add fields to set rules like the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-columns" rel="noopener noreferrer"&gt;number of columns&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-template-rows" rel="noopener noreferrer"&gt;rows&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Add fields to configure the numbers of rows/columns in the grid&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Finally, render a &lt;code&gt;DropZone&lt;/code&gt; inside the &lt;code&gt;Grid&lt;/code&gt; component, applying a &lt;code&gt;display: grid&lt;/code&gt; CSS rule and any other desired layout rules or user-defined settings&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

      &lt;span class="c1"&gt;// Render the DropZone as a CSS grid&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repeat(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, 1fr)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gridTemplateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repeat(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, auto)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you followed these steps, you’ll now be able to navigate to the editor in the browser, drag and drop a &lt;code&gt;Grid&lt;/code&gt;, set the number of columns and rows for it, and drop items inside to automatically snap them to the grid structure:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrgvdaydae8v9e8ec5zv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frrgvdaydae8v9e8ec5zv.gif" alt="grid-container.gif" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, this pattern works great for structured layouts, but if you need more flexibility—like controlling how individual items are placed within the grid, or allow the user to define custom multi-column layouts—you should check out the &lt;em&gt;Grid container - Grid item&lt;/em&gt; pattern, which we’ll cover next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grid container - Grid item pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Grid container - Grid item&lt;/em&gt; pattern builds on the &lt;em&gt;Grid container&lt;/em&gt; pattern by giving you more flexibility in how items are arranged. Instead of enforcing a uniform layout where every item spans the same number of rows and columns, this approach lets users break grids into distinct sections with their own configurable drop zones.&lt;/p&gt;

&lt;p&gt;The key to achieve this is adding a &lt;code&gt;GridItem&lt;/code&gt; component to your config. This component should render a &lt;code&gt;DropZone&lt;/code&gt;, provide fields to set how many rows and columns that &lt;code&gt;DropZone&lt;/code&gt; spans in the grid, and be restricted to use only inside &lt;code&gt;Grid&lt;/code&gt; components. With that in place you would allow users to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop Grid Item zones inside a Grid&lt;/li&gt;
&lt;li&gt;Set how many columns and rows each zone should occupy&lt;/li&gt;
&lt;li&gt;Organize the layout visually by dragging and dropping zones around&lt;/li&gt;
&lt;li&gt;Drag and drop content into each zone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why, with this pattern, you can define custom layouts directly in the editor. For example, you could create a layout with a navbar zone that spans all columns at the top, a table of contents that spans all rows on the left, and a main content section on the right.&lt;/p&gt;

&lt;p&gt;To implement this pattern, follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add a &lt;code&gt;Grid&lt;/code&gt; component to your &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck config&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

    &lt;span class="c1"&gt;// Define a Grid component&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repeat(3, 1fr)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Assign the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#allow?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;allow&lt;/code&gt;&lt;/a&gt; prop on the Grid &lt;code&gt;DropZone&lt;/code&gt; to an array containing only the name of your &lt;code&gt;GridItem&lt;/code&gt; component. This will only allow &lt;code&gt;GridItems&lt;/code&gt; inside the grid&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

            &lt;span class="c1"&gt;// Only allow grid items inside the grid&lt;/span&gt;
            &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GridItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Add the &lt;code&gt;GridItem&lt;/code&gt; component to your &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck config&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

    &lt;span class="c1"&gt;// Define your Grid Item component&lt;/span&gt;
    &lt;span class="na"&gt;GridItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... next steps here&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Remove the default wrapping element Puck adds around draggable components for the &lt;code&gt;GridItem&lt;/code&gt; by enabling the &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#inline?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;inline&lt;/code&gt;&lt;/a&gt; parameter. This will remove the Puck wrapper and allow you to style the grid items as direct children of the grid they are dropped in&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

    &lt;span class="na"&gt;GridItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Remove the default element wrapper&lt;/span&gt;
      &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Add fields to the &lt;code&gt;GridItem&lt;/code&gt; component to define the number of columns and rows each item should span&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;GridItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

      &lt;span class="c1"&gt;// Add fields for the number of columns/rows the item should span&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Render the &lt;code&gt;DropZone&lt;/code&gt; inside the &lt;code&gt;GridItem&lt;/code&gt; with the number of columns and rows it should span. Also, since you previously enabled the &lt;code&gt;inline&lt;/code&gt; parameter, you must pass the &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#puckdragref?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;puck.dragRef&lt;/code&gt;&lt;/a&gt; to the &lt;code&gt;DropZone&lt;/code&gt; to let Puck know it should be treated as a draggable element&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;GridItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

      &lt;span class="c1"&gt;// Render the DropZone with the number of columns/rows it should span&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dragRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid-item-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;gridColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gridRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;7. Finally, disallow &lt;code&gt;GridItems&lt;/code&gt; to be nested inside other &lt;code&gt;GridItems&lt;/code&gt; to provide a better experience while dragging elements around grids&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;GridItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

            &lt;span class="c1"&gt;// Disallow GridItems inside&lt;/span&gt;
            &lt;span class="na"&gt;disallow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GridItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After following these steps, you should now be able to navigate to the browser, drag and drop a &lt;code&gt;Grid&lt;/code&gt;, add &lt;code&gt;GridItems&lt;/code&gt; inside, set the number of columns and rows each item should span, and drag any component inside these grid zones to automatically adjust it to fit the layout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xjyl6uct0rr5xliadvk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3xjyl6uct0rr5xliadvk.gif" alt="grid-container-grid-item.gif" width="760" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This pattern is great when you want to provide full control over layout structure, but if you just need a way for users to drop any component into a grid and still define how much space it should take up inside, the &lt;em&gt;Grid container - Any item&lt;/em&gt; pattern is a better fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grid container - Any item pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Grid container - Any item&lt;/em&gt; pattern is a more flexible approach to working with grids, allowing any component to be placed inside while still letting users control how much space each item takes up. Instead of limiting grid items to a specific type, this pattern enables any component to define their own grid placement dynamically when dropped inside of a grid.&lt;/p&gt;

&lt;p&gt;With this pattern, users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drop any component inside a grid&lt;/li&gt;
&lt;li&gt;Define how many rows and columns each item should span&lt;/li&gt;
&lt;li&gt;Keep components fully functional outside of grids by hiding the rows and columns fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key here is using &lt;a href="https://puckeditor.com/docs/integrating-puck/dynamic-fields?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;dynamic fields&lt;/a&gt; to adjust component settings based on their parent. If a component is inside a Grid, it should show &lt;code&gt;columns&lt;/code&gt; and &lt;code&gt;rows&lt;/code&gt; fields to let users define its span. If it’s outside, those fields should stay hidden.&lt;/p&gt;

&lt;p&gt;To implement this pattern, follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Add a &lt;code&gt;Grid&lt;/code&gt; component to your &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck config&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

    &lt;span class="c1"&gt;// Define a Grid component&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repeat(3, 1fr)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Use the &lt;a href="https://puckeditor.com/docs/integrating-puck/dynamic-fields?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;resolveFields&lt;/code&gt;&lt;/a&gt; API on any component to dynamically show the &lt;code&gt;columns&lt;/code&gt; and &lt;code&gt;rows&lt;/code&gt; fields when placed inside a Grid parent, hiding them when the component is used outside a grid&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

    &lt;span class="na"&gt;AnyComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Remove the default element wrapper&lt;/span&gt;
      &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="c1"&gt;// Use dynamic fields to show "Columns" and "Rows" only when inside a Grid&lt;/span&gt;
      &lt;span class="na"&gt;resolveFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;//... existing fields&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="c1"&gt;// Render the component with the correct grid placement&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
            &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dragRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;gridColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;gridRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Any Component
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;PRO TIP:&lt;/strong&gt; If you find yourself repeating this setup multiple times for any components you wish to drop inside a grid, consider creating a &lt;code&gt;withGrid(componentConfig)&lt;/code&gt; function that adds the &lt;code&gt;resolveFields&lt;/code&gt; setup above to avoid code repetition&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After setting this up, you can navigate to the editor, drop any component into any grid, adjust its column and row span, and see the layout update in real time. If the component is moved outside of the grid, those fields will disappear, keeping it adaptable to different layouts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpy2sbyp35dms9rkn4k0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjpy2sbyp35dms9rkn4k0.gif" alt="grid-container-any-item.gif" width="800" height="374"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This pattern works great if you want to allow any item inside a grid while still controlling how each item is positioned. However, there are times when you might want to restrict users to a specific set of predefined grid layouts so they don’t have to worry about design details. For those cases, the &lt;em&gt;Grid layout&lt;/em&gt; pattern is the best option.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grid layout pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Grid layout&lt;/em&gt; pattern allows users to choose from predefined grid layouts instead of manually adjusting each item's placement or setting up the layout themselves. This is useful when you want to streamline the page-building process and ensure design consistency while still offering flexibility.&lt;/p&gt;

&lt;p&gt;The key difference from the &lt;em&gt;Grid container - Any item&lt;/em&gt; and &lt;em&gt;Grid container - Grid item&lt;/em&gt; patterns is that here, the grid itself determines how components are arranged based on multiple pre-implemented designs, rather than the nested components defining their own placement based on user input.&lt;/p&gt;

&lt;p&gt;With this pattern, users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Select a grid design from a predefined set of grid layouts&lt;/li&gt;
&lt;li&gt;Drop any components into the grids&lt;/li&gt;
&lt;li&gt;Maintain a clean and predictable design without manual configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To implement this pattern, follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define a Grid component with a &lt;code&gt;layout&lt;/code&gt; select field to allow users to choose a predefined grid design. Each option’s value should be the CSS class that implements that specific design&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Import designs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Editor.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Add a layout select input to choose a certain grid design&lt;/span&gt;
        &lt;span class="na"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hero&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hero-layout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content List&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-list-layout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// Read the layout design to style the dropzone based on it&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;layout&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt; &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"grid-zone"&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`grid &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;layout&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Define the CSS classes for your grid layouts so that each option applies a different design to the grid&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* ./Editor.css */&lt;/span&gt;

&lt;span class="c"&gt;/* Base grid styles */&lt;/span&gt;
&lt;span class="nc"&gt;.grid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Hero section layout */&lt;/span&gt;
&lt;span class="nc"&gt;.hero-layout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Layout for a list of content */&lt;/span&gt;
&lt;span class="nc"&gt;.content-list-layout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;grid-template-columns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="n"&gt;fr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once set up, users can drop components into a grid without worrying about manual positioning. They’ll be able to select a layout design from the list of options you provide and the grid items will automatically adjust to match the chosen design. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngqytcm6j05pq1q87ty4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fngqytcm6j05pq1q87ty4.gif" alt="grid-layout.gif" width="901" height="304"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is ideal for use cases where structure and consistency are important, like root-level page layouts, forms, or articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  ↩️ Flex patterns
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_flexible_box_layout/Basic_concepts_of_flexbox" rel="noopener noreferrer"&gt;Flexbox&lt;/a&gt; is all about dynamic, content-aware layouts. Unlike CSS Grids, which are great for structured designs with fixed tracks, Flexbox shines when you need elements to adjust based on their content and available space. It’s perfect for things like responsive lists and card layouts where items should naturally &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow" rel="noopener noreferrer"&gt;grow&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-shrink" rel="noopener noreferrer"&gt;shrink&lt;/a&gt;, or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-wrap" rel="noopener noreferrer"&gt;wrap&lt;/a&gt; as needed.&lt;/p&gt;

&lt;p&gt;Once again, with Puck, you could give users full control over Flexbox CSS rules so that they can fine-tune every detail on their pages. But most of the time, when creating a page builder, you’ll want to abstract away from those complexities so that anyone can create pages without worrying about layout intricacies. After all, your user won’t always be a developer, and Flexboxes are famous for tripping over even the most seasoned front-end engineer from time to time.&lt;/p&gt;

&lt;p&gt;That’s why, in this section, I’m sharing three patterns you can use to build intuitive, flexible page-building experiences with Flexbox in Puck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flex container pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Flex container&lt;/em&gt; pattern is the simplest way to structure a layout using Flexbox in Puck, and it’s the basis of all other flex patterns. In this approach, the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt; itself defines the flex behavior, so all elements inside follow the container’s layout rules. This works well when you want a flexible, row-based or column-based structure where items automatically adjust based on their content and available space. It’s especially useful for things like horizontal navigation bars, stacked sections, or dynamic lists.&lt;/p&gt;

&lt;p&gt;To set it up, follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define a new component in your &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck config&lt;/a&gt; for the Flex container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define your flex container&lt;/span&gt;
    &lt;span class="na"&gt;FlexContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... next steps here&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. &lt;em&gt;Optionally&lt;/em&gt;, add fields to let users configure the flex behavior—for example, you might allow them to control &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-direction" rel="noopener noreferrer"&gt;&lt;code&gt;flex-direction&lt;/code&gt;&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FlexContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Add fields to configure flex behavior&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Column&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;column&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Render a &lt;code&gt;DropZone&lt;/code&gt; inside the Flex container, applying &lt;code&gt;display: flex&lt;/code&gt; and any user-defined settings&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FlexContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;//... existing config&lt;/span&gt;

      &lt;span class="c1"&gt;// Render the DropZone as a flex container with user-defined settings&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexWrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexDirection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;direction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you followed these steps, you should now be able to navigate to the editor, drop a Flexbox component, add some items inside, modify the direction of the container, and see the elements automatically arrange according to the user’s settings:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fde7vi1j1840sem1kasjb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fde7vi1j1840sem1kasjb.gif" alt="flex-container.gif" width="760" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This pattern is great for simple, responsive layouts where all items flow naturally within a flexible container. If you need more control over individual item behavior—such as setting different flex-grow or alignment rules per item—you’ll want to explore the &lt;em&gt;Flex container - Flex item&lt;/em&gt; pattern next.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flex container - Flex item pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Flex container - Flex item&lt;/em&gt; pattern is a simple yet powerful way to structure layouts using Flexbox. With it, you define a &lt;em&gt;Flex container&lt;/em&gt; that holds &lt;em&gt;Flex items&lt;/em&gt; where each item acts as a section where users can drop other components. This makes it useful for setting up flexible page structures—like a sidebar and main content area—or components that need to adapt to different screen sizes, such as a card with three horizontal sections that wrap when space is limited.&lt;/p&gt;

&lt;p&gt;The key to this pattern is introducing a &lt;em&gt;Flex Item&lt;/em&gt; component that wraps each child inside the Flex container. This component, then, provides controls for &lt;code&gt;flex-grow&lt;/code&gt;, &lt;code&gt;flex-shrink&lt;/code&gt;, and &lt;code&gt;flex-basis&lt;/code&gt;, allowing users to tweak layout behavior visually inside the editor. With that in place you would allow users to: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drag and drop Flex Item zones inside a Flex container&lt;/li&gt;
&lt;li&gt;Control how each zone should grow, shrink, and take up space&lt;/li&gt;
&lt;li&gt;Organize the layout visually by dragging and dropping zones around&lt;/li&gt;
&lt;li&gt;Drag and drop content into each zone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To implement this pattern, follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define a new component in your &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck config&lt;/a&gt; for the Flex container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define a Flex container component&lt;/span&gt;
    &lt;span class="na"&gt;FlexContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexWrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Restrict the container to only allow &lt;code&gt;FlexItems&lt;/code&gt; inside&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FlexContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-zone"&lt;/span&gt;
            &lt;span class="c1"&gt;// Only allow FlexItem components inside&lt;/span&gt;
            &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FlexItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexWrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Define the &lt;code&gt;FlexItem&lt;/code&gt; component&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FlexItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Remove the default wrapper around the component to style it as a child&lt;/span&gt;
      &lt;span class="c1"&gt;// of a flexbox&lt;/span&gt;
      &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Add fields for controlling the flex properties of the &lt;code&gt;FlexItems&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FlexItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;grow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;shrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;basis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;5. Render the &lt;code&gt;DropZone&lt;/code&gt; inside the &lt;code&gt;FlexItem&lt;/code&gt;, applying any required flex properties&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FlexItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;grow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shrink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;basis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dragRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-item-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;flexGrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;grow&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexShrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shrink&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexBasis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;basis&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;6. Finally, prevent nesting Flex Items inside each other&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;FlexItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;grow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shrink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;basis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;disallow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FlexItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Prevents nesting&lt;/span&gt;
            &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dragRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-item-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;flexGrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;grow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexShrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shrink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexBasis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;basis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once set up, you’ll be able to drop a &lt;code&gt;FlexContainer&lt;/code&gt; onto the page, add &lt;code&gt;FlexItems&lt;/code&gt; inside, and adjust their behavior using the flex properties. You can then place any other components inside these sections, letting them adapt naturally within the layout.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kxmnp11kgtm1b16bex6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kxmnp11kgtm1b16bex6.gif" alt="flex-container-flex-item.gif" width="800" height="324"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This pattern is ideal when you need a flexible page layout, but if you’re looking for a way to add any component inside a flex container and still be able control it’s flex properties dynamically, the &lt;em&gt;Flex container - Any item&lt;/em&gt; pattern might be a better fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flex container - Any item pattern
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;Flex container - Any item&lt;/em&gt; pattern is a more adaptable approach to using flex layouts, allowing any component to be placed inside while giving users control over how each item behaves within the flex container.&lt;/p&gt;

&lt;p&gt;With this pattern, users can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Drop any component inside a flex container&lt;/li&gt;
&lt;li&gt;Control how items grow, shrink, and take up space&lt;/li&gt;
&lt;li&gt;Keep components fully functional outside of flex containers by hiding flex-specific controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key here is using dynamic fields to expose flex properties like &lt;code&gt;flex-grow&lt;/code&gt;, &lt;code&gt;flex-shrink&lt;/code&gt;, and &lt;code&gt;flex-basis&lt;/code&gt; only when a component is inside a Flex container. Outside of it, these fields should remain hidden to avoid unnecessary complexity.&lt;/p&gt;

&lt;p&gt;To implement this pattern, follow these steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define a new component in your &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck config&lt;/a&gt; for the Flex container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Define a Flex container component&lt;/span&gt;
    &lt;span class="na"&gt;FlexContainer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"flex-zone"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexWrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Define any component that can be placed inside a flex container, &lt;a href="https://puckeditor.com/docs/integrating-puck/dynamic-fields?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;dynamically adjusting its flex related fields&lt;/a&gt; based on the parent container&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

    &lt;span class="na"&gt;AnyComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Remove the default element wrapper&lt;/span&gt;
      &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

      &lt;span class="c1"&gt;// Use dynamic fields to show "Flex Grow," "Flex Shrink," and "Flex Basis" &lt;/span&gt;
      &lt;span class="c1"&gt;// only when inside a FlexContainer&lt;/span&gt;
      &lt;span class="na"&gt;resolveFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="c1"&gt;//... existing fields&lt;/span&gt;
        &lt;span class="p"&gt;};&lt;/span&gt;

        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;FlexContainer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;flexGrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;flexShrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;flexBasis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
          &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;

      &lt;span class="c1"&gt;// Render the component with the configured flex behavior&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;flexGrow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flexShrink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;flexBasis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
            &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dragRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="na"&gt;flexGrow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flexGrow&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexShrink&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flexShrink&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
              &lt;span class="na"&gt;flexBasis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;flexBasis&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            Any Component
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;&lt;strong&gt;PRO TIP:&lt;/strong&gt; If you plan to use this setup for multiple components, consider creating a &lt;code&gt;withFlex(componentConfig)&lt;/code&gt; function that adds the &lt;code&gt;resolveFields&lt;/code&gt; setup above to avoid code repetition&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once set up, users can drop any component into a flex container, tweak its growth and shrink behavior, and see changes in real-time. If the component is moved outside of the flex container, those controls will disappear, keeping it adaptable for different layouts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesku1urbcqyxcxesac7u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fesku1urbcqyxcxesac7u.gif" alt="flex-container-any-item.gif" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This pattern is great for flexible, dynamic layouts where elements need to adjust their sizes based on available space.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔷 Best practices
&lt;/h2&gt;

&lt;p&gt;Beyond the patterns we’ve covered for integrating CSS Grid and Flexbox, there are a few best practices or tips that can help improve the overall drag-and-drop experience and make your editor feel smoother and more intuitive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add dynamic padding for editing
&lt;/h3&gt;

&lt;p&gt;When working with nested layouts in the editor, selecting the right component with your cursor can get tricky—especially if DropZones are tightly packed. A simple way to improve usability is by adding space around DropZones only when the page is being edited. To do this, you can use the &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#puckisediting?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;puck.isEditing&lt;/code&gt;&lt;/a&gt; render prop, to add temporarily padding around DropZones, making it easier to grab and move entire groups of nested components without accidentally selecting inner ones:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;NestedComponent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dropzone-with-padding"&lt;/span&gt;
            &lt;span class="c1"&gt;// If rendering in the editor, add padding, otherwise don't add any&lt;/span&gt;
            &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEditing&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;16px&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prevent unwanted layout shifts
&lt;/h3&gt;

&lt;p&gt;Nothing disrupts a drag-and-drop experience more than unexpected layout shifts while dragging elements around. To keep things predictable, you can use the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#allow?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;allow&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#disallow?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;disallow&lt;/code&gt;&lt;/a&gt; props on your DropZones to control where components can be placed, that way, you can prevent seeing whole lists or containers move elements around when you just wanted to move a page header to the top of your canvas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;My header&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;ProductsList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"product-zone"&lt;/span&gt;
            &lt;span class="c1"&gt;// Only allow product items in this zone&lt;/span&gt;
            &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ProductItem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;ProductItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Product&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We used this in the &lt;em&gt;Grid container - Grid item&lt;/em&gt; and &lt;em&gt;Flex container - Flex item&lt;/em&gt; patterns to avoid nesting of sections and provide a better experience when moving them around the container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Give hints with &lt;code&gt;collisionAxis&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Use the DropZone &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#collisionaxis?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;&lt;code&gt;collisionAxis&lt;/code&gt;&lt;/a&gt; prop to give Puck a hint about which direction your components flow. &lt;/p&gt;

&lt;p&gt;By default, Puck will automatically identify the most likely drag direction based on your DropZone’s CSS properties, but sometimes you might have particular behavior that Puck can’t interpret. For example, if you have a flex container that doesn’t wrap, setting an &lt;code&gt;x&lt;/code&gt; (horizontal) collision axis will help it to know where it should try to add new elements:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
            &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-content"&lt;/span&gt;
            &lt;span class="na"&gt;collisionAxis&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"x"&lt;/span&gt;
            &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;flexWrap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noWrap&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Ready to start building with Puck?
&lt;/h2&gt;

&lt;p&gt;In this article, we covered some key patterns and best practices to help you build intuitive, flexible visual editors with &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; and take advantage of all the new features &lt;a href="https://github.com/puckeditor/puck/releases/tag/v0.18.0?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;v0.18&lt;/a&gt; introduces. Whether you're working with grids or flex layouts, these techniques should give you a solid foundation to create great editing experiences.&lt;/p&gt;

&lt;p&gt;We’d love to hear what you're building! If you have questions, want to suggest improvements, contribute, or just bounce around ideas, here’s how you can get involved:&lt;/p&gt;

&lt;p&gt;🌟 &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Star Puck on GitHub&lt;/a&gt; to show support and help others discover it.&lt;/p&gt;

&lt;p&gt;🤝 &lt;a href="https://discord.gg/V9mDAhuxyZ?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Join our Discord&lt;/a&gt; to chat with the community and share your work.&lt;/p&gt;

&lt;p&gt;📢 Follow Puck on &lt;a href="https://x.com/puckeditor?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;X&lt;/a&gt; and &lt;a href="https://bsky.app/profile/puckeditor.com?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=advanced_layouts_18" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt; for updates, sneak peeks, and tips.&lt;/p&gt;

&lt;p&gt;Have thoughts or feedback? Drop a comment—we’re always up for a good discussion. &lt;/p&gt;

&lt;p&gt;Happy building! 🚀&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Revolutionizing Drag-and-Drop in React: Introducing Puck 0.18</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Thu, 23 Jan 2025 13:29:44 +0000</pubDate>
      <link>https://dev.to/puckeditor/revolutionizing-drag-and-drop-in-react-introducing-puck-018-1a68</link>
      <guid>https://dev.to/puckeditor/revolutionizing-drag-and-drop-in-react-introducing-puck-018-1a68</guid>
      <description>&lt;p&gt;&lt;em&gt;&lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is the open-source visual editor for React, that you can embed in any application to create the next generation of page builders and no-code products. Give us a star on &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;! ⭐️&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;🎉 &lt;strong&gt;&lt;a href="https://github.com/puckeditor/puck/releases/tag/v0.18.0?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;Puck 0.18&lt;/a&gt; is here!&lt;/strong&gt; 🎉&lt;/p&gt;

&lt;p&gt;This update marks a massive leap forward with the debut of our groundbreaking drag-and-drop engine with support for CSS Grid and Flexbox. This eliminates all previous limitations and introduces unparalleled flexibility, so your users can drag and drop any component, anywhere.&lt;/p&gt;

&lt;p&gt;What do I mean by this? See it for yourself:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshc5mfsafmhir0ohkrg8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fshc5mfsafmhir0ohkrg8.gif" alt="GIF showing a component being dragged across a CSS grid layout" width="760" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means your page builders can now support virtually &lt;em&gt;any design&lt;/em&gt; your users dream up—all without writing a single line of code. No more compromises or workarounds—just pure creative freedom.&lt;/p&gt;

&lt;p&gt;But this milestone doesn’t just improve Puck—it transforms it into the ultimate design-in-browser tool by adding a level of flexibility that scales to the level of granularity that you need.&lt;/p&gt;

&lt;p&gt;Whether you want a straightforward page builder with page-level blocks or a highly granular visual layout builder for smaller, atomic components, Puck can adapt to your needs. From websites to document editors to infographic designers—if it’s built with React components, Puck can now bring it to life.&lt;/p&gt;

&lt;p&gt;Let’s take a closer look at the main features in Puck 0.18 and how you can start using them today.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For a deeper dive, check out the &lt;a href="https://github.com/puckeditor/puck/releases/tag/v0.18.0?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;official release notes and full changelog&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating to the New Version
&lt;/h2&gt;

&lt;p&gt;Before we dive in, let’s get you up to speed. &lt;a href="https://puckeditor.com/docs/getting-started?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;Getting started&lt;/a&gt; with the latest version of Puck is quick and straightforward.&lt;/p&gt;

&lt;p&gt;If you’re starting fresh with a new project, just install it using your favorite package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @puckeditor/core &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you are already using Puck you can update it in your existing project by running the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm update @puckeditor/core
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! Since this update doesn’t introduce any breaking changes, you can dive straight into exploring all the new features without worrying about compatibility issues. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗️ Free-form multidimensional Drag and Drop
&lt;/h2&gt;

&lt;p&gt;The new drag-and-drop engine is a complete game changer for user and developer experience. &lt;/p&gt;

&lt;p&gt;Previously, Puck’s drag-and-drop functionality was primarily restricted to the vertical axis, and could be extended with the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt;API to create multi-column layouts. This, however, could be cumbersome for users, as it required manually positioning components in each column and, if you wanted to increase the number of columns and reflow your items, you had to reorganize them manually:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1lrig8h21xib0x7jxr7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1lrig8h21xib0x7jxr7.gif" alt="Demo of multi-column drag-and-drop in Puck before version 0.18" width="760" height="368"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the release of 0.18, you can now freely drag and place React components in any direction on the canvas—vertically, horizontally, or within responsive grids. Puck will also give you instant visual feedback, showing exactly how the layout will look once the component is dropped:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdpf45la5hjxm2ozl7if.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdpf45la5hjxm2ozl7if.gif" alt="Demo of multi-column drag-and-drop in Puck in version 0.18" width="760" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Okay, that sounds amazing, but how do I set this up?" That’s the best part—it’s incredibly simple. All you need to do is style your wrapper &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt; as a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_grid_layout" rel="noopener noreferrer"&gt;grid&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Learn_web_development/Core/CSS_layout/Flexbox" rel="noopener noreferrer"&gt;flexbox&lt;/a&gt; in your Puck &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;config&lt;/code&gt;&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="nx"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//... fields configuration&lt;/span&gt;
    &lt;span class="nl"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
       &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-grid"&lt;/span&gt;
       &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repeat(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, 1fr)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
     &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it—this turns the &lt;code&gt;DropZone&lt;/code&gt;'s div into a CSS grid, ensuring that any component you drag and drop inside automatically aligns with the grid's flow. The same approach works seamlessly for flex containers too.&lt;/p&gt;

&lt;h3&gt;
  
  
  🧱 Advanced CSS layouts
&lt;/h3&gt;

&lt;p&gt;Previously, all Puck components were wrapped in a &lt;code&gt;div&lt;/code&gt;. This made it impossible to treat items as a direct descendant of their parent &lt;code&gt;DropZone&lt;/code&gt;, which can be necessary when working with CSS grid or flex-based layouts.&lt;/p&gt;

&lt;p&gt;Using the new &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#inline?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;inline&lt;/code&gt; parameter&lt;/a&gt;, you can remove the Puck wrapper entirely and treat the child as a direct descendant of the &lt;code&gt;DropZone&lt;/code&gt;. This is useful for building layouts because rules like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex-grow" rel="noopener noreferrer"&gt;&lt;code&gt;flex-grow&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-column" rel="noopener noreferrer"&gt;&lt;code&gt;grid-column&lt;/code&gt;&lt;/a&gt; behave exactly how you’d expect.&lt;/p&gt;

&lt;p&gt;For example, say you wanted to create a card grid where users can customize how many rows and columns each Card spans by using the &lt;code&gt;grid-column&lt;/code&gt; and &lt;code&gt;grid-row&lt;/code&gt; CSS rules—all you’d need to do is configure your components like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... fields configuration&lt;/span&gt;
  &lt;span class="c1"&gt;// Enable inline mode to remove default wrapper div&lt;/span&gt;
  &lt;span class="nl"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;spanRow&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;spanCol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1px solid black&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;gridColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spanCol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;gridRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spanRow&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="c1"&gt;// Pass the drag reference to the new draggable div&lt;/span&gt;
        &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dragRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Card Content
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="nx"&gt;Grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... fields configuration&lt;/span&gt;
  &lt;span class="nl"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
      &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-grid"&lt;/span&gt;
      &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repeat(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;columns&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, 1fr)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;gridTemplateRows&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`repeat(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, 1fr)`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, your users can build sophisticated multi-column layouts by simply tweaking a few fields in the editor:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznmxv3vkmuohe43qzmjr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fznmxv3vkmuohe43qzmjr.gif" alt="GIF showing drag-and-drop of components within a grid, including selection of the number of columns and rows each component spans" width="760" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This also works perfectly with flex containers and items. For instance, you could create a flex container that responsively wraps and scales items up and down depending on the viewport size and the item’s &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/flex" rel="noopener noreferrer"&gt;&lt;code&gt;flex&lt;/code&gt;&lt;/a&gt; rule:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lmtq9fzdf87758gmzbt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4lmtq9fzdf87758gmzbt.gif" alt="GIF showing drag-and-drop of components within a responsive flex container" width="760" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is just the tip of the iceberg. By ditching the wrapper and giving developers direct control over the components, Puck now opens the door for more ambitious layouts, more interactive experiences, and entirely new kinds of projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  💠 &lt;strong&gt;Drag between DropZones containers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;One of the most exciting upgrades in this release is the ability to drag components between &lt;code&gt;DropZones&lt;/code&gt; containers. &lt;/p&gt;

&lt;p&gt;Previously, you could only drag and drop between zones that shared the same parent, which, let’s be honest, felt pretty limiting. If your user needed to move something to another level in the hierarchy, they’d have to copy, paste, or re-enter the data entirely. Now, all that friction is gone. Whether you're moving a component or an entire &lt;code&gt;DropZone&lt;/code&gt; between sibling zones, dragging it into a nested child, or even pulling it back up to a parent, it just works—&lt;em&gt;no extra setup required on your end&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;This change streamlines the entire experience. Moving components around is now intuitive and seamless. For example, if you’re building a dashboard with nested widgets, you can now rearrange everything in one go—dragging charts, controls, or headers into any level of the layout:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7914bbiq5xrhkdxvky38.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7914bbiq5xrhkdxvky38.gif" alt="GIF showing a group of components being dragged and dropped between dropzone containers" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;PRO TIP: If you need to keep some restrictions, you can use the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#allow?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;allow&lt;/code&gt;&lt;/a&gt; prop on the &lt;code&gt;DropZone&lt;/code&gt; to control which components it accepts.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  📏 Dynamic DropZone height
&lt;/h3&gt;

&lt;p&gt;DropZones also received a major quality-of-life update: they now dynamically shrink to match the height of their children and accurately preview the final render.&lt;/p&gt;

&lt;p&gt;Even better, you can now configure a placeholder height for empty DropZones. This lets you define how they behave when there’s nothing inside, giving you full control over your editor’s layout and allowing you to visually prioritize DropZones as needed.&lt;/p&gt;

&lt;p&gt;To do this, all you need to do is set the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#minemptyheight?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;minEmptyHeight&lt;/code&gt;&lt;/a&gt; prop in your &lt;code&gt;DropZone&lt;/code&gt;. For example, you might want to show a short navbar DropZone at the top of your canvas while keeping the main content DropZone as tall as possible. In 0.18 you can now do this by configuring your &lt;code&gt;root&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
        &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"nav"&lt;/span&gt;
        &lt;span class="c1"&gt;// Set the height to 80 pixels when nothing is inside&lt;/span&gt;
        &lt;span class="na"&gt;minEmptyHeight&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
        &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;
        &lt;span class="c1"&gt;// Set the height to 500 pixels when nothing is inside&lt;/span&gt;
        &lt;span class="na"&gt;minEmptyHeight&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a thinner DropZone at the top of the page and a bigger one at the bottom:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ziildu5kc6g7zeomqwt.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ziildu5kc6g7zeomqwt.gif" alt="GIF showing a navbar dropzone and a main content dropzone, each with different empty heights" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By setting &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#minemptyheight?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;minEmptyHeight&lt;/code&gt;&lt;/a&gt;, you ensure your layouts look consistent while keeping empty zones accessible and user-friendly for your specific use-case.&lt;/p&gt;

&lt;p&gt;It’s a small change, but one that makes a huge difference for creating polished page building experiences.&lt;/p&gt;

&lt;h3&gt;
  
  
  🔠 Extending component Drawers with Grids
&lt;/h3&gt;

&lt;p&gt;By default, Puck renders the &lt;a href="https://puckeditor.com/docs/api-reference/components/drawer?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;Drawer&lt;/code&gt;&lt;/a&gt;—a container for all your draggable components—as a vertical list in the sidebar. In the past, you could personalize where you wanted this list to live by using &lt;a href="https://puckeditor.com/docs/extending-puck/custom-interfaces?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;custom interfaces&lt;/a&gt;. However, if you wanted to display it in a grid, that wasn’t really possible due to our legacy drag-and-drop engine. But with the new engine in 0.18, that limitation is gone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pmmxgmfzfieslv3hnkh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0pmmxgmfzfieslv3hnkh.gif" alt="GIF showing the list of components displayed as a grid" width="362" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Explore the rest of 0.18
&lt;/h2&gt;

&lt;p&gt;There’s so much more packed into 0.18 that I couldn’t fit into this post! If you’re into the nitty-gritty, the &lt;a href="https://github.com/puckeditor/puck/releases/tag/v0.18.0?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;release notes&lt;/a&gt; have all the details. But if you’d rather skip reading through them, here’s a quick rundown of the other features outside the drag-and-drop updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Toggle interactive hotkey&lt;/strong&gt;: Easily toggle component interactivity in Preview mode with the &lt;code&gt;cmd+i&lt;/code&gt; (or &lt;code&gt;ctrl+i&lt;/code&gt; on Windows) hotkey—super handy for testing interactive components without leaving your editor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Select parent action&lt;/strong&gt;: Quickly select a component’s parent directly from the action bar. It’s a small tweak, but it makes navigating nested components a lot smoother.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No more &lt;code&gt;position: fixed&lt;/code&gt;&lt;/strong&gt;: We’ve ditched this style from the default layout to make embedding Puck in your app a whole lot simpler.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New &lt;code&gt;&amp;lt;ActionBar.Label&amp;gt;&lt;/code&gt; component&lt;/strong&gt;: Organize and section off parts of your action bar using the new &lt;a href="https://puckeditor.com/docs/api-reference/components/action-bar-label?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;ActionBar.Label&amp;gt;&lt;/code&gt;&lt;/a&gt; component—it’s great for creating more intuitive component selectors.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For those ready to dive deeper, check out the &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;updated documentation&lt;/a&gt; to get up to speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing words
&lt;/h2&gt;

&lt;p&gt;Puck v0.18 really is a testament to what’s possible when we build tools with our community. This update is not just a step forward—it’s a leap forward, and it wouldn't have been possible without your feedback and contributions. Thank you.&lt;/p&gt;

&lt;p&gt;We love hearing about what you're building with Puck! Maybe you’re experimenting with drag-and-drop editors in React, crafting pixel-perfect designs, or dreaming up use cases we haven’t even thought of yet. Whatever it is, let us know!&lt;/p&gt;

&lt;p&gt;Let’s keep the conversation going:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Share your feedback, check out community plugins, ask questions, or just nerd out with us on &lt;a href="https://discord.gg/V9mDAhuxyZ" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Follow along for updates and insights on &lt;a href="https://bsky.app/profile/puckeditor.com?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt; and &lt;a href="https://x.com/puckeditor?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;X&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you’re enjoying the ride, we’d love your support—give us a 🌟 on &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=introducing_puck_018" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. It means a lot, and it helps us keep building the tools you love.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So here’s to Puck v0.18, the community, and all the things you'll create with it 🥂. Let’s see what you build next!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Puck 0.18: New drag-and-drop engine</title>
      <dc:creator>Chris Villa</dc:creator>
      <pubDate>Wed, 22 Jan 2025 12:47:25 +0000</pubDate>
      <link>https://dev.to/puckeditor/puck-018-new-drag-and-drop-engine-3h2o</link>
      <guid>https://dev.to/puckeditor/puck-018-new-drag-and-drop-engine-3h2o</guid>
      <description>&lt;p&gt;&lt;em&gt;Puck is the open-source visual editor for React, powering the next generation of page builders and no-code products. Give us a star on &lt;a href="https://github.com/measuredco/puck" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;! ⭐️&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/measuredco/puck/releases/" rel="noopener noreferrer"&gt;Puck 0.18&lt;/a&gt; launches a groundbreaking new drag-and-drop engine for page building with advanced CSS layouts.&lt;/p&gt;

&lt;p&gt;With native support for CSS grid and flexbox, &lt;a href="https://github.com/measuredco/puck" rel="noopener noreferrer"&gt;Puck 0.18&lt;/a&gt; enables powerful new paradigms for creating design-in-browser experiences directly within your React application.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft09jk8jgso0x4hqt8i7t.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft09jk8jgso0x4hqt8i7t.gif" alt="New drag-and-drop engine" width="1200" height="905"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TLDR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;New drag-and-drop engine&lt;/strong&gt;: Multi-dimensional drag-and-drop across any CSS layout to create a sophisticated page building experience. &lt;a href="https://puckeditor.com/docs/integrating-puck/multi-column-layouts" rel="noopener noreferrer"&gt;Read the docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic DropZone height&lt;/strong&gt;: DropZones now shrink to the height of their children, with a &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#minemptyheight" rel="noopener noreferrer"&gt;configurable height&lt;/a&gt; when empty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Toggle interactive hotkey&lt;/strong&gt;: Make your components interactive in Preview mode with the &lt;code&gt;cmd+i&lt;/code&gt; hotkey.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parent selector&lt;/strong&gt;: A new action allows you to quickly select the component's parent directly from the action bar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No more &lt;code&gt;position: fixed&lt;/code&gt;&lt;/strong&gt;: We've removed this pesky style from the default layout so it's easier to embed in your app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New ActionBar.Label component&lt;/strong&gt;: Create sections in your action bar with the new &lt;a href="https://puckeditor.com/docs/api-reference/components/action-bar-label" rel="noopener noreferrer"&gt; component&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Highlights
&lt;/h2&gt;

&lt;h3&gt;
  
  
  New drag-and-drop engine
&lt;/h3&gt;

&lt;p&gt;Our flagship feature is a new drag-and-drop engine for Puck with full CSS grid &amp;amp; flexbox support to enable advanced layouts. We call these &lt;strong&gt;fluid layouts&lt;/strong&gt;, and they are fully backwards compatible.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a class="mentioned-user" href="https://dev.to/clauderic"&gt;@clauderic&lt;/a&gt; at &lt;a href="https://github.com/clauderic/dnd-kit/" rel="noopener noreferrer"&gt;dnd-kit&lt;/a&gt; for all the support in making this possible, and the Puck community for all the feedback! 🙏 &lt;/p&gt;

&lt;h4&gt;
  
  
  Fluid layouts
&lt;/h4&gt;

&lt;p&gt;To implement a fluid layout, add your display property of choice (e.g. &lt;code&gt;display: flex&lt;/code&gt;) to your DropZone via the &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#style" rel="noopener noreferrer"&gt;&lt;code&gt;style&lt;/code&gt;&lt;/a&gt; or &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#classname" rel="noopener noreferrer"&gt;&lt;code&gt;className&lt;/code&gt;&lt;/a&gt; props and off you go—Puck will gracefully handle drag-and-drop across all dimensions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
          &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-content"&lt;/span&gt;
          &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Use flexbox in this DropZone&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rpvm74iop60tgrrt83i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5rpvm74iop60tgrrt83i.gif" alt="dragging items in a fluid layout with flexbox" width="800" height="191"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See the &lt;a href="https://puckeditor.com/docs/integrating-puck/multi-column-layouts" rel="noopener noreferrer"&gt;Multi-column Layouts docs&lt;/a&gt; for the full documentation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Remove wrapping elements
&lt;/h4&gt;

&lt;p&gt;The new &lt;a href="https://puckeditor.com/docs/api-reference/configuration/component-config#inline" rel="noopener noreferrer"&gt;&lt;code&gt;inline&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://puckeditor.com/v/canary/docs/api-reference/configuration/component-config#puckdragref" rel="noopener noreferrer"&gt;&lt;code&gt;dragRef&lt;/code&gt;&lt;/a&gt; APIs enable you to remove the wrapping element from Puck components entirely, which can be useful if you need to treat your component as a direct descendant of its parent (such as if you need to use CSS properties like &lt;code&gt;flex-grow&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Here's an example implementing an advanced grid layout, where the children can specify their position using the &lt;code&gt;grid-column&lt;/code&gt; and &lt;code&gt;grid-row&lt;/code&gt; properties:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
          &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-content"&lt;/span&gt;
          &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;grid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;gridTemplateColumns&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1fr 1fr 1fr 1fr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Use CSS grid in this DropZone&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Enable inline mode, removing the Puck wrapper&lt;/span&gt;
      &lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;puck&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;
          &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;puck&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dragRef&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Let Puck know this element is draggable&lt;/span&gt;
          &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;gridColumn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spanCol&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;gridRow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`span &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;spanRow&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// Apply styles&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0qdzo6cj1eshzan91jn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc0qdzo6cj1eshzan91jn.gif" alt="Advanced grid example" width="1440" height="613"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Dragging between nested DropZones
&lt;/h4&gt;

&lt;p&gt;The new engine makes it possible to drag between nested DropZones, which resolves one of the longest standing limitations of Puck's drag-and-drop experience. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2ae87gp529ba2mdhact.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb2ae87gp529ba2mdhact.gif" alt="dragging-between-nested-components" width="800" height="417"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic DropZone height
&lt;/h3&gt;

&lt;p&gt;DropZones now shrink to the height of their children so that the preview is a faithful representation of the final output, with a new &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone#minemptyheight" rel="noopener noreferrer"&gt;configurable height&lt;/a&gt; when empty.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;
  &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-content"&lt;/span&gt;
  &lt;span class="na"&gt;minEmptyHeight&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="c1"&gt;// The DropZone will grow to 256px when empty&lt;/span&gt;
&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn69ni79iazxhjs67m9f4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn69ni79iazxhjs67m9f4.gif" alt="Dynamic DropZone resize" width="768" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;&amp;lt;ActionBar.Label&amp;gt;&lt;/code&gt; component
&lt;/h3&gt;

&lt;p&gt;The new &lt;a href="https://puckeditor.com/docs/api-reference/components/action-bar-label" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;ActionBar.Label&amp;gt;&lt;/code&gt;&lt;/a&gt; component enables you to to label areas within a custom ActionBar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Label&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Label 1"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Label&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Label 2"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;★&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Action&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;ActionBar&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F79f7e5ac-1518-431b-99b6-e5ccdbae56f7" class="article-body-image-wrapper"&gt;&lt;img alt="image" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fuser-attachments%2Fassets%2F79f7e5ac-1518-431b-99b6-e5ccdbae56f7" width="442" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Parent selector
&lt;/h3&gt;

&lt;p&gt;A new action allows you to quickly select the component's parent directly from the action bar. Tap the arrow to the left of the component label to jump to the parent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04omqmkk8kalsltczfi4.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F04omqmkk8kalsltczfi4.gif" alt="parent-selector" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Toggle interactive hotkey
&lt;/h3&gt;

&lt;p&gt;Make your components interactive directly within Preview mode with the &lt;code&gt;cmd+i&lt;/code&gt; (or &lt;code&gt;ctrl+i&lt;/code&gt; on Windows) hotkey.&lt;/p&gt;

&lt;p&gt;This can be programatically set via the new &lt;a href="https://puckeditor.com/docs/api-reference/app-state" rel="noopener noreferrer"&gt;&lt;code&gt;previewMode&lt;/code&gt; parameter&lt;/a&gt; on the app state.&lt;/p&gt;

&lt;h3&gt;
  
  
  No more &lt;code&gt;position:fixed&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We've removed this pesky style from the default layout so it's easier to embed in your app. Not much to show here, but let's pour one out for &lt;code&gt;position:fixed&lt;/code&gt; 🥂&lt;/p&gt;

&lt;h2&gt;
  
  
  Breaking changes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Drawer &lt;code&gt;direction&lt;/code&gt; no longer has any effect
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;direction&lt;/code&gt; prop on &lt;code&gt;Drawer&lt;/code&gt; no longer has any effect. Instead, use it to wrap a &lt;code&gt;div&lt;/code&gt; with your chosen display mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Drawer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Drawer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Orange"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Drawer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DropZones are consistently wrapped in a div
&lt;/h3&gt;

&lt;p&gt;Previously, DropZones were only wrapped in a div within the editor (&lt;code&gt;&amp;lt;Puck&amp;gt;&lt;/code&gt;) environment, whereas the render (&lt;code&gt;&amp;lt;Render&amp;gt;&lt;/code&gt;) environment used a fragment. This could result in unexpected rendering differences between environments.&lt;/p&gt;

&lt;p&gt;Now, both environments use a div. If you were relying on the render environment behaving like a Fragment, you may need to adjust your styles. This can be done by applying your styles directly to the DropZone.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Before&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt; &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-zone"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Previously rendered as a fragment in &amp;lt;Render&amp;gt;, but div in &amp;lt;Puck&amp;gt; */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;After&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt; &lt;span class="na"&gt;zone&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"my-zone"&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;flex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Now consistently renders as a div - apply your styles or class directly */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 1&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Item 2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;DropZone&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deprecations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;index&lt;/code&gt; prop on &lt;code&gt;Drawer.Item&lt;/code&gt; is no longer required and will be removed in a future version.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;droppableId&lt;/code&gt; prop on &lt;code&gt;Drawer&lt;/code&gt; is no longer required and will be removed in a future version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Full changelog
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;add action to select parent component to ActionBar (&lt;a href="https://github.com/measuredco/puck/commit/7c910d5272e8d6d77819ccb3280dff143ea848fd" rel="noopener noreferrer"&gt;7c910d5&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;add ActionBar.Label component for adding labels to action bars (&lt;a href="https://github.com/measuredco/puck/commit/d2645fd68a57b4c07bb8a3948ab6a845c2ce1988" rel="noopener noreferrer"&gt;d2645fd&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;add DropZone collisionAxis API for forcing collision direction (&lt;a href="https://github.com/measuredco/puck/commit/ba687329c6fac5085f78768bff6eb37bfd842f33" rel="noopener noreferrer"&gt;ba68732&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;add meta+i hotkey and previewMode state to toggle interactivity (&lt;a href="https://github.com/measuredco/puck/commit/ec1eba58525e0245ee1214f8e401fa935c41fe23" rel="noopener noreferrer"&gt;ec1eba5&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;add wrapFields prop to control padding of fields in Puck.Fields (&lt;a href="https://github.com/measuredco/puck/commit/30f9a926d2640a5bf9f65d8f4c2b6018e73f8719" rel="noopener noreferrer"&gt;30f9a92&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;control empty DropZone height with minEmptyHeight prop (&lt;a href="https://github.com/measuredco/puck/commit/96f83408f4e6219dd35f5c29b204ef18e6d11d64" rel="noopener noreferrer"&gt;96f8340&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;deselect item on viewport change (&lt;a href="https://github.com/measuredco/puck/commit/e35585d767c857413ed5560f311d64bcab1218c4" rel="noopener noreferrer"&gt;e35585d&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;forward the ref to the DropZone component (&lt;a href="https://github.com/measuredco/puck/commit/676aa1c974bd1260aaa687aa3edc2c54ef34e22b" rel="noopener noreferrer"&gt;676aa1c&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;introduce new drag-and-drop engine (&lt;a href="https://github.com/measuredco/puck/commit/6ebb3b8724b8ed56cc76d3ce166b1dc87ed07dad" rel="noopener noreferrer"&gt;6ebb3b8&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;reduce DropZone to height of items unless empty (&lt;a href="https://github.com/measuredco/puck/commit/2b2595a4e3e1c5ed8352cdfbec704290a1b396e8" rel="noopener noreferrer"&gt;2b2595a&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;remove &lt;code&gt;position: fixed;&lt;/code&gt; from Puck layout (&lt;a href="https://github.com/measuredco/puck/commit/5deb7744c07fca12e6aa44d058b495f65b298eab" rel="noopener noreferrer"&gt;5deb774&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;support inline Drawers, deprecating unnecessary props (&lt;a href="https://github.com/measuredco/puck/commit/f93b71e1ad555184fc1a43f151ef1b161be148c6" rel="noopener noreferrer"&gt;f93b71e&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bug Fixes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;deselect item on delete (&lt;a href="https://github.com/measuredco/puck/commit/f27871b5b63be8246cd281d93c49f7744d7e186f" rel="noopener noreferrer"&gt;f27871b&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;improve heading-analyzer reliability (&lt;a href="https://github.com/measuredco/puck/commit/ab6c01862c35e27929b249a6d4bc4d2e9065dc12" rel="noopener noreferrer"&gt;ab6c018&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;never render FieldLabel with padding or borders (&lt;a href="https://github.com/measuredco/puck/commit/a97b54fd9427f3cd587951a0a30a95d56c5ff020" rel="noopener noreferrer"&gt;a97b54f&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;prevent propagation of custom ActionBar actions by default (&lt;a href="https://github.com/measuredco/puck/commit/14909bdc5a782330af661a32bc80ab387ab12897" rel="noopener noreferrer"&gt;14909bd&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;prevent user pollution of ActionBar styles (&lt;a href="https://github.com/measuredco/puck/commit/e154cb7c72c4fce735ccd60ccbdc862314f0ad26" rel="noopener noreferrer"&gt;e154cb7&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;render DropZones the same in Puck and Render (&lt;a href="https://github.com/measuredco/puck/commit/d975aaf90bf7d0956ccf1d6c377a6e20ba224801" rel="noopener noreferrer"&gt;d975aaf&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;reset resolveFields lastFields param when changing component (&lt;a href="https://github.com/measuredco/puck/commit/7fead35fddf8fef49b41508a27c0e6be458ab2c4" rel="noopener noreferrer"&gt;7fead35&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;select new item when dispatching duplicate action (&lt;a href="https://github.com/measuredco/puck/commit/e3d0025d08408103940c2f84c4524266288f38fd" rel="noopener noreferrer"&gt;e3d0025&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;set root DropZone to 100% height (&lt;a href="https://github.com/measuredco/puck/commit/3d93f46555372e83ead6f671e40970937802f5f4" rel="noopener noreferrer"&gt;3d93f46&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;stop actions from overflowing outside left of frame (&lt;a href="https://github.com/measuredco/puck/commit/c036b6d2036cc759e0a2eda6154bdec5b8a7784e" rel="noopener noreferrer"&gt;c036b6d&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;trigger iframe resize when closing devtools (&lt;a href="https://github.com/measuredco/puck/commit/2c0b782d41817caa2b6fae41fc52b1a7ccbb8d09" rel="noopener noreferrer"&gt;2c0b782&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contributors
&lt;/h2&gt;

&lt;p&gt;Thanks to our contributors and sponsors for making this huge milestone possible. New contributors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@1benw made their first contribution in &lt;a href="https://github.com/measuredco/puck/pull/791" rel="noopener noreferrer"&gt;https://github.com/measuredco/puck/pull/791&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like Puck? Give us a ⭐️ on &lt;a href="http://github.com/measuredco/puck/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
