Watch Webinar: Figma - Design to Code in 80% Less Time

Announcing Visual Copilot - Figma to production in half the time

Builder.io logo
Talk to Us
Platform
Developers
Talk to Us

Blog

Home

Resources

Blog

Forum

Github

Login

Signup

×

Visual CMS

Drag-and-drop visual editor and headless CMS for any tech stack

Theme Studio for Shopify

Build and optimize your Shopify-hosted storefront, no coding required

Resources

Blog

Get StartedLogin

When you want your custom component to accept other blocks, configure your component with children. The following video demonstrates a custom tabs component that accepts Builder blocks as content. Each tab receives a unique block that the user drags and drops in.

To get the most our of this tutorial, you should have the following:

For more details on child-related options when working with child components, visit the canHaveChildren, childRequirements, and defaultChildren sections of the registerComponent() documentation.

Tip: Builder's Gen 2 SDKs don't require withChildren(). For more information on the SDKs, visit SDK Comparision.

First, configure the component to receive and render children, based on your framework:

Gen 2 SDK by FrameworkPrepare code with

React

Call props.children wherever the children should be rendered — just like in the React Gen 1 SDK.

Vue and Svelte

Add <slot></slot> wherever you want your children to be. For more detail, visit the Vue document on Slots and the Svelte special elements entry on <slot>.

Qwik

Add Qwik's <Slot/> component. Import { Slot } from '@builder.io/qwik';

Solid.js

