WebReinvent Internal Docs
Builderio Nuxt 3

Custom Components

Introduction

You can expand on Builder's selection of built-in blocks by registering components from your codebase with Builder. Then, teammates can drag and drop your components within Builder's Visual Editor just like any other block. You can use components you code yourself or third-party components with Builder. For details visit the reference link

Registering Components

To use your custom components in the Builder Visual Editor, you need to take a couple of minimal steps in your codebase.

Prerequisites

You should have the following:

  1. Integrated Pages or Sections
  2. The Builder SDK installed for your framework

Setting up your code

To use your custom component with Builder, you need to let Builder know about it by registering your component. Registering your component makes the component appear in the Visual Editor as a custom block.

  1. Defining your component
    Define a HelloWorld.vue component:

<template>
<div>Hello {{ text }}!</div>
</template>

<script setup>
const props = defineProps({
text: {
  type: String,
  default: 'world'
}
})
</script>
  1. Registering your component
    Create a file init-builder.ts in root directory and then create an array of your custom components inside it. Here, the array contains the HelloWorld component with two options:
    i) a name
    ii) a list of possible inputs, in this case a text prop
// file: init-builder.ts
// import your custom component
import HelloWorldComponent from './HelloWorld.vue'

export const REGISTERED_COMPONENTS = [
{
  component: HelloWorldComponent,
  name: 'Hello World',
  inputs: [
    {
      name: 'text',
      type: 'string',
      defaultValue: 'World',
    },
  ],
},
]
  1. Provide this list of REGISTERED_COMPONENTS to the Content component (previously, RenderContent) from Builder:
  • import the REGISTERED_COMPONENTS in your page
import {REGISTERED_COMPONENTS} from "../init-builder";
  • Create getRegisteredComponents function
const getRegisteredComponents = () => {
  return REGISTERED_COMPONENTS;
}
  • Pass getRegisteredComponents to customComponents props of Content component (previously, RenderContent).

<template>
  <Content
    model="page"
    :content="content"
    :apiKey="apiKey"
    :customComponents="getRegisteredComponents()"
  />
</template>
  1. Using your component in Builder's Visual Editor

Now your component should show up in the Visual Editor with no further configuration in the code.

Setting component options

When you register a component, you must provide a name. Optionally, you can also specify:

  1. component default styling
  2. whether Builder wraps the component in a <div>
  3. whether the component displays in the Insert tab
  4. Reference link

The following list describes each of the additional component options:

  • defaultStyles :
    Type: object
    Use for showing an example value in the input form when creating a new instance of this component, to users understand its purpose. Users in the Visual Editor can edit these styles.
export const REGISTERED_COMPONENTS = [
  {
    ...,
    defaultStyles: {
      textAlign: 'center',
      fontSize: "20px",
    },
    ...
  },
]
  • hideFromInsertMenu :
    Type: Boolean
    Hide your component from the Insert tab within Builder's Visual Editor. Use this feature for deprecating a component when you want to reduce its use, but still need the component registered with Builder so that older content that uses the component continues to function properly. For more information on versioning, refer to Versioning Custom Components.
  hideFromInsertMenu: true
  • noWrap :
    Type: Boolean
    By default, Builder wraps your components in a <div>.

You can opt out of this wrapping by using the noWrap option. For a full code example, see Builder's built-in form input component.

The following code snippet features a number of options in the context of registerComponent():

export const REGISTERED_COMPONENTS = [
  {
    // Begin component options
    // Name your component (required)
    component: HelloWorld,
    name: "Hello World",

    // Optional: provide CSS in an object to defaultStyles
    defaultStyles: {
      textAlign: 'center',
      fontSize: "20px",
    },

    // Optional: specify whether your component is wrapped in a <div>
    noWrap: true,

    // Optional: specify if your component shows in the Insert tab
    hideFromInsertMenu: false,
    // End component options

    // Begin inputs:  
    inputs: [{...}],
  },
]

For more detail on all available registerComponent() options, read registerComponent Options.

Programmatically setting bindings

