BUILDER

Resources

Home

Forum

Github

Login

Signup

This page was made in Builder!

Using your react components in Builder

Using your own React components in Builder is one of our most powerful features! Learn how below.

👉Tip: for a lot of examples of using custom components with Builder.io, see our React style guide example repo!

Install the SDK

To begin, use npm or yarn to install @builder.io/react.

npm install --save @builder.io/react

Builder-Enabling a Component

Below is the actual source for a component we use heavily within our docs. This component takes two props, "code" and "language" and uses react-syntax-highlighter to syntax highlight the code.

This is a normal react component that can be used as you typically would, e.g. <CodeBlock code="..." />, but we want to be able to use this in the Builder drag and drop editor.

To do this, we simply import the withBuilder function from the @builder.io/react package in npm. We then add it to our component and specify the name of our component (this is case sensitive and must be unique), and the inputs.

import * as React from 'react';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { Builder } from '@builder.io/react';

class CodeBlockComponent extends React.Component {
  render() {
    return (
      <SyntaxHighlighter language={this.props.language}>
        {this.props.code}
      </SyntaxHighlighter>
    );
  }
}

Builder.registerComponent(CodeBlockComponent, {
  name: 'Code Block',
  inputs: [
    {
      name: 'code',
      type: 'string',
      defaultValue: 'const incr = num => num + 1',
    },
    {
      name: 'language',
      type: 'string',
      defaultValue: 'javascript',
    },
  ],
})

Including the Component

Next, on any page we want to use this component, we need to be sure to import it. For example, in Builder's docs pages, we simply add one import statemement as shown below:

import './code-block'

For our Builder docs, the full file for this looks like:

import './code-block'
import { BuilderComponent } from '@builder.io/react';

export const DocsPage = () => (
  <BuilderComponent modelName="docs-page" />
)

And in our react router, we simply add:

<Route path="/c/docs" exact={false} component={DocsPage} />

Using the React BuilderComponent

👉Tip: want to limit visual editing to only your custom components? Try components only mode

You may have noticed that above we import { BuilderComponent } from '@builder.io/react', but some of our code snippets we use BuilderSimpleComponent. This is intentional - the BuilderSimpleComponent is the simplest option, but for more advanced use cases like including your components you want the BuilderComponent.

Initializing with your api key

One quick note - in all of the below examples we don't include the apiKey attribute on every BuilderComponent. You may do this, but for most react apps it's easier to initialize with your API key one time instead. E.g. in you index.js:

import { builder } from '@builder.io/react';
builder.init(YOUR_KEY);

You can find your public client API key over in your organization settings

Input types

Here is a more lengthy example demonstrating more input types

export const CodeBlock = withBuilder(CodeBlockComponent, {
  name: 'Input Examples',
  inputs: [
    { name: 'text',  type: 'string' },
    { name: 'switch',  type: 'boolean' },
    { name: 'image', type: 'file', allowedFileTypes: ['jpeg', 'png'] },
    { name: 'exampleList',  type: 'list', subFields: [
        { name: 'text', type: 'string' }
    ]},
  ],
})

The schema of inputs is as follows:

NameRequiredDescription

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 should be used 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'

VIEW DETAILED DOCS ON INPUT TYPES

You can also add custom types here via plugins

defaultValue

No

An optional default value for this prop. This is useful for showing an example value in the input form when creating a new instance of this component, to better help people using it understand it's purpose

subFields

No

If the input type is "list" you need to include the "subFields" property that is a list of inputs (with this same schema) for each list item

allowedFileTypes

No

For the "file" input type you typically want to specify what types of files can be uploaded. This is an array that takes content-type files such as:

['jpeg', 'png', 'mp4', 'gif', 'pdf', 'svg']

enum

No

For any text-based field type, can set a specific set of options taht can be used

['option 1', 'option 2']

👉 For more detailed examples of input types, click here

Default Styles

If you would like your components to have some default CSS styles applied, that can be edited in the style tab, you can set default styles like in the example below

export const HelloWorld = withBuilder(HeloWorldComponent, {
  name: 'Hello World',
  defaultStyles: {
    textAlign: 'center',
    fontSize: '20px'
  },
  inputs: [
    { name: 'text',  type: 'string' }
  ]
})

Using your components in the editor

To use your components in the editor, make sure you first follow this guide to allow editing and previewing directly on your site

Then, when editing a page in Builder that imports components with the withBuilder function, they will display in your "insert" tab in a "code components" section.

Developing and testing locally

When developing locally, you'll want to update the preview URL in the top right corner of the preview from your production URL to your local development URL.

Note that when developing locally you are mostly likely developing on a non-ssl http:// url within Builder, which is an https:// site. Browsers don't allow https:// sites to make insecure http:// requests unless you explicitly allow it

On chrome, to allow access to your local http URL choose the shield icon on the right side of your URL bar, and then choose "load unsafe scripts". The page will reload and you may have to enter your local URL a second time, but this time chrome will allow it's content to load

Children in custom components

