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:
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.
- 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>
- Registering your component
Create a fileinit-builder.ts
inroot
directory and then create an array of your custom components inside it. Here, the array contains the HelloWorld component with two options:
i) aname
ii) a list of possibleinputs
, in this case atext
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',
},
],
},
]
- Provide this list of
REGISTERED_COMPONENTS
to theContent
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
tocustomComponents
props ofContent
component (previously,RenderContent
).
<template>
<Content
model="page"
:content="content"
:apiKey="apiKey"
:customComponents="getRegisteredComponents()"
/>
</template>
- 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:
- component default styling
- whether Builder wraps the component in a
<div>
- whether the component displays in the Insert tab
- 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::
- Configured your codebase to use your custom component with Builder
- 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.
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.
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:
- An app you've integrated with Builder
- Familiarity with using custom components in Builder
Adding children to custom components
- Create a
CustomComponent
<template>
<div>
<h1> {{ title }} </h1>
<slot></slot>
</div>
</template>
<script setup>
const props = defineProps({
title: {
type: String,
default: 'Custom Component'
}
})
</script>
- Register
CustomComponent
to Builder.io by adding the code insideinit-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'
}
]
}
]
- 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:
- 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
. - Drag and drop your custom component into the work area.
- 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:
- In the Insert tab, expand the Custom Components section.
- Drag and drop your component onto the work area.
- With your component selected, click the Edit button and add sets of children if needed.
- 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:
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:
- Register a component with the same name as the component in Builder that you'd like to override.
- Set the
override
property totrue
- 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:
- Required inputs: inputs you must use with your custom components
- Optional inputs for further customization: inputs that you can use to modify the look and behavior of your component in the Visual Editor
- Input type examples: definitions, code snippets, and a screenshot of input types in the Visual Editor
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:
Name | Require | Description |
---|---|---|
name | Yes | A unique name for this input that should match the equivalent prop name on your React component. |
type | Yes | Types 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.
Name | Require | Description |
---|---|---|
advanced | Boolean | Set 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. | ||
allowedFileTypes | array | For 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'] |
defaultValue | any | Use for showing an example value in the input form when creating a new instance of this component, to users understand its purpose. |
enum | array | For any text-based field type, you can specify a set of options that the field can use. enum: ['option 1', 'option 2'] |
friendlyName | string | The name the Visual Editor displays for the input. friendlyName: 'Open link in new tab', |
helperText | string | Provide 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:
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:
- Optionally hide the previous version of the component to prevent its use.
- 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.
- Set
hideFromInsertMenu
totrue
. 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'
},
],
},
]
- Register the second version of the component. Keep the original version registered so any content that relies on it continues working as expected.
- 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 isv2: 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.
When backward-compatibility isn't possible
If the updates you want to make preclude backward compatibility, take the following steps:
- Create an entirely new component registered with a new name.
- Keep the previous version of the component registered with Builder so that existing content on your site using that component continues to function properly.
- 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
- canHaveChildren
Type:boolean
Set totrue
if your component can accept children. Only usecanHaveChildren
when a component will have children.
export const REGISTERED_COMPONENTS = [{
component: HeroWithBuilderChildren,
name: 'Hero',
canHaveChildren: true, // Can have child components
...
}];
- 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. - defaultChildren
Type:BuilderElement[]
Use to specify the default child elements for a component. It is an array that containsBuilderElement
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!'}}
}
]
}];
- defaultStyles
Type:object
Specify default styles for an element or component when it is dropped into the Builder.io editor.
Accepts anobject
where eachkey
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'
}
}];
- 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, usedefaults.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'
},
}
}
}];
- 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...
}];
- 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
...
}]
- 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...
}];
- inputs
Type:Input[]
Represents the input schema for the component which is an array ofInput
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...
}];
- 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...
}];
- 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...
}];
- requiredPermissions
Type:Permission[]
Specify the availability of the component dependent on permissions.
PossiblePermission
values areread
,publish
,editCode
,editDesigns
,admin
, andcreate
.
export const REGISTERED_COMPONENTS = [{
component: Button,
name: 'Button',
requiredPermissions: ['editDesigns'],
// Other properties...
}];
- 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 thesift.js
library. This query defines the conditions that at least one parent in the hierarchy should match. The following example uses thecomponent.name
field to check if the parent is either aProduct box
or aCollection
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...
}];
- 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.