You can programmatically set bindings on custom components so you can apply multiple bindings simultaneously. This is helpful when, for example, applying styles from an existing design system. The snippet below registers a custom component with default bindings for the product title and a theme background color:

export const REGISTERED_COMPONENTS = [
  {
    component: ProductCard,
    name: "Product Card",
    defaults: {
      bindings: {
        'component.options.products': 'state.products',
      }
    }
  }
]

Making a component available only in specified models

  • Separate products related components in the init-builder.ts file
export const PRODUCT_COMPONENTS = []
  • After separating the components register products related components while rendering the products model

<template>
  <div v-if="content || isPreviewing()">
    <Content
      model="products"
      :content="content"
      :apiKey="apiKey"
      :customComponents="getRegisteredComponents()"
    />
  </div>
  <div v-else>Content not Found</div>
</template>
<script setup>
const route = useRoute();
import {PRODUCT_COMPONENTS} from "../../init-builder";
import {fetchOneEntry} from '@builder.io/sdk-vue';

// TODO: enter your public API key
const BUILDER_PUBLIC_API_KEY = 'YOUR_API_KEY';

const apiKey = ref(BUILDER_PUBLIC_API_KEY);

const content = await fetchOneEntry({
  model: 'products',
  apiKey: apiKey.value,
  userAttributes: {
    urlPath: '/products/' + route.params.product,
  },
})

const getRegisteredComponents = () => {
  return PRODUCT_COMPONENTS;
}
</script>

Using Custom Components in the Visual Editor

After you've set up your code base to use your custom components within Builder, you can test it out and organize your components in the Visual Editor.

Prerequisites

You should have already done the following::

  1. Configured your codebase to use your custom component with Builder
  2. Set up editing and previewing directly on your site

Finding your custom components in the Visual Editor

When you've successfully registered your custom components with Builder, you'll find your components in the Insert tab in the Custom Components section.

The following image shows the Custom Components section expanded within the Visual Editor for this page. In this example, within the Custom Components section, are eleven custom components that Builder developers have registered. The contents of your Custom Components section depends on which components you've registered, so yours might look different than this screenshot. Custom components

Organizing your components in custom sections

You can create a section in the Visual Editor Insert tab just for your custom components. The following image shows a custom section in the Insert Tab called Our Components, which contains four custom components; a hero, double columns, triple columns, and dynamic columns.

You can create a section in the Visual Editor Insert tab just for your custom components. The following image shows a custom section in the Insert Tab called Our Components, which contains four custom components; a hero, double columns, triple columns, and dynamic columns. Custom sections

To add your custom section to the Insert tab, specify insertMenu as an argument in Builder.register(). Give your section a name and list the items you'd like include in that section as in the following example.

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

Builder.register('insertMenu', {
name: 'Our components',
items: [
  {name: 'Hero'},
  {name: 'Double Columns'},
  {name: 'Triple Columns'},
  {name: 'Dynamic Columns'},
],
})

For more detail on registering Insert tab menus, refer to Interface: Insert Menu Config readme on GitHub and a complete builder-settings.js config .

Developing and testing locally

Before your team starts using your custom component, you can test it out in the Visual Editor. When developing locally, update the preview URL in the top right corner of the preview from your production URL to your local development URL.

The following video shows you how to set your preview URL by clicking the URL setting and typing in your local URL for development. This URL is temporary and does not persist when you leave the page, which makes it ideal for testing out your new component.

Using Child Blocks in Custom Components

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 users drags and drops in.

Prerequisites

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

  1. An app you've integrated with Builder
  2. Familiarity with using custom components in Builder

Adding children to custom components

  1. Create a CustomComponent

<template>
<div>
  <h1> {{ title }} </h1>
  <slot></slot>
</div>
</template>

<script setup>
const props = defineProps({
title: {
  type: String,
  default: 'Custom Component'
}
})
</script>
  1. Register CustomComponent to Builder.io by adding the code inside init-builder.ts
import CustomComponent from "./components/molecules/CustomComponent.vue";

