Custom Tool Example
This page shows how to register a custom "Testimonial" content tool with editable name, quote, and avatar properties and a custom HTML renderer.
src/TestimonialToolEditor.jsx
import React, { useRef, useCallback } from "react";
import { DragbleEditor } from "dragble-react-editor";
const EDITOR_KEY = "your-editor-key";
// --- 1. Define the tool ---
const testimonialTool = {
name: "testimonial",
label: "Testimonial",
icon: "https://cdn.example.com/icons/testimonial.svg",
category: "content",
// Default property values for a new instance
properties: {
authorName: {
label: "Author Name",
defaultValue: "Jane Doe",
widget: "text",
},
quote: {
label: "Quote",
defaultValue: "This product changed my workflow completely.",
widget: "rich_text",
},
avatarUrl: {
label: "Avatar URL",
defaultValue: "https://i.pravatar.cc/80",
widget: "image",
},
starRating: {
label: "Star Rating (1-5)",
defaultValue: 5,
widget: "counter",
options: { min: 1, max: 5 },
},
},
// --- 2. HTML renderer ---
renderer: {
render({ values }) {
const stars =
"\u2605".repeat(values.starRating) +
"\u2606".repeat(5 - values.starRating);
return `
<table role="presentation" width="100%" cellpadding="0" cellspacing="0"
style="background:#f9fafb;border-radius:8px;padding:24px;">
<tr>
<td width="64" valign="top">
<img src="${values.avatarUrl}" alt="${values.authorName}"
width="56" height="56"
style="border-radius:50%;object-fit:cover;" />
</td>
<td style="padding-left:16px;">
<p style="margin:0 0 8px;font-style:italic;color:#374151;font-size:15px;">
"${values.quote}"
</p>
<p style="margin:0;font-weight:600;color:#111827;font-size:14px;">
${values.authorName}
</p>
<p style="margin:4px 0 0;color:#f59e0b;font-size:16px;">${stars}</p>
</td>
</tr>
</table>
`;
},
},
};
// --- 3. Use it ---
export default function TestimonialToolEditor() {
const editorRef = useRef(null);
const onReady = useCallback(() => {
editorRef.current?.registerTool(testimonialTool);
console.log("Testimonial tool registered");
}, []);
const exportHtml = useCallback(() => {
editorRef.current?.exportHtml((data) => {
console.log(data.html);
});
}, []);
return (
<div style={{ display: "flex", flexDirection: "column", height: "100vh" }}>
<div style={{ padding: 10, display: "flex", gap: 8 }}>
<button onClick={exportHtml}>Export HTML</button>
</div>
<div style={{ flex: 1 }}>
<DragbleEditor
ref={editorRef}
editorKey={EDITOR_KEY}
onReady={onReady}
minHeight="100%"
/>
</div>
</div>
);
}
How it works
- Define properties -- each property gets a
widgettype (text,rich_text,image,counter,color,dropdown, etc.) that controls the property editor panel. - Write a renderer --
renderer.render()receives the currentvaluesand returns an HTML string. This HTML is used both inside the editor canvas and in the final export. - Register on ready -- call
registerTool()after the editor firesonReadyso the internal tool registry is initialised.
Property widget reference
| Widget | Value type | Notes |
|---|---|---|
text | string | Single-line input |
rich_text | string | Rich text editor with bold/italic/link |
image | string (URL) | Opens the image picker |
color | string (hex) | Colour picker |
counter | number | Stepper with min/max options |
dropdown | string | Requires options.items array |
toggle | boolean | On/off switch |