@prahladkuma11/canvas-editor
v1.0.8
Published
Powerful React/Konva canvas editor with floating toolbars, multi-page support, and text effects
Downloads
723
Maintainers
Readme
@prahladkuma11/canvas-editor
A production-ready React component library for building powerful canvas-based design applications. Complete with text effects, multi-page support, floating toolbars, Unsplash image integration, and role-based permissions.
Version: 1.0.7 · License: MIT
🚀 Features
✨ Complete Design Tools
- Text editing with 40+ Google Fonts
- Shape tools (rectangles, circles)
- Image editing with filters (hue, saturation, brightness, sharpness)
- Icon support via Iconify (10,000+ icons)
🎨 Advanced Effects
- Text effects (shadow, highlight, glitch, echo)
- Directional angle controls
- Image filters and adjustments
- Smart alignment guides during drag
📄 Multi-Page Support
- Sides mode (front/back pages)
- Pages mode (unlimited pages)
- Page management with admin controls
- Multiple printable areas per page
🔐 Role-Based Permissions
- Admin vs Custom user roles
- Feature access control
- Permission-based UI rendering
⚡ High Performance
- High-performance Konva rendering
- Lazy font loading
- Optimized asset management
📱 Responsive Design
- Mobile-friendly floating toolbars
- Adaptive UI based on screen size
- Touch-ready controls
🖼️ Unsplash Integration
- Browse and search millions of photos
- Built-in request caching (memory + localStorage)
- Pass your API key via
unsplashApiKeyprop
📦 Installation
npm install @prahladkuma11/canvas-editorRequirements
- React 18+ or 19+
- React DOM 18+ or 19+
The library ships its own CSS. Do not add Tailwind CSS — it is not required.
🎯 Quick Start
Option 1: Use Complete App Component
The simplest way — import the complete, ready-to-use canvas editor:
import React from 'react';
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';
import '@prahladkuma11/canvas-editor/styles';
export default function MyApp() {
return (
<CanvasEditorApp
designStateId="tshirt-design-001"
userRole="admin"
unsplashApiKey={import.meta.env.VITE_UNSPLASH_ACCESS_KEY}
initialState={{
settings: { showGrids: false },
}}
onChange={(state) => {
// Live state updates
console.log('Editor changed', state);
}}
onSave={(state) => {
// Save snapshot from Export button
localStorage.setItem('canvas-template', JSON.stringify(state));
}}
/>
);
}This gives you a full-featured editor with all tools, panels, and controls.
Option 2: Build Custom Layout
For more control, use individual components:
import React from 'react';
import {
CanvasEditorProvider,
CanvasSurface,
LeftPanel,
RightPanel,
} from '@prahladkuma11/canvas-editor';
import '@prahladkuma11/canvas-editor/styles';
export default function CustomEditor() {
return (
<CanvasEditorProvider>
<div style={{ display: 'flex', height: '100vh', width: '100vw' }}>
<LeftPanel />
<CanvasSurface />
<RightPanel />
</div>
</CanvasEditorProvider>
);
}🔌 Core API
Props
| Prop | Type | Description |
|---|---|---|
| userRole | 'admin' \| 'custom' | Actual user role. Custom users are restricted to custom view only. |
| role | 'admin' \| 'custom' | Optional preview/view role. Admin users can switch between admin and custom views. |
| unsplashApiKey | string | Your Unsplash API access key. Pass this to enable the image browser in the Graphics and Background panels. |
| designStateId | string | Optional design identifier for automatic localStorage persistence. If local data exists for this ID and initialState is also provided, the editor prompts users to choose between local data and incoming design data. |
| initialState | Partial<CanvasState> \| CanvasState | Optional initial state to pre-populate the canvas. Missing fields are filled from library defaults (including nested settings/pan and partial objects/pages). |
| onChange | (state: CanvasState) => void | Optional callback fired whenever canvas state changes. |
| onSave | (state: CanvasState) => void | Optional callback fired when save is triggered (for example from Export button). |
Design ID Persistence Example
Use designStateId when you want autosave/restore by design key:
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';
export default function HostApp() {
const designId = 'order-7843-front';
const incomingDesign = {
settings: { showGrids: false },
};
return (
<CanvasEditorApp
userRole="admin"
designStateId={designId}
initialState={incomingDesign}
onSave={(state) => {
// Optional: persist exported JSON to your backend
console.log('Saved state', state);
}}
/>
);
}Behavior:
- If localStorage has no data for this ID,
initialStateis used. - If localStorage has data for this ID and
initialStateis passed, users are prompted to choose which one to load. - State changes are auto-saved to localStorage under this ID.
Admin Template + Custom Load Example
Save a template JSON as admin, then load it for custom users via initialState:
import { useState } from 'react';
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';
import '@prahladkuma11/canvas-editor/styles';
export default function App() {
const [savedTemplate, setSavedTemplate] = useState(() => {
const raw = localStorage.getItem('canvas-template');
return raw ? JSON.parse(raw) : undefined;
});
return (
<>
<CanvasEditorApp
userRole="admin"
initialState={savedTemplate}
onSave={(state) => {
const json = JSON.stringify(state);
localStorage.setItem('canvas-template', json);
setSavedTemplate(state);
}}
/>
<CanvasEditorApp
userRole="custom"
initialState={savedTemplate}
/>
</>
);
}useCanvasEditor
Access canvas state and dispatch actions:
import { useCanvasEditor } from '@prahladkuma11/canvas-editor';
function MyComponent() {
const { state, dispatch, triggerSave } = useCanvasEditor();
// Add text object
const addText = () => {
dispatch({
type: 'ADD_OBJECT',
payload: {
type: 'text',
text: 'Hello Canvas',
x: 100,
y: 100,
fontSize: 24,
fontFamily: 'Poppins',
},
});
};
// Update object
const updateText = () => {
dispatch({
type: 'UPDATE_OBJECT',
payload: {
id: 'object-id',
updates: {
text: 'Updated Text',
fontSize: 32,
},
},
});
};
// Delete object
const deleteObject = (id) => {
dispatch({
type: 'DELETE_OBJECTS',
payload: [id],
});
};
const exportJson = () => {
console.log(JSON.stringify(state, null, 2));
triggerSave();
};
return (
<div>
<button onClick={addText}>Add Text</button>
<button onClick={updateText}>Update Text</button>
<button onClick={() => deleteObject('object-1')}>Delete</button>
<button onClick={exportJson}>Export</button>
</div>
);
}Available Actions
// Object management
dispatch({ type: 'ADD_OBJECT', payload: { type, x, y, ... } });
dispatch({ type: 'UPDATE_OBJECT', payload: { id, updates: { ...props } } });
dispatch({ type: 'DELETE_OBJECTS', payload: [objectId] });
dispatch({ type: 'SET_SELECTION', payload: [objectId] });
dispatch({ type: 'SET_EDITING_ID', payload: objectId });
// Canvas controls
dispatch({ type: 'SET_ZOOM', payload: zoomLevel });
dispatch({ type: 'SET_PAN', payload: { x, y } });
dispatch({ type: 'SET_TOOL', payload: 'select' | 'pan' | 'text' | 'rect' | 'circle' | 'image' });
// Page management
dispatch({ type: 'SET_PAGE_MODE', payload: 'sides' | 'pages' });
dispatch({ type: 'ADD_PAGE', payload: { name: 'Page Name', ... } });
dispatch({ type: 'DELETE_PAGE', payload: pageId });
dispatch({ type: 'SET_CURRENT_PAGE', payload: pageId });
dispatch({ type: 'SET_SIDE_ENABLED', payload: { side: 'front' | 'back', enabled: true } });
// Background/printable area
dispatch({ type: 'SET_BACKGROUND_IMAGE', payload: { image: imageDataUrl } });
dispatch({ type: 'SET_BACKGROUND_COLOR', payload: { color: '#ffffff' } });
dispatch({ type: 'TOGGLE_EDIT_PRINTABLE_AREA' });
dispatch({ type: 'ADD_PRINTABLE_AREA' });
dispatch({ type: 'DELETE_PRINTABLE_AREA', payload: printableAreaId });
dispatch({ type: 'SET_ACTIVE_PRINTABLE_AREA', payload: printableAreaId });
dispatch({ type: 'UPDATE_PRINTABLE_AREA', payload: { id: printableAreaId, x: 120, y: 80 } });
// Role & permissions
dispatch({ type: 'SET_ROLE', payload: 'admin' | 'custom' });
dispatch({ type: 'SET_USER_ROLE', payload: 'admin' | 'custom' });
// Settings
dispatch({ type: 'UPDATE_SETTINGS', payload: { showRulers: true, ... } });📦 Components
Main Components
CanvasSurface
Main canvas rendering area with interactive object selection and manipulation.
import { CanvasSurface } from '@prahladkuma11/canvas-editor';
<CanvasSurface />LeftPanel
Sidebar with page/side selection and tools.
import { LeftPanel } from '@prahladkuma11/canvas-editor';
<LeftPanel />RightPanel
Properties panel for editing selected object properties.
import { RightPanel } from '@prahladkuma11/canvas-editor';
<RightPanel />ProductSidePanel
Floating side panel with secondary tools.
import { ProductSidePanel } from '@prahladkuma11/canvas-editor';
<ProductSidePanel />Floating Toolbars
FloatingObjectToolbar
Appears when hovering over objects for quick manipulation.
import { FloatingObjectToolbar } from '@prahladkuma11/canvas-editor';
<FloatingObjectToolbar />TextToolbar
Floating toolbar for text formatting (font, size, effects, alignment).
import { TextToolbar } from '@prahladkuma11/canvas-editor';
<TextToolbar />ShapeToolbar
Toolbar for shape properties (fill, stroke, rotation).
import { ShapeToolbar } from '@prahladkuma11/canvas-editor';
<ShapeToolbar />Secondary Panels
SecondaryPanel
Tab-based panel container for advanced controls.
import { SecondaryPanel } from '@prahladkuma11/canvas-editor';
<SecondaryPanel />Available sub-panels:
TextPanel- Text-specific controlsGraphicsPanel- Shape and image toolsBackgroundPanel- Page background settingsLayersPanel- Object layer managementMaterialPanel- Color and material presetsObjectsPanel- Object listing and selectionUploadsPanel- Asset management
💾 Usage Examples
Example 1: Add Text with Effects
import { useCanvasEditor } from '@prahladkuma11/canvas-editor';
function AddEffectText() {
const { dispatch } = useCanvasEditor();
const addGlitchText = () => {
dispatch({
type: 'ADD_OBJECT',
payload: {
type: 'text',
text: 'Glitch Effect',
x: 100,
y: 100,
fontSize: 48,
fontFamily: 'Bebas Neue',
textEffect: 'glitch',
effectColor: '#ff0000',
effectDistance: 3,
effectAngle: 45,
},
});
};
return <button onClick={addGlitchText}>Add Glitch Text</button>;
}Example 2: Work with Images
function ImageEditor() {
const { dispatch } = useCanvasEditor();
const addImage = (imageUrl) => {
dispatch({
type: 'ADD_OBJECT',
payload: {
type: 'image',
src: imageUrl,
x: 50,
y: 50,
width: 300,
height: 300,
},
});
};
const applyFilters = (imageId) => {
dispatch({
type: 'UPDATE_OBJECT',
payload: {
id: imageId,
updates: {
imageAdjustHue: 25,
imageAdjustSaturation: 1.3,
imageAdjustBrightness: 1.1,
imageSharpness: 1.5,
},
},
});
};
return (
<>
<button onClick={() => addImage('https://example.com/image.jpg')}>
Add Image
</button>
<button onClick={() => applyFilters('image-id')}>Apply Filters</button>
</>
);
}Example 3: Multi-Page Management
function PageManager() {
const { state, dispatch } = useCanvasEditor();
const switchToPages = () => {
dispatch({ type: 'SET_PAGE_MODE', payload: 'pages' });
};
const addNewPage = () => {
dispatch({
type: 'ADD_PAGE',
payload: { name: 'Page ' + (state.pages.length + 1) },
});
};
const selectPage = (pageId) => {
dispatch({ type: 'SET_CURRENT_PAGE', payload: pageId });
};
return (
<div>
<button onClick={switchToPages}>Switch to Pages Mode</button>
<button onClick={addNewPage}>Add Page</button>
<div>
{state.pages.map((page) => (
<button
key={page.id}
onClick={() => selectPage(page.id)}
style={{
fontWeight: page.id === state.currentPageId ? 'bold' : 'normal',
}}
>
{page.name}
</button>
))}
</div>
</div>
);
}Example 4: Role-Based Features
function RoleToggle() {
const { state, dispatch } = useCanvasEditor();
const toggleRole = () => {
const newRole = state.role === 'admin' ? 'custom' : 'admin';
dispatch({ type: 'SET_ROLE', payload: newRole });
};
return (
<div>
<p>Current Role: {state.role}</p>
<button onClick={toggleRole}>Switch Role</button>
{state.role === 'admin' && (
<p>✅ Admin can add/remove pages and customize layouts</p>
)}
{state.role === 'custom' && (
<p>⚠️ Custom users have limited permissions</p>
)}
</div>
);
}🎨 Fonts
The editor includes 40+ Google Fonts organized by category:
Sans-serif: Inter, Roboto, Poppins, Montserrat, Open Sans, Lato, Ubuntu, Source Sans Pro, Raleway, Nunito
Serif: Playfair Display, Merriweather, PT Serif, Crimson Text, Lora, Cormorant
Monospace: JetBrains Mono, Roboto Mono, IBM Plex Mono
Display: Bebas Neue, Oswald, Barlow Condensed, Fredoka
Handwriting: Caveat, Pacifico, Permanent Marker
Fonts are loaded dynamically on-demand via WebFont Loader.
import { TEXT_FONT_OPTIONS, ensureFontLoaded } from '@prahladkuma11/canvas-editor';
// List all available fonts
console.log(TEXT_FONT_OPTIONS);
// Ensure a font is loaded before using it
await ensureFontLoaded('Poppins');🖼️ Unsplash Integration
The Graphics and Background panels can browse and search Unsplash photos. To enable them, pass your Unsplash API access key via the unsplashApiKey prop:
import { CanvasEditorProvider } from '@prahladkuma11/canvas-editor';
<CanvasEditorProvider unsplashApiKey={process.env.REACT_APP_UNSPLASH_KEY}>
...
</CanvasEditorProvider>Or set the key programmatically (e.g. after fetching from your backend):
import { setUnsplashApiKey } from '@prahladkuma11/canvas-editor';
setUnsplashApiKey('your_access_key');To check if a key is currently set:
import { hasUnsplashApiKey } from '@prahladkuma11/canvas-editor';
if (hasUnsplashApiKey()) {
// images are available
}If no key is provided, the image panels display an informational banner instead of broken UI.
Results are cached in memory and localStorage to minimize API calls.
🎯 Type Definitions
Full TypeScript support with exported types:
import {
CanvasState,
CanvasObject,
Page,
Role,
ToolType,
PageMode,
CanvasEditorContextType,
CanvasAction,
} from '@prahladkuma11/canvas-editor';
interface CanvasState {
objects: CanvasObject[];
selectedObjectId: string | null;
activeTool: ToolType;
zoom: number;
pan: { x: number; y: number };
pages: Page[];
selectedPageId: string;
role: Role;
settings: {
showRulers: boolean;
showGrids: boolean;
};
}
interface CanvasObject {
id: string;
type: 'text' | 'rect' | 'circle' | 'image';
x: number;
y: number;
width?: number;
height?: number;
// ... many more properties
}
type Role = 'admin' | 'custom';
type ToolType = 'select' | 'pan' | 'text' | 'rect' | 'circle' | 'image';
type PageMode = 'sides' | 'pages';🎨 Styling
The library ships its own standalone CSS — no Tailwind CSS required. Simply import the stylesheet once at the top level of your app:
import '@prahladkuma11/canvas-editor/styles';This maps to dist/canvas-editor.css in the package. The stylesheet is self-contained and will not conflict with your existing CSS framework or reset.
🎮 Keyboard Shortcuts
- Delete - Delete selected object
- Escape - Deselect
- Arrow Keys - Move selected object
- Shift + Arrow Keys - Fine-tune position
- V - Select tool
- H - Pan tool
⚡ Performance Tips
- Lazy Font Loading - Fonts load on-demand when used
- Asset Optimization - Compress images before uploading
- Layer Management - Use the Layers panel to organize objects
- Page Structure - Keep pages focused on specific content
- Zoom Levels - Use preset zoom levels (25%, 50%, 100%, 200%, etc.)
🌍 Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Mobile browsers (iOS Safari, Chrome Mobile)
📱 Responsive Behavior
The library automatically adapts to different screen sizes:
- Desktop (1920px+): Full UI with all panels
- Tablet (768px-1920px): Optimized layout with hidden panels
- Mobile (<768px): Icon-only toolbars and collapsible panels
🔒 Permissions & Roles
Admin Role
- Add/remove pages
- Customize layouts
- Set canvas background
- Adjust printable areas
- Export canvas
Custom Role
- Edit objects
- Add text/shapes/images
- Apply effects
- Limited feature access
📊 State Management
The editor uses React Context + useReducer for state management:
const { state, dispatch } = useCanvasEditor();
// state: Current canvas state
// dispatch: Send actions to update stateNo external state management required!
📦 Exports Reference
// Complete app
import { CanvasEditorApp } from '@prahladkuma11/canvas-editor';
// Core
import { CanvasEditorProvider, useCanvasEditor } from '@prahladkuma11/canvas-editor';
// Components
import {
CanvasSurface, LeftPanel, RightPanel, SecondaryPanel,
FloatingObjectToolbar, TextToolbar, ShapeToolbar,
} from '@prahladkuma11/canvas-editor';
// Unsplash utilities
import { setUnsplashApiKey, hasUnsplashApiKey } from '@prahladkuma11/canvas-editor';
// Font utilities
import { ensureFontLoaded, TEXT_FONT_OPTIONS } from '@prahladkuma11/canvas-editor';
// Types
import type { CanvasEditorAppProps, CanvasState, CanvasObject, Page, Role, ToolType } from '@prahladkuma11/canvas-editor';🚀 Distribution
The package exports both ES Module and CommonJS bundles plus a stylesheet:
{
"exports": {
".": {
"import": "./dist/canvas-editor.mjs",
"require": "./dist/canvas-editor.cjs"
},
"./styles": "./dist/canvas-editor.css"
}
}Usage in different environments
// ES Module / TypeScript
import { CanvasEditorApp, useCanvasEditor } from '@prahladkuma11/canvas-editor';
// CommonJS
const { CanvasEditorApp } = require('@prahladkuma11/canvas-editor');🤝 Contributing
Contributions are welcome! Please fork the repository and submit a pull request.
📄 License
MIT License — See LICENSE file for details
Built with ❤️ using React, Konva, and TypeScript