export const REGISTERED_COMPONENTS = [
{
  name: 'Custom Component',
  component: CustomComponent,
  canHaveChildren: true,
  inputs: [
    {
      name: 'title',
      type: 'string',
      defaultValue: 'This is the Title'
    }
  ]
}
]
  1. Now you can use children components inside your CustomComponent.

Using your component with children in Builder's Visual Editor

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.

Using your custom component in the Visual Editor

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.

Overriding Built-in Components

In addition to using your own unique custom components with Builder, you can tailor built-in components overriding them with your own component. You can extend, limit. or otherwise tweak built-in components so that your team has exactly what they need to build projects.

Prerequisites

To override Builder's built-in components, you should already be familiar with the following:

  1. Registering Custom Components
  2. Using Custom Components in the Visual Editor

Overriding Builder's built-in components

You can override Builder-provided components by registering a custom component with the same name. By overriding a component, you can add extra features to Builder's built-in blocks and change their behavior.

To override Builder's built-in components, take the following steps:

  1. Register a component with the same name as the component in Builder that you'd like to override.
  2. Set the override property to true
  3. Add an inputs array with each input you want your component to have.
const REGISTERED_COMPONENTS = [
{
  // Provide the name of the built-in Builder component that you want to override.
  component: Image,
  name: 'Image',
  // Signify that this is an override
  override: true,
  inputs: [
    // Add your own input fields.
    {name: 'myInput', ...},
    {name: 'image', type: 'file', ...},
  ],
  ...
}
]

Input Types in Builder

When you create custom components to use in Builder, there are two required inputs and a number of optional inputs to help you further customize your components.

This document covers these inputs types in detail.

This document covers the following:

Prerequisites

you should be familiar with Integrating Your Custom Components with Builder.

Required inputs

When you register a component with Builder, you must include the name and type inputs as in the following table:

NameRequireDescription
nameYesA unique name for this input that should match the equivalent prop name on your React component.
typeYesTypes correlate to what editing UI is appropriate to edit this field. Common types include:
'string'
'number'
'boolean'
'longText' // String type but with a multiline text field editor
'richText' // Displays a rich text editor and provides the value as html
'file' // Uploads a file and provides the value as a url string
'color'
'date'
'email'
'object'
'list'
'reference' // displays a content entry picker to reference

Optional inputs for further customization

You can use additional inputs to further customize your components in Builder. The following table contains Builder's optional inputs.

NameRequireDescription
advancedBooleanSet to true to put this component under the Show More section of the **
Options** tab. Useful for things that are advanced or rarely used and don't need to be prominent.
allowedFileTypesarrayFor the file input type, specify what types of files users can upload. This is an array that takes content-type files such as:
allowedFileTypes: ['jpeg', 'png', 'mp4', 'gif', 'pdf', 'svg']
defaultValueanyUse for showing an example value in the input form when creating a new instance of this component, to users understand its purpose.
enumarrayFor any text-based field type, you can specify a set of options that the field can use.
enum: ['option 1', 'option 2']
friendlyNamestringThe name the Visual Editor displays for the input.
friendlyName: 'Open link in new tab',
helperTextstringProvide text to help the end user know how to fill in this input. Displays below the input.
helperText: 'Some helpful description about how to use this input'

for more info visit to this link

Versioning Custom Components

When you rely on custom components, sometimes you want to publish new versions and phase out previous versions. This document shows you how to version your custom components so that your team is using the most up-to-date iteration.

The following video shows two versions of a component in the Visual Editor and how, with minimal code edits you can deprecate one version in favor of another.

Prerequisites

To version custom components, you should already be familiar with the following:

  1. Registering Custom Components
  2. Using Custom Components in the Visual Editor

Making new versions backward-compatible

Always make new versions of custom components backward-compatible so that any content using a prior version of the component continues to work.

Builder uses the current version of the custom component when rendering your content because that is the version that exists on your site–even if the content was originally created with a prior version of the component.

For example, if the new version of the custom component has an additional input, make sure to handle the case where that input is undefined, since old content does not have a value for that input.

