<?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: Fede Bonel Tozzi</title>
    <description>The latest articles on DEV Community by Fede Bonel Tozzi (@fede_bonel_tozzi).</description>
    <link>https://dev.to/fede_bonel_tozzi</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%2Fuser%2Fprofile_image%2F2620135%2F3c294c70-07d5-4af4-8163-1329bb0138c4.jpeg</url>
      <title>DEV Community: Fede Bonel Tozzi</title>
      <link>https://dev.to/fede_bonel_tozzi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fede_bonel_tozzi"/>
    <language>en</language>
    <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>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>Top 5 Page Builders for React in 2026</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Sat, 08 Feb 2025 00:03:50 +0000</pubDate>
      <link>https://dev.to/fede_bonel_tozzi/top-5-page-builders-for-react-190g</link>
      <guid>https://dev.to/fede_bonel_tozzi/top-5-page-builders-for-react-190g</guid>
      <description>&lt;p&gt;You're building a modern web app, and everything is going smoothly—until a client says, &lt;em&gt;“I want to customize the layout myself.”&lt;/em&gt; Suddenly, you’re faced with a monumental challenge: building a page builder.&lt;/p&gt;

&lt;p&gt;It sounds simple—drag, drop, tweak—but we know better. A real page builder involves complex drag-and-drop logic, forms, and integration with both your back and front end components. It’s not just hard—it’s &lt;em&gt;a lot&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Yet, page builders are non-negotiable in a lot of projects. They’re the missing link in headless CMS stacks, and a core part of many low-code tools, letting users create and edit layouts without writing code. But, thankfully, you don’t have to build one from scratch—React’s ecosystem offers plenty of options.&lt;/p&gt;

&lt;p&gt;So, which ones are worth your time? I’ve narrowed it down to five top picks to help you choose the right tool for your project. Whether you’re putting the head back on your headless CMS, building a no-code platform, or a layout editor, this list will help you find the right tool for the job.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Choosing the Right Page Builder&lt;/li&gt;
&lt;li&gt;Puck - Visual Editor for React&lt;/li&gt;
&lt;li&gt;Storyblok - Headless CMS&lt;/li&gt;
&lt;li&gt;Builder.io - Headless CMS&lt;/li&gt;
&lt;li&gt;Tiptap - Visual Editor&lt;/li&gt;
&lt;li&gt;GrapesJS - Visual Editor&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚖ Choosing the Right Page Builder
&lt;/h2&gt;

&lt;p&gt;Before we get into it, it’s important to understand something: Not all page builders are created equal. The most critical factor when selecting one? &lt;strong&gt;Flexibility&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flexibility&lt;/strong&gt; means more than just drag-and-drop widgets or customizable layouts. The ideal page builder adapts to your needs without locking you into rigid patterns, headless CMS vendors, or a bloated API. It should:

&lt;ul&gt;
&lt;li&gt;Let &lt;strong&gt;users&lt;/strong&gt; create beautiful, versatile designs intuitively&lt;/li&gt;
&lt;li&gt;Empower &lt;strong&gt;developers&lt;/strong&gt; to integrate it seamlessly into their apps, match their website’s unique look and feel, and even tweak the UX for specific use cases&lt;/li&gt;
&lt;li&gt;Enable you to own your data, so that your web pages aren’t stuck with a proprietary vendor&lt;/li&gt;
&lt;/ul&gt;


&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%2Feu0z5h3c5m762c4h8t6y.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%2Feu0z5h3c5m762c4h8t6y.png" alt="flexibility venn diagram" width="778" height="778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short, a great page builder doesn’t just work; it works &lt;em&gt;with you&lt;/em&gt;. But let’s be real, every tool has tradeoffs. The trick is finding the one that balances ease of use, integration, customization, and long-term flexibility for your project.&lt;/p&gt;

&lt;p&gt;Ready to find the perfect fit? Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Puck - Visual Editor for React
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_page_builders" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is an embeddable, modular, open-source visual editor for React with built-in support for complex drag-and-drop page building. &lt;/p&gt;

&lt;p&gt;While other headless options in this list, like GrapesJS, provide a barebones page building experience, and tools like Builder.io and Storyblok offer fully-fledged CMS platforms, Puck aims to bridge the gap. It combines an extendable ready-to-use page editor with a decoupled page export model, giving you flexibility without locking you into a specific backend or proprietary ecosystem.&lt;/p&gt;

&lt;p&gt;With Puck, you don’t need to build an editor from scratch, commit to a propietary CMS, or go through a multi-step setup. In fact, you don’t even need a CMS at all if you don’t want one. Getting started is as simple as rendering the &lt;a href="https://puckeditor.com/docs/api-reference/components/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_page_builders" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;Puck /&amp;gt;&lt;/code&gt;&lt;/a&gt; component and passing a &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_page_builders" rel="noopener noreferrer"&gt;config object&lt;/a&gt; that defines which components you want to make draggable.&lt;/p&gt;

&lt;p&gt;It’s so straightforward that this is the only tool on this list where I can show you the setup in just a few lines of 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;// This renders your editor:&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="c1"&gt;// Your initial data here&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="c1"&gt;// Your draggable components here&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="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;text&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="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;&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;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;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="si"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// You export function here&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="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="nf"&gt;uploadSomewhere&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="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;Once that’s in place, you get a fully functional, user-friendly editor right out of the box:&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%2Fm9kn4n4wffh8m17s661v.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%2Fm9kn4n4wffh8m17s661v.gif" alt="Puck" width="700" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That simplicity, combined with its open-source flexibility, makes Puck a great fit for a variety of projects beyond just page building.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Puck’s strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 Pure open-source under an MIT license&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Puck is fully open-source under the &lt;a href="https://github.com/puckeditor/puck?tab=MIT-1-ov-file#readme" rel="noopener noreferrer"&gt;MIT license&lt;/a&gt;, making it a solid choice for both internal tools and commercial applications. There are no restrictive terms or hidden limitations—you can use it or fork it however you see fit. You don’t need to subscribe to anything or work around a specific platform, you embed the editor within your app and do whatever you want with it.&lt;/p&gt;

&lt;p&gt;What really sets Puck apart, though, is its strong community-driven development. The project has an active online presence, with real-time support available on &lt;a href="https://discord.gg/V9mDAhuxyZ" rel="noopener noreferrer"&gt;Discord&lt;/a&gt;, social media, and &lt;a href="https://github.com/puckeditor/puck" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Issues and feature requests don’t just sit unanswered—new ideas from the community are actively reviewed, planned, and implemented in each update. Also, if you contribute, your work is recognized and valued, which makes it great if you enjoy open-source collaboration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Free-form drag-and-drop&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Puck’s drag-and-drop experience feels intuitive, flexible, and polished. Unlike the other editors on this list, this one provides a clear visual preview of how elements will be reorganized before they’re placed, making layout adjustments smooth and predictable. More importantly, it’s not limited to a rigid block-based structure—you can freely drag elements into any CSS layout, including grids and flexboxes. Because of this Puck is a strong contender for both UI design or simple content authoring. In fact, you could use it for both at the same time for different user roles, since it provides a useful &lt;a href="https://puckeditor.com/docs/api-reference/permissions?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_page_builders" rel="noopener noreferrer"&gt;permissions API&lt;/a&gt; to disable and enable features on demand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹High flexibility&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the best things about Puck is that you don’t have to sacrifice developer experience for customization. You can use it with its default UI for a quick setup, &lt;a href="https://puckeditor.com/docs/extending-puck/custom-interfaces#ui-overrides?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_page_builders" rel="noopener noreferrer"&gt;extend it as much as you need&lt;/a&gt;, or &lt;a href="https://puckeditor.com/docs/extending-puck/custom-interfaces#composition?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_page_builders" rel="noopener noreferrer"&gt;even fully replace it&lt;/a&gt;. It gives you the same deep customization that GrapesJS offers, but with the convenience of out-of-the-box behavior like Builder.io, and you can mix and match as much as you want of each side—go fully headless, use Puck’s UI as-is, or integrate just the parts you need.&lt;/p&gt;

