Workspace
<Workspace /> is the React component that hosts the Fabric canvas inside the Ydesign editor — the area where users actually draw and interact with the design. Its responsibilities are intentionally narrow:
- On mount, spin up an
@ydesign/coreEditorbound to the store - Insert the Fabric canvas into the container and watch for size changes
- Render the transparent-background checkerboard and the inner shadow
- On unmount, destroy the
Editorand fully clean up Fabric's events and DOM
All business behavior (zoom, selection, layers, crop, import/export, …) is exposed through the Store API and the @ydesign/core handlers, not through this component's props.
Basic usage
import { createStore } from '@ydesign/react-editor';
import Workspace from '@ydesign/react-editor/canvas/workspace';
const store = createStore({
key: 'YOUR_API_KEY',
token: 'user-token',
});
export default function App() {
return (
<div style={{ height: '100vh' }}>
<Workspace store={store} />
</div>
);
}
<Workspace /> automatically stretches to fill its parent. You don't need to set its size manually — just make sure the parent has an explicit width / height (or flex: 1).
⚠️ Workspace mounts its Fabric canvas at
id="canvas", so there can be only one<Workspace />instance per page today. For "multi-canvas comparison" scenarios, swap via routing or conditional rendering.
Initial configuration
Workspace pulls its initial size and workarea defaults from store:
const store = createStore({ key: 'YOUR_API_KEY' });
store.width; // => 1080 (default)
store.height; // => 1080 (default)
store.backgroundColor; // => '#fff'
store.unit; // => 'px'
store.dpi; // => 72
To change the defaults before Workspace mounts, call the corresponding actions right after createStore:
const store = createStore({ key: 'YOUR_API_KEY' });
store.setSize({ width: 1200, height: 800 });
store.setUnit({ unit: 'mm', dpi: 300 });
store.setBackgroundColor('#f0f0f0');
// ...then mount <Workspace store={store} />
Resize the canvas
store.setSize({ width: 1600, height: 900 });
// Same thing, directly on the handler
store.editor?.workareaHandler.setSize({ width: 1600, height: 900 });
A resize triggers:
workareaHandlerrebuilds the workarea rectzoomHandler.auto()fits the canvas to screen- A
workarea:changedevent syncsstore.width/store.height - A history entry is pushed (so the resize is undoable)
Unit conversion
store.setUnit({ unit: 'mm', dpi: 300 });
// 1080px @ 72dpi → 381mm @ 72dpi → 4500px @ 300dpi
See Store overview · canvas size / unit / background.
Change the background
// Solid
store.setBackgroundColor('#f5f5f5');
// Transparent (a checkerboard is shown automatically)
store.setBackgroundColor('rgba(255, 255, 255, 0)');
store.setBackgroundColor('transparent');
// Linear gradient
store.setBackgroundColor({
type: 'linear',
coords: { x1: 0, y1: 0, x2: 1, y2: 1 },
colorStops: [
{ offset: 0, color: '#ff6a00' },
{ offset: 1, color: '#2253eb' },
],
});
When the background color has alpha (rgba(..., <1) or transparent), Workspace automatically renders a checkerboard beneath the workarea as a visual hint:
┌─────────────────────────┐
│ ▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦ │
│ ▦▦▦▦▦ workarea ▦▦▦▦▦▦▦▦ │
│ ▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦▦ │
└─────────────────────────┘
Zoom & viewport
Zoom is handled by ZoomHandler, not by Workspace props:
const zoom = store.editor?.zoomHandler;
zoom?.zoomIn(); // one step in
zoom?.zoomOut(); // one step out
zoom?.zoomToOne(); // 100%
zoom?.zoomToFit(); // fit to screen
zoom?.zoomToRatio(1.5); // 150%
zoom?.centerViewport(); // reset pan
// Current zoom is mirrored on store
console.log(store.scale);
Ydesign ships a <ZoomButtons /> component (bottom-right). If you want custom zoom UI, just call the handler methods above.
Rulers · bleed · guidelines
Rulers
store.rulesVisible = true;
Bleed (for print)
const workarea = store.editor?.workareaHandler;
workarea?.setBleed?.(3); // 3mm bleed
store.bleedVisible = true; // show red bleed lines
Smart guidelines / snapping
Enabled by default, managed by GuidelinesHandler:
store.editor?.guidelinesHandler.enable();
store.editor?.guidelinesHandler.disable();
Dragging an object shows center / equidistant / alignment guides automatically.
Keyboard shortcuts
Workspace ships a built-in set of shortcuts (registered in @ydesign/core's HotkeyHandler):
| Key | Action |
|---|---|
Ctrl/⌘ + Z | Undo |
Ctrl/⌘ + Shift + Z | Redo |
Ctrl/⌘ + C / V / X | Copy / paste / cut |
Ctrl/⌘ + D | Duplicate |
Ctrl/⌘ + A | Select all |
Ctrl/⌘ + G / Shift + G | Group / ungroup |
Delete / Backspace | Delete selection |
Arrow | Nudge by 1px |
Shift + Arrow | Nudge by 10px |
Ctrl/⌘ + = / - / 0 | Zoom in / out / reset |
Custom shortcuts
Register your own via HotkeyHandler.bind:
store.editor?.hotkeyHandler.bind({
'ctrl+s, command+s': e => {
e.preventDefault();
saveToServer();
},
'ctrl+shift+d, command+shift+d': () => {
store.editor?.objectsHandler.duplicate();
},
});
Your bindings are added on top of the built-in ones. To truly replace a default behavior, call the corresponding handler method inside your callback.
Context menu
A canvas context menu (<ContextMenu />) is coming soon — see ContextMenu (planned).
Until it ships, if you need a right-click menu today, listen for contextmenu on the container, call event.preventDefault(), and render your own menu whose items call store.editor.xxxHandler. A full example lives in the context-menu doc's "Workarounds" section.
Container styling
Workspace's root element has a stable id and default light/dark background:
<div id="canvas_container" className="bg-[#ecf0f1] dark:bg-[#92969d]">
{/* inner canvas */}
</div>
Override the background via global CSS:
#canvas_container {
background: #1e1e1e !important;
}
#canvas_container .inside-shadow {
box-shadow: inset 0 0 40px rgba(0, 0, 0, 0.15);
}
To change the Tailwind class itself, override with [data-theme="dark"] or wrap Workspace in your own shell.
Unmount & cleanup
On unmount, <Workspace /> takes care of:
- Removing the
after:renderlistener from the Fabric canvas - Calling
editor.destroy()to tear down all handlers and DOM
So just let React unmount the component normally (via routing or {show && <Workspace />}). No manual cleanup is required.
When Workspace is mounted through createDesignEditorApp, unmount via the React root:
const { root } = createDesignEditorApp({ container, key: 'YOUR_API_KEY' });
// Unmount
root.unmount();
Debugging: access the underlying Editor
For convenience during development, Workspace attaches the Editor instance to window._c:
// Browser console
window._c.zoomHandler.zoomToFit();
window._c.objectsHandler.clear();
window._c.historyHandler.getStatus();
⚠️ Development-only backdoor. Don't rely on
window._cin production code — usestore.editor.xxxHandlerinstead.
Differences from Polotno's Workspace
If you've used <Workspace /> from Polotno, keep these differences in mind:
| Feature | Polotno | Ydesign |
|---|---|---|
components.PageControls (per-page controls) | ✅ | ❌ (no Page concept — see Scenes) |
components.NoPages (empty state UI) | ✅ | ❌ (single-canvas model doesn't need it) |
backgroundColor / pageBorderColor / paddingX / paddingY props | ✅ | ❌ (use CSS or store.setBackgroundColor) |
onKeyDown custom key handler | ✅ | Use store.editor.hotkeyHandler.bind({ ... }) instead |
setTransformerStyle / setHighlighterStyle (Konva) | ✅ | ❌ (Fabric-based; configure via Fabric selection styles) |
| Canvas context menu | ✅ | 🚧 planned (see ContextMenu) |
| Auto checkerboard for transparent background | — | ✅ built-in |
The underlying engine is different: Polotno uses Konva, Ydesign uses Fabric.js v6. Their coordinate systems and event models differ — APIs don't map 1:1.
Next
- 👉 Toolbar
- 👉 Tooltip (planned)
- 👉 Store overview — control the canvas via the store
- 👉 Elements
- 👉 Scenes & import/export
- 👉 Customizations · canvas behavior