Importantly, even if you are deprecating a previous version of a component, keep that previous version registered with Builder so that any instances still in use continue to work properly.

An example of backward compatibility

The following code is a minimal first version of HelloWorldComponent with a single input called text.

// Original version that has a input called "text"
import HelloWorldComponent from './HelloWorld.vue'

export const REGISTERED_COMPONENTS = [
{
  component: HelloWorldComponent,
  name: 'Hello World',
  inputs: [
    {
      name: 'text', // <-- one input in this original version of the component
      type: 'string'
    },
  ],
},
]

Naming versioned custom components

When naming your custom components, including when introducing new versions, be sure to give each component a unique name. You can use completely different names such as Our Header and Our New Header, or you can use a naming technique that uses version numbers in your code while maintaining the same name, unchanged, in the Visual Editor.

To manage names and versions of custom components, we recommend the following workflow:

  1. Optionally hide the previous version of the component to prevent its use.
  2. Give the component(s) a version number.

To hide and assign a version number to your components, follow the steps for your framework: To keep the same name in the Visual Editor while using version numbers in your code, you can prepend a version indicator such as number followed by a colon, :, in the name of the component in REGISTERED_COMPONENTS.

The following example demonstrates removing the first version of a HeadingComponent from the Visual Editor and replacing it with a second version by the same name.

  1. Set hideFromInsertMenu to true. In this way, the first version of this component doesn't show up in the ** Insert** tab.
// Original version that has a input called "text"
import HeadingComponent from './Heading.vue'

export const REGISTERED_COMPONENTS = [
{
  component: HeadingComponent,
  name: 'Heading', // <-- the name your users see in the Visual Editor
  hideFromInsertMenu: true, // <-- don't show in Insert tab
  inputs: [
    {
      name: 'text', // <-- one input in this original version of the component
      type: 'string',
      defaultValue: 'I am version 1'
    },
  ],
},
]
  1. Register the second version of the component. Keep the original version registered so any content that relies on it continues working as expected.
  2. In the second component name, prepend the name that shows up in the Visual Editor with a version followed by a colon. In this example, the name is v2: Heading.
export const REGISTERED_COMPONENTS = [
{
  component: HeadingComponent,
  name: 'v2: Heading', // <-- the name your users see in the Visual Editor
  inputs: [
    {
      name: 'text',
      type: 'string',
      defaultValue: 'I\'m version 2'
    },
  ],
},
]

Builder omits anything that you put before the colon, so you have flexibility in how you indicate versions.

If you have content that uses version one, that content still renders as long as you keep that original component registered; however, with hideFromInsertMenu set to true, the first version doesn't show up in the Visual Editor. Instead, the second version shows up, making sure users use the new version.

The full example is below:

// Original version that has a input called "text"
import HeadingComponent from './Heading.vue'

export const REGISTERED_COMPONENTS = [
{
  component: HeadingComponent,
  name: 'Heading',
  hideFromInsertMenu: true,
  inputs: [
    {
      name: 'text',
      type: 'string',
      defaultValue: 'I\'m version 1'
    },
  ],
},
{
  component: HeadingComponent,
  name: 'v2: Heading',
  inputs: [
    {
      name: 'text',
      type: 'string',
      defaultValue: 'I\'m version 2'
    },
  ],
},
]

The following is a screenshot of a page with both versions of the HeadingComponent. The first version was added before it was deprecated by setting hideFromInsertMenu to true. and has the default value of "I'm version 1".

The second version replaced the first version with the same name in the Visual Editor, Heading, but the default value now says "I'm version 2".

The first version remains functional because it is still registered with Builder.

Custom components versioning

When backward-compatibility isn't possible

If the updates you want to make preclude backward compatibility, take the following steps:

  1. Create an entirely new component registered with a new name.
  2. Keep the previous version of the component registered with Builder so that existing content on your site using that component continues to function properly.
  3. Hide the old version of the component from the Builder editor so your team doesn't use the old version with newer content. To hide a component, use the hideFromInsertMenu option when registering the component, as in the following example code:
import HeadingComponent from './Heading.vue'