&lt;p&gt;Also, since Puck is just a React component that lets you drag and drop other components, it works seamlessly with all React.js environments like Next.js or Remix. And because its API is unopinionated, it’s not limited to page building—you can use it as a layout design tool, whether it’s a PDF creator, an infographic designer, or something completely custom. If it’s made of React components, Puck can handle it.&lt;/p&gt;

&lt;p&gt;Another major plus is that Puck lets you keep your layouts fully independent from any backend. It exports pages as JSON with a clean, easy-to-migrate schema, meaning you’re never locked into a vendor-specific backend.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Gentle learning curve - Easy to start building&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike GrapesJS, which requires you to build your own UI from scratch, Puck comes with a functional, ready-to-use interface out of the box. This means you can start building immediately and refine your setup over time, rather than needing to fully understand the entire API before getting anything useful done. Because of this, developer onboarding is much smoother—there’s no steep learning curve, and you won’t have to invest time training your team just to get started. Instead, you can focus on actually building and delivering value to your users.&lt;/p&gt;

&lt;p&gt;Another big plus is that Puck is unopinionated, so there’s no need to introduce users to vendor-specific concepts or workflows. You can shape the editor to fit their existing knowledge and preferences, reducing friction and making adoption even easier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Plugin API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Puck also provides a Plugin API. This means you can build some common custom behavior you may have on multiple editors once, package it, and reuse it as much as you need without repeating or rethinking setups and ensuring you remain consistent across all the experiences you build.&lt;/p&gt;

&lt;p&gt;You don’t need to build your own plugins either, Puck already has an active ecosystem of community-built plugins for things like rich text editing, styling libraries, and specific CMS integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Puck’s limitations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔻 No out-of-the-box storage&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Puck is purely a visual editor library, not a full CMS. This means it doesn’t come with built-in storage or backend support—you’ll need to handle that part yourself. While this gives you complete control over how and where your data is stored, it also means that if you’re looking for a fully managed, plug-and-play solution, Puck might not be the best fit. If your project requires a backend and you don’t already have one in place, you’ll need to set up your own storage system or integrate it with an existing CMS or database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Still in Pre-1.0 Development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Puck is still in its early stages, with a version number below 1.0. This means that while it’s already a powerful and flexible tool, some APIs are still evolving and could change. The good news is that the team has been clear about their commitment to backward compatibility—they aim to minimize breaking changes and provide ample time for migrations when necessary. Still, if you're looking for absolute long-term stability right now, this is something to keep in mind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 No collaboration features&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Puck doesn’t come with real-time collaboration out of the box. If your project requires multiple users editing the same content simultaneously, you’ll need to set up your own solution—whether that’s using WebSockets, CRDTs, or an existing third-party service. While this gives you the flexibility to implement collaboration in a way that fits your stack, it does mean extra effort on your end if collaborative editing is a core requirement.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best For
&lt;/h3&gt;

&lt;p&gt;Puck is a great choice if you want full control over your page builder while keeping development time minimal. It’s ideal if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need a fully embeddable, customizable editor that blends seamlessly into your app’s UI.&lt;/li&gt;
&lt;li&gt;Already have (or plan to use) a headless CMS, REST API, or other backend setup—or simply want to keep your options open without vendor lock-in.&lt;/li&gt;
&lt;li&gt;Want to keep content editing entirely separate from your backend, ensuring flexibility and future-proofing your architecture.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, Puck might not be the best fit if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You’re looking for a fully managed, plug-and-play solution with built-in backend storage.&lt;/li&gt;
&lt;li&gt;You need absolute long-term API stability right now—while stability is improving, some APIs are still evolving as Puck matures.&lt;/li&gt;
&lt;li&gt;Real-time collaboration is a must-have and you don’t have the bandwidth to implement it yourself.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those trade-offs work for you, &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=top_5_page_builders" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; offers a powerful, open-source alternative to proprietary page builders, with plenty of flexibility to make it truly your own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Storyblok - Headless CMS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.storyblok.com" rel="noopener noreferrer"&gt;Storyblok&lt;/a&gt; is, first and foremost, a headless CMS that happens to include an optional visual editor. It helps developers and content creators manage structured content efficiently while providing an integrable visual editing experience.&lt;/p&gt;

&lt;p&gt;At first glance, Storyblok might seem to "break" the headless CMS model by offering an editor. However, rather than functioning like a traditional CMS, it achieves this through SDKs and iframes, allowing developers to integrate a visual editing experience in their client code without being locked into a specific UI. This approach gives the flexibility of a headless CMS while maintaining the core advantages of a traditional one.&lt;/p&gt;

&lt;p&gt;That said, because Storyblok is a proprietary CMS first, page builder second, there are inherent trade-offs. Let’s break down its strengths, limitations, and when it makes the most sense to use.&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%2F6l1tyz6vqla0dokcoeuk.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%2F6l1tyz6vqla0dokcoeuk.gif" alt="Storyblock Demo" width="699" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Strengths of Storyblok’s editor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 User-Friendly Visual Editor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Storyblok’s editor allows users to preview and edit content dynamically on different client apps. This works by integrating an SDK into your client code that in turn embeds your app in the editor through an iframe. This means that instead of adding the editor to your app, you’re embedding your app into their editor, which can be useful if you need a quick way to enable visual content creation with a solid out-of-the-box user experience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Streamlined component implementation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Storyblok lets you add custom components to the editor as draggable blocks for users. To enable this, the editor’s SDK maps your React components to the content types you’ve defined in Storyblok’s CMS schema. This approach works well for structured content and makes integrating your React applications more seamless. Instead of handling complex configurations, you simply implement the components for each content type you want to support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Multi-Framework Compatibility&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;While this article focuses on React, Storyblok’s editor also works with Vue, Next.js, Astro, Svelte, and more. This means that, if you are working in a project that needs a page builder that works across multiple frameworks—say, different departments in a company using different stacks—Storyblok can be a solid choice for you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Collaboration &amp;amp; Workflow Tools&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;A major advantage of Storyblok is its editorial collaboration features. If you integrate this editor, you can also expect out of the box support for role-based access control, versioning, and approval workflows. This makes it ideal for teams that need a structured content creation and review process.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Limitations of Storyblok’s editor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔻 Locked into Storyblok’s Ecosystem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unlike standalone page builders, Storyblok’s editor is only available when using its vendor specific CMS and SDKs. This means your client code is dependent on Storyblok, making it difficult to switch to a different backend or page builder later on. This is what we would call vendor lock-in, which is a risk you should consider before selecting this page builder for your project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Proprietary &amp;amp; SaaS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Storyblok operates as a SaaS product, requiring a paid subscription. This means the editor is an external service you use and pay regularly, it’s not yours, and it's not part of your product. You can’t change or fork it, and you’ll only be able to use it as long as you’re able to pay for it. As I mentioned before, you embed your code in their editor, and not the other way around. While that may be useful in the short term, that could be a dealbreaker for you if, in the long run, you plan to migrate lots of pages to some other system.&lt;/p&gt;

&lt;p&gt;Also, at larger companies, proprietary tools like Storyblok need to go through a lengthy procurement process, which may not be viable for you or your project restrictions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Limited Control Over the Editor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The page builder exists as a separate application hosted by Storyblok, which means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Users must navigate to a different website to access it, requiring separate credentials from your admin panel and possibly providing a different user experience than your own—making the creation flow feel disconnected.&lt;/li&gt;
&lt;li&gt;You can’t embed or modify the editor’s UI or behavior, as it’s a completely separate application. This makes it unsuitable for projects requiring editor customization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🔻 Steep learning curve&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Understanding Storyblok’s content structure (Spaces, Stories, Blocks) can be overwhelming, particularly for those new to headless CMSs. There are a lot of concepts you, and your users, will need to learn in order to start using this CMS and integrating it to your application. This means you will have to invest some additional time in learning, documenting and training of your team and non-technical users.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;Storyblok is a great fit if you’re looking for a headless CMS with a built-in page builder, especially if structured content management is a top priority in your project and editorial teams need a visual interface.&lt;/p&gt;