You can also have children within your Buider.io components (limited to specific child options or any type of child). Note the use of withChildren() below - this will allow this component to have children dropped into it

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

export const Hero = props =>
  <div className={heroStyles}>{div}</div>

const HeroWithBuilderChildren = withChildren(Hero)

Builder.registerElement(HeroWithBuilderChildren, {
  name: 'Hero',
  // Adding defaults is important for easy usability
  defaultChildren: [
    { 
      '@type': '@builder.io/sdk:Element',
      component: { name: 'Text', options: { text: 'I am child text block!' } }
    }
  ]
})

If you want to be fancy and only allow certain types of children, you can do this with childRequirements like in this example

  childRequirements: {
    message: 'You can only put Buttons, Text, or Headings in a Hero',
    query: {
      'component.name': { $in: ['Button', 'Text', 'Heading'] },
    },
  }

Advanced child sub-component use cases

For use-cases where you need multiple sets of children rendered - e.g. making your own custom tabs components, see our built-in tabs component source code here. More examples of component source code with multiple children

▶ Expand code example

👉Tip: You can see a detailed answer to advanced React children use cases with code examples over in our forum!

noWrap

By default Builder wraps your components in a dom element (by default a div but can be changed) with a few needed attributes and classes for the editor to work as expected.

You can opt out of this wrapping by using the noWrap option. You can see a full code example of this with our built-in form input component here

For sake of example here, if you say wanted to use a Material UI TextField in Builder.io with noWrap

import { TextField } from '@material-ui/core'

export const BuilderTextField = props => (
  // Important! Builder.io must add a couple classes and attributes via props.attributes
  // Important! If you add your own classes do it after ...props.attributes 
  <TextField 
    variant={props.variant} 
    {...props.attributes} 
    className={`my-class ${props.attributes.className}`}
   />
)

Builder.registerComponent(BuilderTextField, {
  name: 'TextField',
  noWrap: true, // Important!
  inputs: [{ name: 'variant', type: 'string' }]
})

Input type examples:

string: any text, usually shorter in length and unformatted

alias: text

  {
    name: 'buttonText',
    type: 'string',
    defaultValue: 'Click',
  }

number: an input field expected to take a number such as an amount

  {
    name: 'amount',
    type: 'number',
    defaultValue: 20,
  }

boolean: an input field taking true or false

  {
    name: 'darkMode',
    type: 'boolean',
    defaultValue: true,
  }

longText: same as string type but with a multiline text field editor. (if formatted, use richText)

  {
    name: 'description',
    type: 'longText',
    defaultValue: 'Builder is the first and only headless CMS with a powerful 
    drag-and-drop visual editor that lets you build, 
    optimize, and measure digital experiences with speed and flexibility'
  }

richText: Displays a rich text editor and provides the value as html

alias: html

  {
    name: 'description',
    type: 'richText',
    defaultValue: '<b>This text is bold</b>'
  }

file: Uploads a file and provides the value as a url string. See allowedFileTypes for additional details.

  { 
    name: 'image',
    type: 'file', 
    allowedFileTypes: ['jpeg', 'png'] 
  }

color: provides a color value (hex or rgb) to a component

 {
    name: 'backgroundColor',
    type: 'color',
    defaultValue: '#fafafafa',
  }

date: any format that the date constructor for Javascript takes

  {
    name: 'event',
    type: 'date',
    defaultValue: 'December 17, 1995 03:24:00', 
  }

email: create an email value for a component

  {
    name: 'signup',
    type: 'email',
    defaultValue: 'noreply@email.com'
  }

object: a set of specific names and values

*note: you will get errors if no defaultValue is set for objects 
  {
      name: 'Carousel',
      type: 'object',
      defaultValue: {
         variant: 'primary'
      },
      subFields: [
        {
          name: 'text',
          type: 'string',
          required: true,
        },
        {
          name: 'url',
          type: 'url',
          required: true,
        },
        {
          name: 'variant',
          type: 'string',
          defaultValue: 'primary',
          enum: ['primary', 'info', 'dark', 'light', 'warning'],
        },
      ],
    }

list: list of items

alias: array

*note: you will get errors if no defaultValue is set for lists

{
      name: 'reviews',
      type: 'list',
      defaultValue: [ 
            { reviewText: 'hello' 
     }],
      subFields: [
	{
          name: 'reviewText',
          type: 'string',
          defaultValue: '"You guys are 
          the best"',
        },
        {
          name: 'reviewAuthor',
          type: 'string',
          defaultValue: 'Jane Smith',
        },
        {
          name: 'image',
          type: 'file',
          allowedFileTypes: ['jpeg', 'jpg', 'png', 'svg'],
          required: true,
          defaultValue:
         'https://cdn.builder.io/api/v1/image/assets%2Fpwgjf0RoYWbdnJSbpBAjXNRMe9F2%2Ffb27a7c790324294af8be1c35fe30f4d',
        },
      ],
    }

Components-only mode

Want to limit page building to only your custom code components? Try out components-only mode

Don't use React?

Try using symbols for the same level of functionality and symbols support any framework!

Up next

Components only mode
Was this article helpful?