You can extend and customize Builder by making your own plugin. With custom plugins you can register custom field types for your Builder model custom fields and inputs for your custom components, custom targeting or symbols.
This document covers making a minimal plugin to add a custom type for use in a Data model.
Tip: All plans can create and submit Builder Plugins. When you're ready to contribute your plugin, head over to GitHub and submit a PR. Make sure to add your plugin to the plugins
directory.
To get the most out of this document, make sure you are familiar with the following:
- Types of Plugins: get an intro to the different kinds of plugins you can make
- Custom fields: customize field types in models
- Data models: define the shape of your data
This tutorial guides you through creating a plugin with a custom type and custom type editor. By creating custom types in a plugin, you provide rich data types that you can use across the Visual Editor. For example, you can:
- Create custom component inputs that accept structured data fields
- Target content based on products in a customer's cart
- Model fields that store multimedia content
- Enable Symbol inputs to accept externally hosted documents
Each custom type has a custom type editor, which provides a user interface for selecting values for your custom type fields. For example, editors can fetch external resources and present the user with a browsable list of items or provide menus and fields for data entry.
With custom types and custom type editors, your users can create and update nearly any kind of data structure beyond the basic types provided by Builder, all from within the Visual Editor.
After installing the plugin that registers your custom type editor, the corresponding custom type is available across the Visual Editor, specifically when using the following features:
- Custom component inputs
- Targeting content
- Model fields
- Symbol inputs
Create a directory for your plugin and open the new director with the following command:
mkdir text-plugin && cd text-plugin
Initialize the project to use npm
:
npm init
Press Enter
through the prompts and respond yes
to the proposed package.json
.
Use npm install
to install the dependencies for this project. You can use --save-dev
because Builder provides these dependencies for you later. The dependencies are:
@builder.io/app-context
: exposes certain APIs to interact with APIs to interact with Builder@builder.io/react
: enables you to use React to create your pluginwebpack
: module bundler for JavaScriptwebpack-dev-server
: development server with live reloading@babel/preset-react
: supportsJSX
babel-loader
: transpiles modern and superset JavaScript, such asJSX
using Babel with webpack
Paste the following command, which includes all these dependencies, at the command line:
npm install --save-dev @builder.io/app-context @builder.io/react webpack webpack-dev-server webpack-cli @babel/preset-react babel-loader
Install react-quill
for the Rich Text editor that this plugin uses:
npm install react-quill
This section guides you through creating the project infrastructure by creating the key files and pasting in the contents for each.
Replace the contents of package.json
with the following:
{
"name": "rich-text-plugin",
"version": "1.0.0",
"description": "",
"entry": "plugin",
"output": "plugin.system.js",
"main": "dist/plugin.system.js",
"files": [
"dist"
],
"scripts": {
"build": "webpack --mode production",
"start": "webpack-dev-server --mode development"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/preset-react": "^7.18.6",
"@builder.io/app-context": "^1.0.0",
"@builder.io/react": "^2.0.13",
"@emotion/core": "^11.0.0",
"babel-loader": "^8.2.5",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.10.0"
},
"dependencies": {
"react-quill": "^2.0.0"
}
}
At the root of your project, create a file called .babelrc
and paste in the following:
// contents of .babelrc
// use preset-react to support JSX
{
"presets": ["@babel/preset-react"],
"env": {
"development": {
"presets": [["@babel/preset-react", { "development": true }]]
}
}
}
Again at the root of your project, create a file called webpack.config.js
and paste in the following:
// contents of webpack.config.js
const path = require('path');
const pkg = require('./package.json');
module.exports = {
entry: `./src/${pkg.entry}.jsx`,
externals: {
'@builder.io/react': '@builder.io/react',
'@builder.io/app-context': '@builder.io/app-context',
"@emotion/core": "@emotion/core",
"react": "react",
"react-dom": "react-dom"
},
output: {
filename: pkg.output,
path: path.resolve(__dirname, 'dist'),
libraryTarget: 'system',
},
resolve: {
extensions: ['.js', '.jsx'],
},
module: {
rules: [
{
test: /\.(jsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
},
],
},
],
},
devServer: {
port: 1268,
static: {
directory: path.join(__dirname, './dist'),
},
headers: {
'Access-Control-Allow-Private-Network': 'true',
'Access-Control-Allow-Origin': '*',
},
},
};
This file establishes these key configurations:
- The location of the entry point, or where webpack starts when bundling. Here, it's
plugin.jsx
. - The
externals
are dependencies that you don't need to bundle. You don't need to bundle them because when your plugin is ready, Builder provides these dependencies for you. resolve.extensions
specifies that you're using JavaScript andJSX
files. If you're using other file extensions, add them here.- The module settings specify that you're using
JSX
files, not bundlingnode_modules
, and using babel-loader to work withJSX
. - With
devServer
, configures your plugin to run locally onlocalhost:1268
, serve from the./dist
directory, and provides headers for local development with Builder.
Tip: You can also pass data into your plugin when registering it as an input type. This is useful if you want to control things like API keys or settings in your own codebase, but want to be able to pass into the plugin whenever it is used. To do this, use the options
property when registering an input for a component. For more detail, see this example on GitHub from an async dropdown plugin.
Create a folder called src
with a file called plugin.jsx
and paste in the following:
// contents of plugin.jsx
/** @jsx jsx */
import { jsx } from '@emotion/core';
import { Builder } from '@builder.io/react';
import ReactQuill from 'react-quill';
function RichTextEditor(props) {
return (
<ReactQuill
value={props.value}
onChange={props.onChange}
/>
);
}
Builder.registerEditor({
name: 'MyText',
component: RichTextEditor
});
The key points in the example above are:
- The editor component wraps
ReactQuill
, which in turn takesonChange
andvalue
as props. The value you set can be any type serializable to JSON, such asstring
,number
,null
,array
, orobject
, and be as deeply nested as you need. Refer to the react-quill documentation for more information. props.onChange
andprops.value
are passed to the editor component, which controls the editor's state.value
is the current value of the field.onChange
is a callback function that accepts one parameter, which is the field's new value after the user changes that value within the editor. For more information, see Controlled Components in the React documentation.- When the user updates the text area provided by
ReactQuill
,ReactQuill
callsonChange
, which updates the editor's state. - Updating the editor's state updates
props.value
, which is passed down toReactQuill
to update that component's internal state. - Registering the editor component with
Builder.registerEditor()
creates theMyText
custom type.
Builder.registerEditor()
accepts one parameter: an options object with three properties:
name
: a string, typically camel-cased, that represents the custom type's namecomponent
: the editor componentoptions
(optional): an object
npm run build && npm run start
Tip: 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. To allow access to your local http
URL in Chrome, click the shield icon on the right side of the address bar, and choose load unsafe scripts. The page will reload and you might have to enter your local URL a second time for Chrome to allow its content to load.
For more information on input types, see Input Types for Custom Components.
- Go to Account Settings.
- Click the pencil icon for Plugins to add the plugin.
- Enter the local address for this example plugin:
http://localhost:1268/plugin.system.js
. - Click the Save button.
The following video shows these steps:
- Go to Models.
- Select a model to edit or create a new one.
- In the model, click the + New Field button.
- To confirm that your plugin is working, click the Type dropdown and scroll down to select the type MyText. Notice that when you select MyText, the Default value input changes to a Rich Text input that includes text formatting options.
MyText comes from the name
you provided in plugin.jsx
. to Builder.registerEditor()
. The following video shows these steps:
This tutorial covered how to create a minimal plugin and use it locally. When you've created your plugin, you can do one of two things:
- Get your unique plugin creations added to Builder, by heading over to GitHub and submitting a PR with your plugin on the Builder.io repo.
- If you're on an Enterprise plan, you can instead host your plugin yourself as a private plugin. For more information, see Creating a Private Plugin.