&lt;p&gt;However, if you need more design features or a self-hosted, standalone, or highly customizable page builder, Storyblok is not the best choice for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Builder.io - Headless CMS
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.builder.io" rel="noopener noreferrer"&gt;Builder.io&lt;/a&gt; is a proprietary headless visual CMS designed for low-code and no-code experiences, with a strong emphasis on its visual editor. In fact, Builder.io calls itself a “visual development platform”—a fitting description since it provides a packaged environment for visually building and deploying content-driven webpages.&lt;/p&gt;

&lt;p&gt;While it shares similarities with Storyblok, there’s a key distinction: Storyblok is a headless CMS first and a page builder second, whereas Builder.io treats both equally. This makes it a strong choice for projects that need structured content management alongside extensive design features and a packaged backend.&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%2Fharztzgj0lh1yv1nmxm5.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%2Fharztzgj0lh1yv1nmxm5.gif" alt="Builder.io demo" width="700" height="312"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Strengths of Builder.io’s editor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 Advanced visual editing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;At first glance, Builder.io’s visual editor looks a lot like Storyblok’s, but after using it, I found it to be more polished—especially when it comes to drag-and-drop functionality. You can actually drag blocks onto the canvas and get a better sense of where they’ll land in the layout, which makes it feel more intuitive for UI design rather than just content authoring.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Powerful developer tooling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;From a developer’s perspective, integrating Builder.io’s editor isn’t all that different from Storyblok. You install an SDK, link your project on Builder.io’s site, register your components, and decide where the content should render—that’s pretty much it. Once set up, users can edit pages directly on Builder.io’s platform.&lt;/p&gt;

&lt;p&gt;The improvement on this, though, is that you can visually integrate it into your code using Builder.io’s SDK and DevTools. This reduces the need for manual setup and cuts down on boilerplate, making the process smoother, guided and overall more intuitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Extensive docs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The platform has plenty of documentation to guide both developers and end users, which is always a plus. There are detailed guides, videos, and examples to help with setup, customization, and editor usage, making it easier to get things working without too much guesswork.&lt;/p&gt;

&lt;p&gt;That said, if you’re new to CMS platforms, the sheer amount of docs can feel a bit overwhelming. You might find yourself clicking through page after page just to piece things together. It’s thorough, but expect a bit of a learning curve if you’re not familiar with how headless CMSs work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Modular approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Like Storyblok, Builder.io lets you add your own React components to the editor for drag-and-drop use. However, the way it handles this is a bit different—you define your components in your code first, and they become available in the editor after. This setup works well if you're integrating Builder.io into a single project, since you maintain full control over the component definitions on your code. But if you’re managing multiple code bases, it can get tricky to keep track of what needs to be implemented where, since there’s no single source of truth inside the editor itself.&lt;/p&gt;

&lt;p&gt;One area where this editor stands out, though, is its support for Symbols and Templates. Symbols let you group and reuse components with specific props across pages directly in the editor, while templates work the same but allow updating those props. This can be handy because you can implement smaller, more flexible components and let users mix and match them however they need.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Limitations of Builder.io’s editor
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔻 It’s a proprietary solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Builder.io’s biggest limitation is that it’s a proprietary platform—you can’t self-host or embed the editor in your app, as both your designs and the editor run on their servers. You might see mentions of Builder.io being open-source, but to clarify: &lt;a href="https://forum.builder.io/t/is-builder-io-open-source-software-can-we-self-host/2634/2" rel="noopener noreferrer"&gt;only their SDKs are, not the visual editor itself&lt;/a&gt;. This means you’re limited to the features and customization options they provide. If you want to modify the page building experience, such as introducing your own field types or embedding it into your own application, this could be a deal breaker.&lt;/p&gt;

&lt;p&gt;Another thing to keep in mind is content portability. Your pages are tightly integrated with Builder.io’s system, and while they offer an AI-powered export feature, it’s paywalled—so migrating your pages elsewhere could come with extra costs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Access under SaaS model&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Builder.io also follows a SaaS model requiring a monthly subscription to use their editor. Pricing depends on multiple factors, but a key one is “visual views”—which works similarly to how Google Analytics counts page views. This means that your costs will scale based on how often your pages are viewed, even though Builder.io only hosts your content and page design with your client still needing to be hosted somewhere else.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Overkill for simple page editing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If all you need is a straightforward visual editor for building pages or layouts, Builder.io is overkill. It’s a powerful platform with a lot of features, but if you’re not planning to use its CMS, code generation, targeting, or integrations, it could feel more complex and expensive than necessary. While it offers plenty of built-in solutions, if you're just looking for flexible drag-and-drop page editing with export functionality that can easily be embedded in your app and integrated with any backend, there are simpler more modular tools that might be a better fit.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;Builder.io shines in situations where you're working on more complex projects, especially in areas like e-commerce or content-heavy websites where you need a reliable visual editor packaged with a CMS out of the box. If you're already planning on using a content management system (CMS) or know that you'll need one in the future, Builder.io's integration capabilities can be a real asset.&lt;/p&gt;

&lt;p&gt;That said, if you don’t need that level of complexity, already have a CMS and want to keep it separate from your page editing, prefer a budget-friendly option, or need to embed the page building into your own application, there are other solutions on this list that might serve you better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tiptap - Visual Editor
&lt;/h2&gt;

&lt;p&gt;While Storyblok and Builder.io offer full-page editing experiences with structured CMS capabilities, &lt;a href="https://github.com/ueberdosis/tiptap/tree/develop" rel="noopener noreferrer"&gt;Tiptap&lt;/a&gt; takes a different approach. It’s not a traditional page builder but rather an embeddable headless editor built on &lt;a href="https://prosemirror.net/" rel="noopener noreferrer"&gt;ProseMirror&lt;/a&gt;. This means that instead of giving you a predefined UI to work with, it provides the underlying logic, leaving you in full control of the interface, interaction and level of functionality you want to provide in your page builder.&lt;/p&gt;

&lt;p&gt;Because of this, Tiptap is an interesting option if you want to build a fully custom page editing experience rather than relying on an off-the-shelf solution. While it’s primarily a text editor, it can be extended to support drag-and-drop for custom React components and it allows exporting to HTML and JSON schemas, making it a viable option for page building.&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%2F9fbcd6m8zpddyhska9pb.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%2F9fbcd6m8zpddyhska9pb.gif" alt="Tiptap demo" width="400" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ Tiptap’s strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 Open-source under an MIT license&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of Tiptap’s biggest strengths is its open-source nature. Licensed under MIT, it gives you full control to modify, commercialize and extend it however you need—no vendor lock-in, no restrictions. You get a powerful, headless editor that you can integrate into your own app and self-host without worrying about licensing fees or usage limits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Extension based architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tiptap is built around an extension-based architecture, making it highly customizable while also providing a solid set of pre-built extensions for common editing needs like code blocks, horizontal rules, and more. This means that while Tiptap is headless, you don’t have to start from scratch or reinvent the wheel every time you add essential functionality.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Open-source backend for collaborative editing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another positive aspect to Tiptap is that, while it doesn’t tie you to any specific backend, it does provide an optional open-source server to add fullstack self-hosted collaborative editing. This server is based on the Y.js framework, and it provides a lot of the functionality you need to add an scalable collaborative editing experience, without much work from your side. Interally that is the same stack they use as the base of their own cloud SaaS solution, so it’s a nice touch that they’re releasing it to the public so you can decide if hosting it by yourself or not. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Highly customizable&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since Tiptap is a headless, block-based text-editing framework, you’re free to design the UI and interactions exactly how you want while still benefiting from the structured logic and opinionated API it provides. There’s no default styling and no forced layouts—just a foundation you can build on to fit your needs. This is extremely useful if you have a specific requirements for a page builder, or want to make the editor your own unique experience without any third party branding.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ Tiptap’s Limitations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔻 Requires a long time to set up&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tiptap is powerful, but it’s not something you can just plug in and start using right away. Since it’s completely headless, it doesn’t provide an intuitive drag-and-drop UI out of the box—you have to build that yourself. As I mentioned before, this gives you full control, but it also means setup can take significantly longer compared to traditional page builders. If you're a developer looking for a drop-in solution, you might find the initial work required to be a bit of a hurdle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Steep learning curve and not beginner friendly&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another disadvantage to Tiptap being fully headless, is that getting up to speed with it takes time. It’s a framework and it requires you to build your UI from the ground up, so you’ll need to spend time learning its APIs, understanding its extension-based architecture, and figuring out how to structure your editor effectively. If you're working with a team, onboarding will also take longer, as everyone will need to get familiar with Tiptap’s approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔻 Falls short for design-in-browser&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since Tiptap is fundamentally a text editor, its page-building capabilities have limits—especially when it comes to full-fledged layout design. While you can extend it to support drag-and-drop interactions, its architecture is more suited for a block-based editor (similar to Notion) rather than a flexible, multi-column, responsive design tool.&lt;/p&gt;

