Learn how to customize themes, create custom shapes, and extend the design tool functionality.
Override the default theme by defining custom CSS variables:
:root {
--primary-color: #667eea;
--secondary-color: #764ba2;
--background-color: #ffffff;
--text-color: #2d3748;
--border-color: #e2e8f0;
--success-color: #48bb78;
--warning-color: #ed8936;
--error-color: #f56565;
}
The design tool includes built-in dark mode support:
import { useCanvasManager } from '@rifrocket/fabricjs-design-tool';
const canvasManager = useCanvasManager(canvas, {
darkMode: true,
backgroundColor: '#1a202c'
});
// Toggle dark mode programmatically
canvasManager.toggleDarkMode();
Customize the toolbar with your own components:
import { Header } from '@rifrocket/fabricjs-design-tool/ui';
function CustomHeader() {
return (
<Header
logo={<img src="/my-logo.png" alt="Logo" />}
customActions={[
{ label: 'Custom Action', onClick: handleCustomAction },
{ label: 'Export PDF', onClick: handlePDFExport }
]}
/>
);
}
Extend Fabric.js to create your own shape types:
import { fabric } from 'fabric';
// Create a custom star shape
const StarShape = fabric.util.createClass(fabric.Polygon, {
type: 'star',
initialize: function(options) {
const points = this.createStarPoints(options.points || 5, options.radius || 50);
this.callSuper('initialize', points, options);
},
createStarPoints: function(numPoints, radius) {
const points = [];
const angle = Math.PI / numPoints;
for (let i = 0; i < numPoints * 2; i++) {
const r = i % 2 === 0 ? radius : radius / 2;
points.push({
x: Math.cos(i * angle) * r,
y: Math.sin(i * angle) * r
});
}
return points;
}
});
// Register the shape
fabric.StarShape = StarShape;
Integrate custom shapes into the shape factory:
import { shapeFactory } from '@rifrocket/fabricjs-design-tool';
// Extend the shape factory
shapeFactory.createStar = function(options = {}) {
return new fabric.StarShape({
left: options.left || 100,
top: options.top || 100,
points: options.points || 5,
radius: options.radius || 50,
fill: options.fill || '#ffcc00',
stroke: options.stroke || '#ff6600',
strokeWidth: options.strokeWidth || 2,
...options
});
};
// Use in your application
const star = shapeFactory.createStar({
left: 200,
top: 150,
points: 6,
fill: 'gold'
});
canvas.add(star);
Define presets for commonly used shapes:
const shapePresets = {
logo: {
type: 'rectangle',
width: 200,
height: 60,
fill: '#667eea',
rx: 8,
ry: 8
},
button: {
type: 'rectangle',
width: 120,
height: 40,
fill: '#48bb78',
rx: 20,
ry: 20
},
icon: {
type: 'circle',
radius: 20,
fill: '#ed8936'
}
};
// Apply presets
function addPresetShape(presetName, position) {
const preset = shapePresets[presetName];
const shape = shapeFactory[`create${preset.type.charAt(0).toUpperCase() + preset.type.slice(1)}`]({
...preset,
left: position.x,
top: position.y
});
canvas.add(shape);
}
Add custom functionality to the toolbar:
import { LeftSidebar } from '@rifrocket/fabricjs-design-tool/ui';
function CustomLeftSidebar({ canvas }) {
const handleAddWatermark = () => {
const watermark = new fabric.Text('CONFIDENTIAL', {
left: canvas.width / 2,
top: canvas.height / 2,
fontSize: 60,
fill: 'rgba(255, 0, 0, 0.3)',
angle: -45,
selectable: false
});
canvas.add(watermark);
};
const customTools = [
{
icon: '💧',
label: 'Add Watermark',
onClick: handleAddWatermark
},
{
icon: '📏',
label: 'Add Ruler',
onClick: () => addRulerTool(canvas)
}
];
return (
<LeftSidebar
customTools={customTools}
showDefaultTools={true}
/>
);
}
Add support for additional export formats:
import { canvasUtils } from '@rifrocket/fabricjs-design-tool';
import jsPDF from 'jspdf';
// Extend export functionality
canvasUtils.exportToPDF = function(canvas, options = {}) {
const pdf = new jsPDF({
orientation: options.orientation || 'landscape',
unit: 'px',
format: [canvas.width, canvas.height]
});
const imgData = canvas.toDataURL('image/png');
pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height);
if (options.download) {
pdf.save(options.filename || 'design.pdf');
}
return pdf;
};
// Use custom export
const exportToPDF = () => {
canvasUtils.exportToPDF(canvas, {
filename: 'my-design.pdf',
download: true
});
};
Build reusable plugins for specific functionality:
// Create a grid plugin
const GridPlugin = {
name: 'grid',
version: '1.0.0',
install(canvas, options = {}) {
const gridSize = options.gridSize || 20;
const gridColor = options.gridColor || '#e0e0e0';
canvas.on('after:render', () => {
this.drawGrid(canvas, gridSize, gridColor);
});
return {
toggleGrid: () => this.toggleGrid(canvas),
setGridSize: (size) => this.setGridSize(canvas, size)
};
},
drawGrid(canvas, size, color) {
const ctx = canvas.getContext();
const width = canvas.width;
const height = canvas.height;
ctx.strokeStyle = color;
ctx.lineWidth = 1;
// Draw vertical lines
for (let x = 0; x <= width; x += size) {
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, height);
ctx.stroke();
}
// Draw horizontal lines
for (let y = 0; y <= height; y += size) {
ctx.beginPath();
ctx.moveTo(0, y);
ctx.lineTo(width, y);
ctx.stroke();
}
}
};
// Use the plugin
const gridControls = GridPlugin.install(canvas, {
gridSize: 25,
gridColor: '#f0f0f0'
});
Manage multiple plugins with a plugin manager:
class PluginManager {
constructor(canvas) {
this.canvas = canvas;
this.plugins = new Map();
}
install(plugin, options = {}) {
if (this.plugins.has(plugin.name)) {
console.warn(`Plugin ${plugin.name} is already installed`);
return;
}
const api = plugin.install(this.canvas, options);
this.plugins.set(plugin.name, { plugin, api, options });
return api;
}
uninstall(pluginName) {
const pluginData = this.plugins.get(pluginName);
if (pluginData && pluginData.plugin.uninstall) {
pluginData.plugin.uninstall(this.canvas);
}
this.plugins.delete(pluginName);
}
getPlugin(pluginName) {
const pluginData = this.plugins.get(pluginName);
return pluginData ? pluginData.api : null;
}
}
// Usage
const pluginManager = new PluginManager(canvas);
const gridAPI = pluginManager.install(GridPlugin);
const rulerAPI = pluginManager.install(RulerPlugin);
// Use plugin APIs
gridAPI.toggleGrid();
rulerAPI.showRulers();
Listen to canvas events and add custom behavior:
import { useCanvasManager } from '@rifrocket/fabricjs-design-tool';
function useCustomEvents(canvas) {
useEffect(() => {
if (!canvas) return;
// Custom object selection handler
const handleSelection = (e) => {
const obj = e.target;
console.log('Object selected:', obj.type);
// Add custom behavior
if (obj.type === 'text') {
showTextEditingPanel(obj);
}
};
// Auto-save on modifications
const handleModified = () => {
debounce(() => {
const canvasData = canvas.toJSON();
localStorage.setItem('canvas-backup', JSON.stringify(canvasData));
}, 1000)();
};
canvas.on('selection:created', handleSelection);
canvas.on('object:modified', handleModified);
return () => {
canvas.off('selection:created', handleSelection);
canvas.off('object:modified', handleModified);
};
}, [canvas]);
}
Create custom events for plugin communication:
class EventEmitter {
constructor() {
this.events = {};
}
on(eventName, callback) {
if (!this.events[eventName]) {
this.events[eventName] = [];
}
this.events[eventName].push(callback);
}
emit(eventName, data) {
const callbacks = this.events[eventName];
if (callbacks) {
callbacks.forEach(callback => callback(data));
}
}
off(eventName, callback) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter(cb => cb !== callback);
}
}
}
// Usage
const designToolEvents = new EventEmitter();
// Plugin A listens for events
designToolEvents.on('shape:created', (shapeData) => {
console.log('New shape created:', shapeData);
});
// Plugin B emits events
designToolEvents.emit('shape:created', {
type: 'rectangle',
id: 'rect-1',
timestamp: Date.now()
});