This doc demonstrates how to create a custom plugin that adds a word counter to Builder's Visual Editor. Follow the instructions in this doc to learn how to create your own custom plugin.
This doc assumes you have followed the instructions in Custom plugin setup to create a local directory with the boilerplate code needed to create a custom plugin.
Create a toolbar plugin that, when clicked, provides a count of words within the selected Builder block.
Begin by registering a component. The Builder.register() method accepts two arguments:
- A string, representing the plugin type.
- An object, providing configuration details for the plugin, including the component to render.
The following code includes the plugin type 'editor.toolbarButton', which adds a button to the toolbar at the top of the Visual Editor.
For the second argument, an object is passed in with a single key, component. The value for this key is a function that returns a ToolbarButton component.
This code should be added to the src/plugin.jsx file.
Builder.register("editor.toolbarButton", {
component: () => <ToolbarButton />,
});Next, create the component. In the code block below, a ToolbarButton component is created that launches an alert when clicked.
const onClick = (e) => {
e.preventDefault();
alert("Hello from the toolbar button plugin!");
};
function ToolbarButton() {
return (
<button onClick={onClick}>Word Count</button>
);
}Refresh your Visual Editor to see your toolbar button. If you do not see a button, review your code or return to Custom plugin setup.
The video below demonstrates what to expect at this point in the process.
To implement the word count feature, make the following adjustments to the ToolbarButton component:
- Use the
builderobject attached to the browser'swindowobject to access currently selected elements. - Select the first element of selected elements.
- Access the
innerTextof that element. - Count the number of words within the text.
// Define a simple toolbar button component that will be registered as a plugin.
const onClick = (e) => {
e.preventDefault();
const element = window.builder?.selectedElements?.[0];
const text = element?.innerText?.trim();
const wordCount =
text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
alert(`Word Count: ${wordCount}`);
};
function ToolbarButton() {
return (
<button onClick={onClick}>Word Count</button>
);
}Refresh the Visual Editor, select a containing block that contains text, and press your custom plugin button. You should see a change to the alert message.
Note: Your word count value may say 0. This is addressed in the next step.
At this point, you have implemented a custom plugin. You can now continue to improve the implementation of your plugin.
Consider styling your plugin to fit Builder's branding or your own. Additionally, continue to test your plugin's implementation to solve bugs and edge cases.
For example, the current implementation of this plugin assumes the user will select a wrapping block that contains text. A more robust implementation will be needed to select wrapping containers, text blocks, and more.
See a more complete plugin.jsx file below.
// The jsx pragma and import replace React.createElement with Emotion's jsx factory,
// enabling the css prop for inline Emotion styling on JSX elements.
/** @jsx jsx */
import { jsx } from "@emotion/core";
// Import the Builder SDK, which provides the Builder global and APIs for registering plugins.
import { Builder } from "@builder.io/react";
// Inline styles for the toolbar button component.
const style = {
border: "none",
cursor: "pointer",
marginRight: "8px",
padding: "8px 12px",
fontFamily: "inherit",
fontSize: "14px",
color: "white",
backgroundColor: "rgb(200, 26, 235)",
borderRadius: "6px",
};
// Recursively extract all text values from a Builder content data object.
function extractText(value) {
if (typeof value === "string") return value;
if (Array.isArray(value)) return value.map(extractText).join(" ");
if (value && typeof value === "object") {
return Object.values(value).map(extractText).join(" ");
}
return "";
}
// Define a simple toolbar button component that will be registered as a plugin.
const onClick = (e) => {
e.preventDefault();
const element = window.builder?.selectedElements?.[0];
const options = element?.component?.options;
const text =
element?.innerText?.trim() ||
extractText(options?.toJSON?.() ?? options ?? {});
const wordCount = text.trim() === "" ? 0 : text.trim().split(/\s+/).length;
alert(`Word Count: ${wordCount}`);
};
function ToolbarButton() {
return <button onClick={onClick}>Word Count</button>;
}
// Register the toolbar button plugin with Builder. The first argument specifies
// the plugin type and the second argument provides the plugin implementation,
// including the React component to render.
Builder.register("editor.toolbarButton", {
component: () => <ToolbarButton />,
});