&lt;p&gt;If you need true WYSIWYG page-building with pixel-perfect layout control, getting Tiptap to work that way will require significant effort. You’d have to implement a lot of custom logic to handle grid systems, nested components, and fluid layouts, which is a level of complexity other page builders handle natively.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best for
&lt;/h3&gt;

&lt;p&gt;If you have the time and resources to build a custom page builder—especially one focused on text-heavy content like documents, articles, blogs, or knowledge bases—Tiptap could be a great fit. It gives you full control over the editing experience while letting you decouple content storage from it.&lt;/p&gt;

&lt;p&gt;However, if you still want Tiptap’s open-source ethos, customization capabilities, and extension/plugin based architecture, but need a more traditional page builder with flexible layout editing and an extensible UI out of the box, you might find other editors on this list better suited.&lt;/p&gt;

&lt;h2&gt;
  
  
  GrapesJS - Visual Editor
&lt;/h2&gt;

&lt;p&gt;Like Tiptap, &lt;a href="https://github.com/GrapesJS/grapesjs" rel="noopener noreferrer"&gt;GrapesJS&lt;/a&gt; is a headless framework, but one specifically built for page editors, giving you the flexibility to create fully custom drag-and-drop experiences without being tied to a specific CMS, backend, or ecosystem. This means it provides the core editing logic but leaves most of the UI implementation and content management entirely up to you.&lt;/p&gt;

&lt;p&gt;GrapesJS has two main offerings: the open-source framework which is headless and unopinionated, and the Studio, a cloud-based, proprietary version that adds additional out-of-the-box functionality and an opinionated API to reduce boilerplate when setting up your editor. For this section, I'll focus on the framework version, since it's the one we have full control over.&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%2Fjmmdgi2u2qzjimn2423n.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%2Fjmmdgi2u2qzjimn2423n.gif" alt="GrapesJS gif" width="791" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ GrapesJS’s strengths
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;🔹 BSD 3-clause License&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Similarly to Tiptap, one of the biggest advantages of GrapesJS is its open-source nature. The framework is licensed under BSD-3, meaning you’re free to use, modify, and extend it without any licensing fees or restrictions. This also means, your editor won’t be tied to a specific platform or ecosystem and you will be able to embed it in your app and customize it however you see fit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔹 Headless editor for page building&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GrapesJS is a headless editor specifically built for creating web pages. This means that, unlike Tiptap, which focuses on text editing, it provides a drag-and-drop interface and APIs for handling HTML elements, and CSS. However, like Tiptap, it doesn’t enforce a specific UI, styling system, or backend, giving you full control over the user experience, from the toolbar setup to the styling logic.&lt;/p&gt;

&lt;p&gt;The drag-and-drop interaction feels very similar to Builder.io’s and is generally smoother than Storyblok’s. However, it does have some quirks—especially when working with complex multi-column layouts using Grid or Flexbox since it doesn’t always preview how elements will fit before you drop them.&lt;/p&gt;

&lt;p&gt;🔹 &lt;strong&gt;Exports to HTML and CSS&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the key features of GrapesJS is its ability to export content as HTML and CSS. Unlike some page builders that rely on proprietary data formats or JSON-based schemas, GrapesJS ensures that whatever you build can be used in any web environment without extra processing. This makes it especially useful for apps that generate static pages or email templates, and makes it play nicely with most web frameworks and CMS platforms, giving you plenty of flexibility in how you integrate it into your system.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ GrapesJS’s limitations
&lt;/h3&gt;

&lt;p&gt;🔻 &lt;strong&gt;Long setup&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GrapesJS gives you a lot of flexibility, but that comes with a tradeoff—setup can be time-consuming. Since it’s a headless framework, it provides only a minimal pre-built UI and page-building experience. That means you’ll need to configure everything yourself, from the layout to the styling system, to get it fully production-ready. And it’s not just about visuals, even basic features, like adding custom fields for a block, require extra work compared to other editors since the framework is designed to be as unopinionated as possible.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Steep learning curve and complex API&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;GrapesJS offers a powerful API, but it’s not the most intuitive. Unlike some page builder libraries that use a more declarative, configuration-based approach, GrapesJS requires you to work directly with JavaScript and HTML-based APIs to define building blocks, styles, and interactions. This is mainly because it’s framework-agnostic and there’s no built-in abstraction for popular front-end libraries, which makes it feel a bit barebones at first.&lt;/p&gt;

&lt;p&gt;This also means getting up and running will take you some effort—you’ll need to spend time reading the documentation and getting familiar with its way of handling editor logic. This is similar to what happened with Tiptap, it has lot of flexibility, but that comes with a steeper learning curve.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;JS Only – Not a React-based component system&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;While GrapesJS supports JSX, it doesn’t provide built-in integrations for React. It does provide a React package, but its focus is more on rendering and editing the editor UI rather than allowing you to add React components as draggable elements. Basically this means you can’t directly use React components as building blocks within the editor, which isn’t helpful if you’re using React for your front-end.&lt;/p&gt;

&lt;p&gt;🔻 &lt;strong&gt;Can be hard to use for non-technical users&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Even though GrapesJS is a visual editor, it leans heavily on an HTML and CSS model. So, while you can create custom blocks and fields and abstract away some complexity, the core editing logic you will be writing still revolves around direct manipulation of HTML elements and styles. &lt;/p&gt;

&lt;p&gt;This makes the final editor great for developers who want fine-grained control, but it’s not as user-friendly for non-technical users who expect a more guided, WYSIWYG-style experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎯 Best For
&lt;/h3&gt;

&lt;p&gt;GrapesJS is a solid choice if you need a highly customizable, self-hosted page-building framework with full control over the editing experience. It’s especially useful if your project requires an open-source alternative with highly customized drag-and-drop page building.&lt;/p&gt;

&lt;p&gt;That said, if you're looking for a more plug-and-play visual editor with built-in CMS features, tools like Builder.io or Storyblok might be a better fit. If your focus is more on text heavy content rather than full-page layouts, Tiptap would be a better match. And if you need an embeddable editor that works out of the box with free-form drag-and-drop, flexible APIs, and open-source licensing, then Puck might be a better fit.&lt;/p&gt;

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

&lt;p&gt;In this article I explored some of the most relevant page builders out there, and broke down their strengths, limitations, and ideal use cases. More than just listing features, I tried focusing on flexibility—how these tools balance developer and user experience, and avoiding vendor lock-in—which are key factors when choosing the right editor for your project.&lt;/p&gt;

&lt;p&gt;At the end of the day, like everything in engineering, the best tool you can choose will depend on your needs. Whether you're looking for a fully customizable framework, a quick-to-integrate solution, or something in between, I hope this breakdown helped you navigate the options and make your decision. 🚀&lt;/p&gt;