Use the children helper (See SolidJS docs

Then, make sure to add the children helper canHaveChildren: true component option when registering.

For Gen 1 or Gen 2, now your component should show up in the Visual Editor with no further configuration in the code.

The following video shows the Hero block in the Custom Components section of the Insert tab. When the Hero block is on the page in the Visual Editor, you can drag and drop more blocks and drop them into the Hero block, along with the other children defined in code.

To use a registered component with children, do the following:

  1. If you're still developing your component, change the Preview URL to localhost with the port you're using for your local app and the page name. In this example, the URL is http://localhost:3000/cc-children.
  2. Drag and drop your custom component into the work area.
  3. Drag other blocks onto your custom component.

In the following video, after the user drops a Columns block into the custom component, they open the Layers tab to show the Columns block nested within the Hero block.

For React, use this technique if you want to manually specify details about your custom component's children. This section shows you how to to create a a Tabs component that renders multiple sets of children. Refer to the corresponding comments in the code for each step.

  1. Import React, useState, BuilderBlocks, and Builder along with your other JavaScript imports.
  2. Create your component. This example creates a Tabs component where each tab has a label as well as content that displays when that tab is active.
  3. Configure BuilderBlocks so that Builder knows what makes up your component.
  4. Register your component using registerComponent(). Specify the required values of name and type as well as subFields. The subFields are label and content and they get their values from defaultValue. When you create a new tab in the Visual Editor, the default label is New Tab 1 while the content is empty, because of the empty array.
// 1. Add imports 
import React, { useState } from 'react';
import { BuilderBlocks, Builder } from '@builder.io/react';


// 2. Create your component. 
// This is a tabs component with some basic styles. When the user clicks 
// on a tab, that content becomes active.
function Tabs(props) {
  const [activeTab, setActiveTab] = useState(0);
  return (
    <>
      <div
        style={{
          display: 'flex',
          overflow: 'auto',
        }}
      >
        {props.tabs?.map((item, index) => (
          <span
            key={index}
            style={{
              padding: 20,
              color: activeTab === index ? 'blue' : '#000',
            }}
            onClick={() => {
              setActiveTab(index);
            }}
          >
            {item.label}
          </span>
        ))}
      </div>
      {props.tabs?.length && (
        <BuilderBlocks
          parentElementId={props.builderBlock.id}
          dataPath={`component.options.tabs.${activeTab}.content`}
          blocks={props.tabs[activeTab].content}
        />
      )}
    </>
  );
}
// 3. Configure BuilderBlocks. The above section with BuilderBlocks 
// tells Builder what to expect: 
// the parent id, the path to the child component, 
// and what the blocks should be made of. 
// Here, the blocks are made of the content of the active tab 

// 4. Register your component. This component is called Tabs, is of type 
// list and contains two subFields: a label and the content.
// As a best practice, provide a defaultValue. The default label is 
// "Tab 1" and the content is an empty array.

Builder.registerComponent(Tabs, {
  name: 'Tabs',
  inputs: [
    {
      name: 'tabs',
      type: 'list',
      subFields: [
        {
          name: 'label',
          type: 'text',
          defaultValue: 'New tab',
        },
        {
          name: 'content',
          type: 'uiBlocks',
          defaultValue: [],
        },
      ],
      defaultValue: [
        {
          label: 'Tab 1',
          content: [],
        },
      ],
    },
  ],
});

This section explains how to configure custom components to accept children with the React Gen 1 and Gen 2 SDKs. By implementing editable regions within custom components, users can dynamically add and customize content within their Builder projects.

Register inputs for each of your editable sections of type blocks:

// Register the component and its inputs
Builder.registerComponent(HeroWithBuilderChildren, {
  name: 'Hero',
  inputs: [
    // Input for section A editable region
    {
      name: 'sectionA',
      type: 'blocks', // Specify type of blocks
      hideFromUI: true,
      helperText: 'This is an editable region.',
      defaultValue: [
        {
          '@type': '@builder.io/sdk:Element',
            component: {
              name: 'Text',
              options: {
                text: 'Section A Editable in Builder...',
            },
          },
          responsiveStyles: {
            large: {
              // Styles for the editable section
            },
          },
        },
      ],
    },
    // Input for section B editable region
    {
      name: 'sectionB',
      type: 'blocks',
      hideFromUI: true,
      helperText: 'This is an editable region.',
      defaultValue: [
        {
          '@type': '@builder.io/sdk:Element',
          component: {
            name: 'Text',
            options: {
              text: 'Section B Editable in Builder...',
            },
          },
          responsiveStyles: {
            large: {
              // Styles for the editable section
            },
          },
        },
      ],
    },
  ],
});

Use builder-blocks-outlet component to render those blocks within your component template:

@Component({
  selector: 'my-custom-section',
  template: `
    <h2>Section A</h2>
    <!-- Render section A editable region using 
    builder-blocks-outlet component -->
    <builder-blocks-outlet
      [blocks]="sectionA"
      [builderState]="builderState"
      [builderBlock]="builderBlock"
      dataPath="component.options.sectionA"
    ></builder-blocks-outlet>
    <h2>Section B</h2>
    <!-- Render section B editable region using 
    builder-blocks-outlet component -->
    <builder-blocks-outlet
      [blocks]="sectionB"
      [builderState]="builderState"
      [builderBlock]="builderBlock"
      dataPath="component.options.sectionB"
    ></builder-blocks-outlet>
  `,
})
export class MyCustomSection implements OnChanges {
  @Input()
  sectionA = null;

  @Input()
  sectionB = null;

  @Input()
  builderState = null;

  @Input()
  builderBlock = null;
}

When working with Gen 2 SDKs, such as those for Vue, Svelte, Qwik, and React Gen 2, you can add editable regions to your custom components using the <Blocks> component. This way, users can dynamically add and customize content within the Builder Visual Editor.

Here's an example of how to use the <Blocks> component to render child blocks within a custom Tabs component in a Vue application:

<template>
  <div>
    <span class="builder-tabs-wrap">
      <span
        v-for="(item, index) in tabs"
        :key="index"
        class="builder-tab-wrap"
        :class="{ 'builder-tab-active': activeTab === index }"
        @click="handleClick(index)"
      >
        <Blocks
          :parent="props.builderBlock.id"
          :path="`component.options.tabs.${index}.label`"
          :blocks="item.label"
        />
      </span>
    </span>
    <div v-if="activeTabContent">
      <Blocks
        :parent="props.builderBlock.id"
        :path="`component.options.tabs.${activeTab}.content`"
        :blocks="activeTabContent"
      />
    </div>
  </div>
</template>

<script setup>
import { ref, computed } from 'vue';
import { Blocks } from '@builder.io/sdk-vue';

const props = defineProps({
  tabs: Array,
  builderBlock: Object,
  // other props...
});

const activeTab = ref(props.defaultActiveTab ? props.defaultActiveTab - 1 : 0);
const activeTabContent = computed(() => {
  return props.tabs && props.tabs.length > activeTab.value
    ? props.tabs[activeTab.value].content
    : undefined;
});

function handleClick(index) {
  // handle tab click logic
}
</script>

The important parts to note are:

  1. Import the Blocks component from @builder.io/sdk-vue.
  2. Use the <Blocks> component to render the child blocks for each tab label and the active tab content.
  3. Pass the required props to the component:
  • parent: Specify the ID of the parent Builder block using props.builderBlock.id.
  • path: Define the path to the child blocks within the component's options using a template literal.
  • blocks: Pass the actual child blocks to be rendered, either item.label for tab labels or activeTabContent for the active tab content.

The <Blocks> component in Gen 2 SDKs replaces the component used in Gen 1. The prop names have changed slightly:

PropertyGen 1Gen 2

Parent block ID

parentElementId

parent

Path to child blocks

dataPath

path

Child blocks

blocks

blocks

To use a custom component with multiple sets of children in the Visual Editor, take the following steps:

  1. In the Insert tab, expand the Custom Components section.
  2. Drag and drop your component onto the work area.
  3. With your component selected, click the Edit button and add sets of children if needed.
  4. Drag other blocks, for example Text or Image blocks, into the Add Block region of your component.

To customize your components even further, you can leverage Builder's Input Types.

For more examples of custom components with multiple sets of children, check out:

Was this article helpful?

Product

Visual CMS

Theme Studio for Shopify

Sign up

Login

Featured Integrations

React

Angular

Next.js

Gatsby

Get In Touch

Chat With Us

Twitter

Linkedin

Careers

© 2020 Builder.io, Inc.

Security

Privacy Policy

Terms of Service

Newsletter

Get the latest from Builder.io

By submitting, you agree to our Privacy Policy