Skip to main content

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 { PexelizeEditor } from '@pexelize/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 }}>
<PexelizeEditor
ref={editorRef}
editorKey={EDITOR_KEY}
onReady={onReady}
minHeight="100%"
/>
</div>
</div>
);
}

How it works

  1. Define properties -- each property gets a widget type (text, rich_text, image, counter, color, dropdown, etc.) that controls the property editor panel.
  2. Write a renderer -- renderer.render() receives the current values and returns an HTML string. This HTML is used both inside the editor canvas and in the final export.
  3. Register on ready -- call registerTool() after the editor fires onReady so the internal tool registry is initialised.

Property widget reference

WidgetValue typeNotes
textstringSingle-line input
rich_textstringRich text editor with bold/italic/link
imagestring (URL)Opens the image picker
colorstring (hex)Colour picker
counternumberStepper with min/max options
dropdownstringRequires options.items array
togglebooleanOn/off switch