&lt;p&gt;💬 If you have more editors in mind you think I should add to the list, thoughts, questions, or just want to chat more about this, feel free to reach out! You can find me on &lt;a href="https://www.linkedin.com/in/federico-jorge-bonel-tozzi/" rel="noopener noreferrer"&gt;&lt;strong&gt;LinkedIn&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://x.com/fe_bo_to" rel="noopener noreferrer"&gt;&lt;strong&gt;X&lt;/strong&gt;&lt;/a&gt;, and &lt;a href="https://bsky.app/profile/fe-bo-to.bsky.social" rel="noopener noreferrer"&gt;&lt;strong&gt;Bluesky&lt;/strong&gt;&lt;/a&gt;. Looking forward to hearing your take!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cms</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>Managing application state in Puck</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Tue, 14 Jan 2025 13:16:08 +0000</pubDate>
      <link>https://dev.to/puckeditor/managing-application-state-in-puck-4nek</link>
      <guid>https://dev.to/puckeditor/managing-application-state-in-puck-4nek</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=managing_application_state" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is the open-source visual editor for React, empowering 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=managing_application_state" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;! ⭐️&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Puck has been rapidly growing, and it’s been awesome to watch! 🚀 Developers from all backgrounds are pushing the boundaries of what this open-source visual editor can do. But as more people dive into Puck, one question keeps coming up in our &lt;a href="https://discord.gg/D9e4E3MQVZ" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“How can I pass data or share state between components in Puck?”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words: how do you make one component react to changes in another? For example, you might create a &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=managing_application_state" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt; component with a search input, so that any lists dropped inside it can read its value:&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%2Fjwgn7hb1i07w9jf3ikzz.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%2Fjwgn7hb1i07w9jf3ikzz.gif" alt="Intro example" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At first, Puck’s built-in magic might make you think it handles state in a unique way. But here’s the thing: &lt;strong&gt;Puck is just React—and so are the components you pass into it.&lt;/strong&gt; That means that you can rely on any state library or tool you would normally use to manage and share data between components. For this article, I’ll keep it simple and teach you how to solve this problem by using &lt;a href="https://react.dev/learn/passing-data-deeply-with-context" rel="noopener noreferrer"&gt;Context&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Before we get started: I’ll assume you’ve got a basic understanding of Puck and how it works. If you’re new here, that’s totally fine—you’re welcome to follow along! But 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=managing_application_state" rel="noopener noreferrer"&gt;getting started guide&lt;/a&gt; first to get familiar with the basics&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;To make things easy, I’ve prepared a basic &lt;a href="https://github.com/FedericoBonel/basic-puck-app" rel="noopener noreferrer"&gt;React project on GitHub&lt;/a&gt; with Puck pre-installed and ready to go. Clone and install it by running the following commands in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/FedericoBonel/basic-puck-app
&lt;span class="nb"&gt;cd&lt;/span&gt; ./basic-puck-app
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Already working on an existing project? No problem at all! You can simply install Puck as a dependency 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 i @puckeditor/core &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you’re using frameworks like Next.js or Remix, Puck offers &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=managing_application_state" rel="noopener noreferrer"&gt;official recipes&lt;/a&gt; to make the setup process seamless.&lt;/p&gt;

&lt;p&gt;For this tutorial, I’ll assume you’ve cloned the GitHub repo to keep things straightforward. That said, the concepts and steps will apply to any setup—just update the file names as needed to fit your project’s structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Puck
&lt;/h3&gt;

&lt;p&gt;With your project ready to go, the next step is to configure Puck. Open the &lt;code&gt;src/App.jsx&lt;/code&gt; file and swap its contents with the code below. This will set up Puck with a basic &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=managing_application_state" rel="noopener noreferrer"&gt;config&lt;/a&gt; for dragging and dropping two components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;Dashboard&lt;/code&gt; component that greets users and contains a &lt;a href="https://puckeditor.com/docs/api-reference/components/drop-zone?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=managing_application_state" rel="noopener noreferrer"&gt;&lt;code&gt;DropZone&lt;/code&gt;&lt;/a&gt; for nesting other components&lt;/li&gt;
&lt;li&gt;An &lt;code&gt;ArticleList&lt;/code&gt; component that displays a list of articles inside the &lt;code&gt;Dashboard&lt;/code&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="c1"&gt;// App.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="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;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="c1"&gt;// The configs for your draggable components&lt;/span&gt;
&lt;span class="c1"&gt;// Ideally you would pull these out into their own files&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dashboardConfig&lt;/span&gt; &lt;span class="o"&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="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;minWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fit-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;50%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome User!&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="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;"list"&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;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/signup"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign up now&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&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="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;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;14px&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;
          Already have an account? &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/signin"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Sign in here&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&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;&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articleListConfig&lt;/span&gt; &lt;span class="o"&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;// These articles could come from an external data source&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt; &lt;span class="o"&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;Puck&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;React State&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;NextJS&lt;/span&gt;&lt;span class="dl"&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;ul&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;articles&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;article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;i&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;article&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;li&lt;/span&gt;&lt;span class="p"&gt;&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;ul&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;// The Puck configuration object&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;Dashboard&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dashboardConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ArticleList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;articleListConfig&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;// Your editor component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&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="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="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="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve updated the file, start the application in development mode, and navigate to &lt;a href="http://localhost:5173/" rel="noopener noreferrer"&gt;http://localhost:5173&lt;/a&gt; to verify everything is working as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&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%2Fzlmezbbkfe16wpy9ap1m.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%2Fzlmezbbkfe16wpy9ap1m.gif" alt="Initial Setup" width="960" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Your basic setup is now complete. Next, let’s dive into adding shared state to your editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Context
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://react.dev/learn/passing-data-deeply-with-context" rel="noopener noreferrer"&gt;React Context&lt;/a&gt; is the perfect solution for our problem because it offers a simple way to share and manage data across all your components—both inside and outside the editor. It creates a "global state" that you can access whenever needed, making it ideal for scenarios where you need to pull in data from outside Puck—like the selected theme or the logged-in user—or share data between Puck components.&lt;/p&gt;

&lt;p&gt;In this guide, I’ll walk you through two common use cases for React Context within Puck:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Accessing data stored outside Puck:&lt;/strong&gt; We’ll begin by setting up a context containing a logged-in user’s data outside of the &lt;code&gt;&amp;lt;Puck /&amp;gt;&lt;/code&gt; component, and then access it from within Puck components.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Passing data to a nested component:&lt;/strong&gt; Next, we’ll set up a search query context within the &lt;code&gt;Dashboard&lt;/code&gt;. This will allow us to capture a user’s search query, store it in the context, and pass it down to the &lt;code&gt;ArticleList&lt;/code&gt; component. The goal is to filter the list of articles based on the user’s query, demonstrating how to pass data between a parent and child Puck component.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Step 1: Define the Context outside Puck
&lt;/h3&gt;

&lt;p&gt;Setting up context in Puck follows the same pattern as any React app. You create a &lt;a href="https://react.dev/reference/react/createContext" rel="noopener noreferrer"&gt;Context provider&lt;/a&gt; to define and manage your shared state, wrap it around a parent component, and access or update the state wherever it's required in your app.&lt;/p&gt;

&lt;p&gt;Start by creating a new Context for the user data. This Context will include both the user object and a function to update the user state.&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;// context/userContext.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;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&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;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Create the Context&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Custom hook to use the User Context&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useUserContext&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;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserContext&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;useUserContext must be used within a UserProvider&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="nx"&gt;context&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;
  
  
  Step 2: Create the Context Provider outside Puck
&lt;/h3&gt;

&lt;p&gt;Next, create a &lt;code&gt;UserProvider&lt;/code&gt; component that will wrap your Puck editor. This provider will manage the &lt;code&gt;user&lt;/code&gt; state and make it available to all children.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For the sake of brevity, I’m using a dummy user and the setter function returned by useState.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// context/userContext.jsx&lt;/span&gt;

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