export const REGISTERED_COMPONENTS = [
  // Hide the original version
  {
    component: HeadingComponent,
    name: 'Heading',
    hideFromInsertMenu: true,
    inputs: [
      {
        name: 'text',
        type: 'string',
        defaultValue: 'I\'m version 1'
      },
    ],
  },
  // New version to use instead
  {
    component: HeadingComponent,
    name: 'v2: Heading',
    inputs: [
      {
        name: 'text',
        type: 'string',
        defaultValue: 'I\'m version 2'
      },
    ],
  },
]

Deprecating custom components

To phase out custom components while ensuring that contents using those components still work as intended, use the hideFromInsertMenu option so your component is no longer visible in the Visual Editor.

Continuing to register a deprecated component with Builder means Builder can still use your component for older content while teammates use the newer version that's in the Insert tab.

For more detail, see the entry on hideFromInsertMenu in Registering Custom Components.

export const REGISTERED_COMPONENTS = [
  // Hide the original version
  {
    ...,
    hideFromInsertMenu: true,
    ...
  }
]

registerComponent() options

With the registerComponent() function, you can define and register custom components for the Visual Editor with tailored solutions by defining properties, behavior, and appearance of your components.

Prerequisites

To get the most out of this document, you should be familiar with Registering Custom Components.

Required: name property

name

Type: string

Assign a unique name to identify, reference, and register the component.

export const REGISTERED_COMPONENTS = [{
component: MyButton,
name: 'MyButton',
// Other properties...
}];

To override a built-in component with your custom component, you can use the same name as the built-in component. For example, to replace the built-in Text component, you can register your component with the name Text:

export const REGISTERED_COMPONENTS = [{
// Overrides built-in Text component
component: Text,
name: 'Text',
override: true,
// Other properties...
}];

Optional properties

  1. canHaveChildren

    Type: boolean

    Set to true if your component can accept children. Only use canHaveChildren when a component will have children.
export const REGISTERED_COMPONENTS = [{
component: HeroWithBuilderChildren,
name: 'Hero',
canHaveChildren: true, // Can have child components
...
}];
  1. childRequirements

    Type: object

    Specify restrictions on the direct children of a component. Use to define the rules that the children must match in order to be considered valid. canHaveChildren
  2. defaultChildren

    Type: BuilderElement[]

    Use to specify the default child elements for a component. It is an array that contains BuilderElement objects, which represent the child elements within the component.
export const REGISTERED_COMPONENTS = [{
component: HeroWithBuilderChildren,
name: 'Hero',
defaultChildren: [
  {
    '@type': '@builder.io/sdk:Element',
    component: {name: 'Text', options: {text: 'I am child text block!'}}
  }
]
}];
  1. defaultStyles

    Type: object

    Specify default styles for an element or component when it is dropped into the Builder.io editor.

    Accepts an object where each key represents a breakpoint or screen size, and the corresponding value is a set of CSS styles to be applied at that breakpoint.
export const REGISTERED_COMPONENTS = [{
component: SaleBanner,
name: 'Sale Banner',
defaultStyles: {
  backgroundColor: 'red',
  color: 'white',
  fontSize: '24px'
}
}];
  1. defaults

    Type: Partial<BuilderElement>

    Provide default options for the block when it is created. When a new instance of the block is created, these default options will be merged with any user-defined options, providing initial values for the block's properties.

    For instance, to define responsive styles, use defaults.responsiveStyles:
export const REGISTERED_COMPONENTS = [{
component: SaleBanner,
name: 'Sale Banner',
defaults: {
  responsiveStyles: {
    // Index signature with dynamic keys and string values
    large: {
      backgroundColor: 'red',
      color: 'white',
      fontSize: '24px'
    },
    small: {
      backgroundColor: 'green',
      color: 'black',
      fontSize: '16px'
    },
  }
}
}];
  1. docsLink

    Type: string

    Provide a link to a documentation page for the component. It is a string that represents the URL of the documentation page. This is helpful for users who want to learn more about the component's features, usage, or customization options.
