How to Add Dynamic Editing Regions to Custom Components

Need to create a custom component with an area where users can drag in additional blocks? This post will guide you through creating one, building off of the <DynamicColumns> component in our react-design-system starter and using <BuilderBlocks> for the editing region.

Read more about the React design system example here

Open the react-design-system example in your code editor and navigate to src/components/. Let’s call our new component CustomColumns and create a folder for it. Add a file called CustomColumns.jsx then copy and paste the code from DynamicColumns.jsx . To make the <CardContent> area editable, replace the text component with <BuilderBlocks/> :

import { Image, BuilderBlocks } from '@builder.io/react';

...

    <CardContent>
        <BuilderBlocks
            key={index}
            child
            parentElementId={props.builderBlock && props.builderBlock.id}
            blocks={col.blocks}
            dataPath={`component.options.columns.${index}.blocks`}
        />
    </CardContent>

...

This is similar to how we implement fully customizable columns in Builder’s built-in Columns component.

Next we need to register the component. Create another file in CustomColumns/ called CustomColumns.builder.js and copy and paste the code from DynamicColumns.builder.js . Notice the list input type here which is what allows users to add more columns identical to the default column. Replace the default text inputs with blocks to make the area under the image dynamically editable.

import { Builder } from '@builder.io/react';
import { CustomColumns } from './CustomColumns';

Builder.registerComponent(CustomColumns, {
  name: 'Custom Columns',
  description: 'Example of a custom column with editing regions',
  inputs: [
    {
      name: 'columns',
      type: 'array',
      defaultValue: [
        {
          image: 'https://cdn.builder.io/api/v1/image/assets%2Fpwgjf0RoYWbdnJSbpBAjXNRMe9F2%2Ffb27a7c790324294af8be1c35fe30f4d',
          blocks: [],
        },
        {
          image: 'https://cdn.builder.io/api/v1/image/assets%2Fpwgjf0RoYWbdnJSbpBAjXNRMe9F2%2Ffb27a7c790324294af8be1c35fe30f4d',
          blocks: [],
        },
      ],
      subFields: [
        {
          name: 'image',
          type: 'file',
          allowedFileTypes: ['jpeg', 'jpg', 'png', 'svg'],
          required: true,
          defaultValue:
            'https://cdn.builder.io/api/v1/image/assets%2Fpwgjf0RoYWbdnJSbpBAjXNRMe9F2%2Ffb27a7c790324294af8be1c35fe30f4d',
        },
				{
          name: 'blocks',
          type: 'blocks',
          hideFromUI: true,
          helperText: 'This is an editable region where you can drag and drop blocks.',
        },
      ],
    },
  ],
});

Now we can test our new Custom Columns component in the visual editor and see that the area under the image is editable!

Additionally, we can add a default component to the blocks as a placeholder:

...
{
    image:
        'https://cdn.builder.io/api/v1/image/assets%2Fpwgjf0RoYWbdnJSbpBAjXNRMe9F2%2Ffb27a7c790324294af8be1c35fe30f4d',
    blocks: [
        {
            '@type': '@builder.io/sdk:Element',
            component: {
                name: 'Text',
                options: {
                    text: 'Enter some text...',
                },
            },
        },
    ],
},
...

custom-cols (1)

View the full code for the Custom Columns component here:

https://github.com/BuilderIO/builder/tree/main/examples/react-design-system/src/components/CustomColumns

1 Like

Just to add on - a simple example that just needs one set of children:

import { BuilderBlocks, Builder } from '@builder.io/react';

function CustomSection(props) {
  return (
    <div>
      <BuilderBlocks blocks={props.children} parentElementId={props.builderBlock.id} dataPath="children" />
    </div>
  );
}

Builder.registerComponent(CustomSection, {
  name: 'My Section',
  inputs: [/* optional, any other inputs */],
  // Optional, if you want default children. If none are provided or if children are empty the
  // "+ add block" button shows
  defaultChildren: [
    {
      '@type': '@builder.io/sdk:Element',
      component: {
        name: 'Text',
        options: {
          text: 'I am a default child!',
        },
      },
    }
  ]
});

Hi Steve, when I use your simple example and try to drop or add a new block using the " + Add block" button these blocks are being added after the Section Block and not inside.

Hey @luigiveoworld - can you share a reproducible code example? Will help us be able to point out any possible issues or replicate on our end to debug

I directly copy and pasted this code block, after that I used it in Builder visual editor and using the “Add block” button is adding the new block after this block and not inside of it.:

import { BuilderBlocks, Builder } from '@builder.io/react';

function CustomSection(props) {
  return (
    <div>
      <BuilderBlocks blocks={props.children} parentElementId={props.builderBlock.id} dataPath="children" />
    </div>
  );
}

Builder.registerComponent(CustomSection, {
  name: 'My Section',
  inputs: [],
  defaultChildren: []
});

hey @luigiveoworld - thanks for sending. On second look I may have gotten a couple important nuances wrong on my first suggestion, I think what we want is this - can you give it a try?

import { BuilderBlocks, Builder } from '@builder.io/react';

function CustomSection(props) {
  return (
    <div>
      <BuilderBlocks blocks={props.builderBlock.children} parentElementId={props.builderBlock.id} dataPath="this.children" />
    </div>
  );
}

Builder.registerComponent(CustomSection, {
  name: 'My Section',
  canHaveChildren: true
});

It’s based on this example builder/Mutation.tsx at main · BuilderIO/builder · GitHub

Thank you @steve I can confirm that it works as expected now.