&lt;span class="c1"&gt;//eslint-disable-next-line react/prop-types&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UserProvider&lt;/span&gt; &lt;span class="o"&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Start the context state with a dummy user&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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;John Doe&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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="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;setUser&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;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="nc"&gt;UserContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;UserProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useUserContext&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Integrate the Provider with Puck
&lt;/h3&gt;

&lt;p&gt;To integrate the provider with your Puck editor, simply wrap the editor with the &lt;code&gt;UserProvider&lt;/code&gt;. You can put the &lt;code&gt;UserProvider&lt;/code&gt; anywhere above the editor in your component tree (like in your &lt;code&gt;index&lt;/code&gt; file), and it'll work just fine. Once you've done that, all your editor components will have access to the 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="c1"&gt;// App.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;UserProvider&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useUserContext&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;./context/UserContext&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="kd"&gt;const&lt;/span&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="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;UserProvider&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="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="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;UserProvider&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;h3&gt;
  
  
  Step 4: Consume the Context in the Puck Components
&lt;/h3&gt;

&lt;p&gt;Now you can access the &lt;code&gt;UserContext&lt;/code&gt; in any of your Puck components. Following our use case example, let’s update the &lt;code&gt;Dashboard&lt;/code&gt; component so that it displays a “welcome back” message for logged-in users and a “generic welcome” message for guests.&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;// App.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;useUserContext&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;./context/UserContext&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dashboardConfig&lt;/span&gt; &lt;span class="o"&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;// Access the user context&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;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useUserContext&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;minWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fit-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;50%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&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="si"&gt;{&lt;/span&gt;&lt;span class="cm"&gt;/* Read the user's 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;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome back &lt;span class="si"&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;name&lt;/span&gt; &lt;span class="o"&gt;??&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;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="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;"list"&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;/* Update the user on logout */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;Sign out 🏃🚪&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="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="c1"&gt;//... the Puck config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you're using eslint, you may see the linting error ‘React Hook "useUserContext" is called in function "render" that is neither a React function component nor a custom React Hook function.’ This is because ESLint can’t recognise the syntax of the render function as a component.&lt;br&gt;
To fix this you can pull the render function into its own component and pass it (&lt;code&gt;render: WelcomeCard&lt;/code&gt;), or ask ESLint to ignore the line (&lt;code&gt;eslint-disable-next-line react-hooks/rules-of-hooks&lt;/code&gt;).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now you can go back to the editor, drag and drop the new &lt;code&gt;Dashboard&lt;/code&gt; component, and you'll see that the &lt;code&gt;UserContext&lt;/code&gt; is being accessed to display the “welcome back” message for the dummy logged-in user:&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%2Fny4eyn4ep9bnbfb2w7zg.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%2Fny4eyn4ep9bnbfb2w7zg.gif" alt="Final first use case demo" width="960" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you were to &lt;a href="https://puckeditor.com/docs/api-reference/components/puck#onpublishdata?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=managing_application_state" rel="noopener noreferrer"&gt;publish&lt;/a&gt; that data and pass it 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=managing_application_state" rel="noopener noreferrer"&gt;Render&lt;/a&gt; component using the same config and &lt;code&gt;UserProvider&lt;/code&gt;, the result would look just as it was in the editor, and you could also click on the sign-out button to clear the logged-in user state:&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%2Fp6fjhitfm8b8kajt4pvm.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%2Fp6fjhitfm8b8kajt4pvm.gif" alt="Final first use case demo in render component" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this done, you now have a state originating outside of Puck, seamlessly passed to and updated by Puck components! ✔️&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Adding a search query
&lt;/h3&gt;

&lt;p&gt;Now let's move to our second use case: Passing data from a Puck component to another.&lt;/p&gt;

&lt;p&gt;To make this work, we’ll add a search query input and an &lt;code&gt;initialQuery&lt;/code&gt; prop to the &lt;code&gt;Dashboard&lt;/code&gt; component. This will allow users to filter the list of articles and give us the option to set an initial query for them to start with.&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;// App.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;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&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;react&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dashboardConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Set the default props for the initial query 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;initialQuery&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;React&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;// Create a field for setting the inital query&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;initialQuery&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;Initial Search Query&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;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// Read the initial query prop&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;initialQuery&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;// ... existing dashboard setup&lt;/span&gt;

    &lt;span class="c1"&gt;// Create the state for the search query&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;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setQuery&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialQuery&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Update the query's value from the editor's field&lt;/span&gt;
    &lt;span class="nf"&gt;useEffect&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;setQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;initialQuery&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;initialQuery&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;minWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fit-content&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;maxWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;50%&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;margin&lt;/span&gt;&lt;span class="p"&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;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Welcome back &lt;span class="si"&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;name&lt;/span&gt; &lt;span class="o"&gt;??&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;h3&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;/* Create the input the user will use to filter the lists */&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;input&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&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;query&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;setQuery&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="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;"list"&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;user&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;Sign out 🏃🚪&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="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;
  
  
  Step 6: Provide context from a Puck component
&lt;/h3&gt;

&lt;p&gt;Next, let’s set up a context in the &lt;code&gt;Dashboard&lt;/code&gt; component to pass down the query state. This will ensure that when the user updates the search input, the new query is stored in the context and shared with the &lt;code&gt;ArticleList&lt;/code&gt; or any other component dropped inside the &lt;code&gt;Dashboard&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="c1"&gt;// App.jsx&lt;/span&gt;
&lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

&lt;span class="c1"&gt;// Create the query context that will pass down the query state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;QueryContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&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;useQueryContext&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="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;QueryContext&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;dashboardConfig&lt;/span&gt; &lt;span class="o"&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;// ... existing dashboard setup&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;QueryContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setQuery&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;/* ... existing dashboard component */&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;QueryContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&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;//... the Puck config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7: Consume the Context from Puck components
&lt;/h3&gt;

&lt;p&gt;Now, we’ll read the context in the components that are dropped inside the context provider. In our case, we’ll consume the context in the &lt;code&gt;ArticleList&lt;/code&gt; component, which the user has nested inside the &lt;code&gt;Dashboard&lt;/code&gt; via the DropZone. This allows the &lt;code&gt;ArticleList&lt;/code&gt; to respond to changes in the search query and update accordingly.&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;// App.jsx&lt;/span&gt;
&lt;span class="c1"&gt;//... existing setup&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articleListConfig&lt;/span&gt; &lt;span class="o"&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;// Access the query state&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;query&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQueryContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Filter out the articles based on the query&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt; &lt;span class="o"&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;Puck&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;React State&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;NextJS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;article&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;article&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&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="c1"&gt;//... existing return statement&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 head into the editor, drag a &lt;code&gt;Dashboard&lt;/code&gt; component onto the canvas, drop an &lt;code&gt;ArticleList&lt;/code&gt; inside it, and modify the &lt;code&gt;initialQuery&lt;/code&gt; field, you’ll see the list dynamically filter the articles based on the query. 🎯&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%2F2vcd60i7v0rb2tmm6x74.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%2F2vcd60i7v0rb2tmm6x74.gif" alt="Final second use case demo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You could even expand this setup by having multiple list components with different content  reuse the same query context.&lt;/p&gt;

&lt;p&gt;🎉 And that’s it! You now have shared state between nested Puck components. 🎉&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros &amp;amp; Cons of using React Context
&lt;/h2&gt;

&lt;p&gt;✅ Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Provides a robust solution for sharing state across components, both inside and outside Puck&lt;/li&gt;
&lt;li&gt;Integrates seamlessly with existing React patterns and components&lt;/li&gt;
&lt;li&gt;Can handle complex logic and state&lt;/li&gt;
&lt;li&gt;Zero external dependencies as React Context comes with React&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance can degrade if you frequently update state at the top of a large component tree, as every subscriber needs to re-render&lt;/li&gt;
&lt;li&gt;When managing multiple context providers, things can become harder to debug&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Taking it further
&lt;/h2&gt;