export const REGISTERED_COMPONENTS = [{
component: InfoCallOut,
name: 'Info Call Out',
docsLink: 'https://www.builder.io/c/docs/developers',
// Other properties...
}];
  1. hideFromInsertMenu

    Type: boolean

    Hide your component in editor, useful for gradually deprecating and Versioning Custom Components.
export const REGISTERED_COMPONENTS = [{
component: Heading,
name: 'Heading',
hideFromInsertMenu: true, // <-- don't show in Insert tab
...
}]
  1. image

    Type: string

    Link to an image to be used as an icon for this component in Builder's Visual Editor.
export const REGISTERED_COMPONENTS = [{
component: Hero,
name: 'Hero',
// replace with your actual asset URL
image: 'https://cdn.builder.io/hero-image.jpg',
// Other properties...
}];
  1. inputs

    Type: Input[]

    Represents the input schema for the component which is an array of Input objects that define the options in the user interface (UI) to configure the component's properties or behavior.

    Each input object specifies properties, for example:
export const REGISTERED_COMPONENTS = [{
component: MyComponent,
name: 'MyComponent',
inputs: [
  {
    name: 'text',
    type: 'text',
    label: 'Text Input',
    defaultValue: 'Hello, Builder!',
    required: true
  }
],
// Other properties...
}];
  1. noWrap

    Type: boolean

    Assign a unique name to identify, reference, and register the component.
export const REGISTERED_COMPONENTS = [{
component: FormInputComponent,
name: 'FormInputComponent',
noWrap: true
// Other properties...
}];
  1. override

    Type: boolean

    Override the behavior of a built-in component. Any special behavior or functionality provided by the default button component, such as virtual options or custom editors, are skipped.
export const REGISTERED_COMPONENTS = [{
component: Button,
name: 'Button',
override: true
// Other properties...
}];
  1. requiredPermissions

    Type: Permission[]

    Specify the availability of the component dependent on permissions.

    Possible Permission values are read, publish, editCode, editDesigns, admin, and create.
export const REGISTERED_COMPONENTS = [{
component: Button,
name: 'Button',
requiredPermissions: ['editDesigns'],
// Other properties...
}];
  1. requiresParent

    Type: object

    Specify restrictions on the parent component that must be met in order for the current component to be valid. Use to define requirements for the parent component's properties or type.
// example parent component
export const REGISTERED_COMPONENTS = [{
component: ProductBox,
name: 'Product Box',
// Other properties...
}];
// component that requiresParent
export const REGISTERED_COMPONENTS = [{
component: AddToCartButton,
name: 'Add to Cart Button',
requiresParent: {
  component: 'Product Box',
  message: "'Add to Cart' buttons must be within a 'Product Box'",
},
// Other properties...
}];

The available requiresParent options are:

  • message: A string that represents the message to be displayed when the parent requirement is not met.
  • component (optional): Specify that the parent component must be a particular component name.
  • query (optional): Specify a MongoDB-style query using the sift.js library. This query defines the conditions that at least one parent in the hierarchy should match. The following example uses the component.name field to check if the parent is either a Product box or a Collection component.
    query: {
    // Thils element must be somewhere inside
    // either a 'Product box' or 'Collection' component
    'component.name'
    

: { $in: 'Product Box', 'Collection' } }


14. <u>**_screenshot_**</u> <br><br>
  **Type**: `string` <br><br>
  Provide a screenshot that is displayed when a user hovers over the component in the Visual Editor.

```js
export const REGISTERED_COMPONENTS = [{
component: MyComponent,
name: 'MyComponent',
// replace with your actual asset URL
screenshot: 'https://cdn.builder.io/my-component-screenshot.png',
// other component properties...
}];
  1. tag

    Type: string

    Use to specify a custom tag name for your custom web components. In this example, you could use <my-custom-component>.
export const REGISTERED_COMPONENTS = [{
component: MyCustomComponent,
name: 'MyCustomComponent',
tag: 'my-custom-component',
// other component properties...
}];

The tag property is specifically for custom web components and is not applicable to other types of components.


Copyright © 2024