&lt;p&gt;There are a bunch of ways in which you can improve managing shared state in Puck depending on the complexity of your editor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Optimize context usage -&lt;/strong&gt; If you notice performance issues or unnecessary re-renders, consider splitting your context into smaller, more focused contexts. This allows components to subscribe only to the parts of the state they need, minimizing re-renders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incorporate state libraries -&lt;/strong&gt; If you have multiple shared states and more complex logic, you could move beyond React Context and use your favorite state library. Whether it’s &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt;, &lt;a href="https://github.com/pmndrs/zustand" rel="noopener noreferrer"&gt;Zustand&lt;/a&gt;, or another library your project is already using, these can simplify managing complex state and improve rendering performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Leverage server-side state -&lt;/strong&gt; If your application relies heavily on data fetched from a server, consider using a library like &lt;a href="https://tanstack.com/query/v5" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt; or &lt;a href="https://swr.vercel.app/" rel="noopener noreferrer"&gt;SWR&lt;/a&gt;. These libraries manage caching, re-fetching, and synchronization for you, reducing the need for complex shared client-side state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Your Turn to Build Smarter with Puck 💡
&lt;/h3&gt;

&lt;p&gt;Taking shared state management in Puck to the next level unlocks a world of possibilities for building dynamic, reactive page builders. I’m excited to see the unique and powerful apps you’ll build using these strategies. &lt;/p&gt;

&lt;p&gt;So, if this article has inspired you to build something, you have a question about Puck or you want to contribute, here’s how you can get involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;🌟 Star &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=managing_application_state" rel="noopener noreferrer"&gt;Puck on GitHub&lt;/a&gt;&lt;/strong&gt; to show your support and inspire others to explore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🤝 Join our &lt;a href="https://discord.gg/V9mDAhuxyZ" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;&lt;/strong&gt; to connect, learn, and collaborate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📢 Follow us on &lt;a href="https://x.com/puckeditor" 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=managing_application_state" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;&lt;/strong&gt; for sneak peeks, updates, and tips.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📘 Explore the &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=managing_application_state" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;&lt;/strong&gt; for advanced techniques to supercharge your builds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The future of Puck—and no-code innovation—is in your hands. Start building today, and let’s redefine what’s possible together! 🙌&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>react</category>
    </item>
    <item>
      <title>Using CSS variables to create dynamic themes in Puck</title>
      <dc:creator>Fede Bonel Tozzi</dc:creator>
      <pubDate>Wed, 08 Jan 2025 13:53:24 +0000</pubDate>
      <link>https://dev.to/puckeditor/using-css-variables-to-create-dynamic-themes-in-puck-26lm</link>
      <guid>https://dev.to/puckeditor/using-css-variables-to-create-dynamic-themes-in-puck-26lm</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=dynamic_themes_css" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; is the open-source visual editor for React, empowering 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=dynamic_themes_css" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;! ⭐️&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;When integrating &lt;a href="https://puckeditor.com/?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;Puck&lt;/a&gt; into your page-building product, a common requirement is to allow your users to centrally manage the theme of the page without having to make adjustments to every component.&lt;/p&gt;

&lt;p&gt;For example: users need a way to manage font styles globally—even for components already added to the page. Here’s how that could look in action:&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%2F99sftn6wdz5bwsoy2g3y.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%2F99sftn6wdz5bwsoy2g3y.gif" alt="Intro" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since Puck is just a React component, there are lots of ways to solve this problem (as well as managing other, more complex state). For this article, I’ll focus on one of the simplest yet most powerful approaches—&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;CSS properties&lt;/a&gt;. Let’s dive in!&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%2F5i4graylg4k8ljepfs30.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%2F5i4graylg4k8ljepfs30.gif" alt="Lets get started" width="480" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;For this tutorial, I’ll assume you have a basic understanding of Puck and its functionality. If you’re new to Puck, don’t worry—you’re still welcome to follow along! However, I recommend checking the &lt;a href="https://puckeditor.com/docs/getting-started?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;getting started guide&lt;/a&gt; to familiarize yourself with the fundamentals.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Project Setup using Vite
&lt;/h2&gt;

&lt;p&gt;I’ll start by setting up a project using &lt;a href="https://vite.dev/guide/#scaffolding-your-first-vite-project" rel="noopener noreferrer"&gt;Vite's scaffolding&lt;/a&gt; script. You’re welcome to follow along or skip this section if you already have a project with Puck.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If you’re using Next.js or Remix you could also use one of the &lt;a href="https://github.com/puckeditor/puck/tree/main/recipes?tab=readme-ov-file#recipes?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;official Puck recipes&lt;/a&gt; for setting up your project.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Creating Project
&lt;/h3&gt;

&lt;p&gt;To get started, open your terminal and run the following commands to create a new Vite project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm create vite@latest puck-global-themes &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--template&lt;/span&gt; react
&lt;span class="nb"&gt;cd &lt;/span&gt;puck-global-themes
npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Next, install the Puck package:&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/core &lt;span class="nt"&gt;--save&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Deleting conflicting styles
&lt;/h3&gt;

&lt;p&gt;When you start a new project with Vite, it'll come with some default styles that will clash with Puck's. To fix this, delete &lt;code&gt;src/index.css&lt;/code&gt; and &lt;code&gt;src/App.css&lt;/code&gt;, then remove their imports from &lt;code&gt;src/main.jsx&lt;/code&gt; and &lt;code&gt;src/App.jsx&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="c1"&gt;// main.jsx&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;./index.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Remove this line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.jsx&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;./App.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Remove this line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Rendering the Puck editor
&lt;/h3&gt;

&lt;p&gt;With the basic project structure ready, head over to your &lt;code&gt;src/App.jsx&lt;/code&gt; file and replace its contents with the code below. This will set up Puck with a basic &lt;a href="https://puckeditor.com/docs/integrating-puck/component-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;config&lt;/a&gt; for dragging and dropping Heading and Paragraph components:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note that, for the sake of simplicity I'm using in-line styling in this example&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.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="c1"&gt;// The configs for each of your draggable components&lt;/span&gt;
&lt;span class="c1"&gt;// Ideally you would pull each of these to their own files&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headingConfig&lt;/span&gt; &lt;span class="o"&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="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="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;(&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paragraphConfig&lt;/span&gt; &lt;span class="o"&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;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a paragraph...&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;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;textarea&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;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;(&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;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&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;span class="c1"&gt;// The Puck configuration object&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;Heading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;headingConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;Paragraph&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;paragraphConfig&lt;/span&gt;&lt;span class="p"&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="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;children&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;main&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, Helvetica, 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="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;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;main&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;// Your editor component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&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="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="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="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, run the application in development mode, navigate to &lt;a href="http://localhost:5173" rel="noopener noreferrer"&gt;http://localhost:5173&lt;/a&gt;, and check that everything is working as expected:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&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%2Fnfho5j1an6rgp9udehvd.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%2Fnfho5j1an6rgp9udehvd.gif" alt="Basic Setup" width="960" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Awesome! Now that you have a foundation to build on, let's add theming for your users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding theming
&lt;/h2&gt;

&lt;p&gt;As I mentioned before, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;CSS custom properties&lt;/a&gt; are an excellent choice for adding interactive theming to your editor. Why is this a great approach? Because it’s lightweight, doesn’t require external dependencies, and is (for the most part) handled natively by the browser.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup
&lt;/h3&gt;

&lt;p&gt;To add dynamic theming with CSS properties you'll need to define your theme variables as properties at some parent of the component that needs them. This could be Puck's &lt;a href="https://puckeditor.com/docs/integrating-puck/root-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;&lt;code&gt;root&lt;/code&gt;&lt;/a&gt; component or any other parent component in your editor's hierarchy, it really doesn't matter as long as you define them and then read them where you need using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/var" rel="noopener noreferrer"&gt;&lt;code&gt;var&lt;/code&gt;&lt;/a&gt; CSS function.&lt;/p&gt;

&lt;p&gt;For this tutorial, I'll focus on using Puck's &lt;code&gt;root&lt;/code&gt; component so that users are able to set font size and color for headings and paragraphs in a single place at the top level of 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%2F42u7w7pfazxkjk5mll12.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%2F42u7w7pfazxkjk5mll12.gif" alt="Demo of the app we are going to build" width="960" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Define the Variables
&lt;/h3&gt;

&lt;p&gt;Start by defining an object to centralize all the CSS variables names. This will improve consistency and save you from the rabbit hole of debugging issues caused by mistyped property names in CSS.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Pro tip: If you’re using TypeScript you could use an enum instead of an object to get additional type safety&lt;/em&gt;&lt;/p&gt;


&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// App.jsx&lt;/span&gt;

&lt;span class="c1"&gt;// The names for each one of the CSS variables&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;fontColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--font-color&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;headingFontSize&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-font-size&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;paragraphFontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--paragraph-font-size&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;
  
  
  Step 2: Add the Variables to the Parent Component
&lt;/h3&gt;

&lt;p&gt;Next, update the &lt;a href="https://puckeditor.com/docs/integrating-puck/root-configuration?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;&lt;code&gt;root&lt;/code&gt; config&lt;/a&gt; to include the props and fields for the theme variables. This will allow users to update the custom properties directly from within the root fields of the editor. &lt;/p&gt;

&lt;p&gt;To do this, you’ll need to define the fields for the new root props and their default values:&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;// The Puck configuration object&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="c1"&gt;//... existing components&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="c1"&gt;// The default values for your CSS variables&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;fontColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#000000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;headingFontSize&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="na"&gt;paragraphFontSize&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="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// The fields for your CSS variables&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;fontColor&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;Font color (hex)&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;span class="na"&gt;headingFontSize&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;Heading font size (px)&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;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;paragraphFontSize&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;Paragraph font size (px)&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;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="c1"&gt;//... the root's render function&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 that, in the root’s render function, read the custom properties from the props and pass them to the root element as CSS variables.&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;// The Puck configuration object&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="c1"&gt;//... existing components&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="c1"&gt;//... the root fields 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;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fontColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headingFontSize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;paragraphFontSize&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;main&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, Helvetica, 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="c1"&gt;// The new variables using our names object and the new props&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontColor&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;fontColor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headingFontSize&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="nx"&gt;headingFontSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paragraphFontSize&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="nx"&gt;paragraphFontSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;px`&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="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;main&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;If you now go to the editor, you should see the new fields at the root level for each one of the props. There is no visual feedback yet, but if you inspect the styles of the editor’s root, you will see the variables being set with every change you make to the fields.&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%2F6v3puhwqslbi30sjyhxh.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%2F6v3puhwqslbi30sjyhxh.gif" alt="Fields with CSS variables no visual feedback" width="960" height="540"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Access Shared Variables in Child Components
&lt;/h3&gt;

&lt;p&gt;Finally, access the theme variables in the components you need by using the &lt;code&gt;var&lt;/code&gt; CSS function in your styles.&lt;/p&gt;

&lt;p&gt;In our example, you’ll add them in the &lt;code&gt;headingConfig&lt;/code&gt; and the &lt;code&gt;paragraphConfig&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;headingConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... The heading fields 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="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="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="c1"&gt;// Here we are reading the shared styles for the headers&lt;/span&gt;
            &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`var(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontColor&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;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`var(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headingFontSize&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="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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paragraphConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//... The paragraph fields 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;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;(&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="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="c1"&gt;// Here we are reading the shared styles for the paragraphs&lt;/span&gt;
            &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`var(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fontColor&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;fontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`var(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;SharedCSSVars&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;paragraphFontSize&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="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;content&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;If you return to the editor, drag and drop some headings and paragraphs, and then modify the fields at the root level, you’ll notice that all the headings and paragraphs update to reflect those changes:&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%2Ffo3afbxtq2wt14o6fuo1.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%2Ffo3afbxtq2wt14o6fuo1.gif" alt="Finished app" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🥳 And that’s it! Your users are now able to define a theme seamlessly applied across different components in their pages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros &amp;amp; Cons of using CSS properties
&lt;/h3&gt;

&lt;p&gt;✅ Pros:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple and intuitive setup&lt;/li&gt;
&lt;li&gt;Built-in cascading and override functionality in page hierarchy, allowing for easy management of shared styles and the ability to customize individual components&lt;/li&gt;
&lt;li&gt;Zero external dependencies and lightweight footprint—CSS custom properties are natively supported by browsers&lt;/li&gt;
&lt;li&gt;Less boilerplate and maintenance complexity than other options&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;❌ Cons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only suitable for simple styling—can't handle complex data or logic&lt;/li&gt;
&lt;li&gt;Debugging can be difficult when nesting multiple levels of CSS property overrides&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Taking it further
&lt;/h2&gt;

&lt;p&gt;Depending on your specific use case, there are plenty of ways to take your editor theming even further:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use predefined themes&lt;/strong&gt; - In some applications, you may need to provide default theme options—like Dark, Light, or Minimal. For this, you could use a &lt;a href="https://puckeditor.com/docs/api-reference/fields/select?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;select field&lt;/a&gt; with multiple theme objects. 
These objects can group all the CSS properties as a unit, allowing users to easily swap between different themes for their pages without having to fill in single fields.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrate styling libraries&lt;/strong&gt; – For larger editors, it may be better to use a styling library to leverage pre-built styles, streamline your definitions, and reduce boilerplate. Libraries like &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind&lt;/a&gt; or &lt;a href="https://emotion.sh/docs/introduction" rel="noopener noreferrer"&gt;Emotion&lt;/a&gt; can easily integrate with Puck, giving you flexibility and efficiency while maintaining a polished look. In fact, Puck even offers an &lt;a href="https://github.com/puckeditor/puck/tree/main/packages/plugin-emotion-cache?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;Emotion plugin&lt;/a&gt; that simplifies the integration process for you!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add component-level theme overrides&lt;/strong&gt; – Sometimes users may need to customize individual components in their pages so that they stand out from the rest of the theme. Since we’re using CSS properties, this can easily be achieved by overriding the parent-level theme variables at the component level. For example, you could add an optional font color field inside your &lt;code&gt;Heading&lt;/code&gt; component that, if provided, would redefine the CSS property specifically for that &lt;code&gt;h1&lt;/code&gt; element.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Stay Connected and Keep Building 🚀
&lt;/h2&gt;

&lt;p&gt;I hope this tutorial has empowered you to leverage CSS variables for dynamic theming in Puck-powered page builders. The open-source developer community is at the heart of Puck's evolution, and I can’t wait to see the innovative apps you create!&lt;/p&gt;

&lt;p&gt;We’re dedicated to pushing Puck’s capabilities even further, with a constant stream of new features, including a revolutionary drag-and-drop engine for complex grids and layouts launching  imminently, and a suite of new plugins on the horizon.&lt;/p&gt;

&lt;p&gt;So, if Puck has inspired you or if you’d like to stay up to date with the latest features, here’s how you can get involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⭐ Star us on &lt;a href="https://github.com/puckeditor/puck?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/strong&gt; to support the project and inspire others to explore its potential.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;💬 Join our &lt;a href="https://discord.gg/V9mDAhuxyZ" rel="noopener noreferrer"&gt;Discord community&lt;/a&gt;&lt;/strong&gt; to share your projects, ask questions, and collaborate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📢 Follow us on &lt;a href="https://x.com/puckeditor" 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=dynamic_themes_css" rel="noopener noreferrer"&gt;Bluesky&lt;/a&gt;&lt;/strong&gt; for the latest updates, sneak peeks, and feature announcements.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📚 Dive deeper into the &lt;a href="https://puckeditor.com/docs?utm_source=dev&amp;amp;utm_medium=article&amp;amp;utm_campaign=dynamic_themes_css" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;&lt;/strong&gt; for advanced techniques and insights that can take your no-code workflows to the next level.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>tutorial</category>
      <category>javascript</category>
      <category>react</category>
    </item>
  </channel>
</rss>
