Large format & high-resolution exports
Large-format printing needs resolutions browsers often cannot render. This guide covers print-grade output with Ydesign (@ydesign/react-editor / @ydesign/core) for banners, posters, and similar canvases.
Quick start
- Choose a path: client (desktop, max side ≤ 8k px) or server / vector (max side > 8k, or mobile).
- Client path: set
multiplierto 2–4 and verify on the target device. - Print check: each source image edge must satisfy “physical mm × target DPI ÷ 25.4”.
- Vector options:
- Client: export SVG / HTML (resolution-independent; feature support varies).
- Server: Cloud Render API or the planned
@ydesign/pdf-exportfor vector PDF.
- Swap in high-res assets only right before export; keep previews in the editor.
The challenge
Large designs often need pixels the browser cannot allocate. For example:
- 1.5 m × 3 m banner @ 200 DPI (1500 × 3000 mm) ≈ 11,811 × 23,622 px
- 3 m × 3 m banner @ 200 DPI (3000 × 3000 mm) ≈ 23,622 × 23,622 px
Browsers cap canvas size by browser / OS / hardware — typically about 4,000–16,000 px per side. Ydesign does not add an artificial export ceiling; failures come from the browser and hardware, not the SDK.
Browser render limits
Factors include:
- Browser: Chrome, Firefox, Safari differ
- OS: macOS / Windows / Linux memory policies differ
- Hardware: GPU VRAM and available RAM set the real ceiling
- Rule of thumb: ~4,000–16,000 px per side
Beyond that you may see:
- Failed canvas renders
- Tab / browser crashes
- Incomplete, fully transparent, or corrupted exports
Choosing a strategy
Pick by scenario, audience, and design complexity.
Decision cheat sheet
- Client (desktop, max side ≤ 8k): realtime and fast;
multiplier2–4. - Server / vector (> 8k, mobile, or critical print): Cloud Render API,
@ydesign/core/node(planned), or SVG / HTML where supported. - Hybrid: probe device + complexity; fall back to server / vector near the limit.
Client rendering
Good for:
- Max side ~≤ 8,000 px
- High-end desktops
- Smaller print jobs (cards, flyers, A-series posters)
- Live preview and instant export
Limits:
- Mobile / low-end devices fail easily
- Huge exports use a lot of memory
- Larger canvases take longer
Server rendering
Good for:
- Max side above ~8,000 px
- Mobile / low-end users
- Cross-device consistency
- Batch / automation pipelines
- Vector PDF (independent of pixel caps)
Options:
- Cloud Render API — hosted, no ops
@ydesign/core/node(planned) — self-hosted Node, data stays in your network
Hybrid
Route exports by device capability:
const isHighEndDevice =
(navigator.hardwareConcurrency ?? 0) >= 8 &&
// @ts-expect-error deviceMemory is not everywhere
(navigator.deviceMemory ?? 0) >= 8;
const exportDesign = async (store, multiplier = 2) => {
const maxSide = Math.max(store.width, store.height) * multiplier;
if (maxSide > 8000 || !isHighEndDevice) {
return await exportViaCloudAPI(store);
}
return await store.saveAsImage({ multiplier });
};
Client HD export
Use multiplier to scale export pixels without changing the editor canvas size. Higher values → larger, sharper output.
Understanding multiplier
multiplier scales canvas width/height for the export:
// Canvas: 1200 × 1200 px
// multiplier: 2 → 2400 × 2400 px
// multiplier: 4 → 4800 × 4800 px
await store.saveAsImage({ multiplier: 2 });
Note: multiplier controls render pixels (sharpness); dpi / unit on store drive rulers and physical-size math. See Units.
Practical ceiling
Start HD at multiplier: 2; for large format try 4 or 8, and always measure on the target device:
import { unitToPx } from '@ydesign/react-editor/utils/unit';
// Target ~1.2 m × 1.2 m @ 200 DPI ≈ 9,449 × 9,449 px
// Keep the editor canvas interactive, e.g. 1,200 × 1,200
store.setSize({ width: 1200, height: 1200 });
// Export at ~8× toward the target pixels
await store.saveAsImage({ multiplier: 8 });
Warning: Very high multiplier (e.g. 8+) can crash some devices. Prefer server rendering for critical flows.
Full client example
import { unitToPx } from '@ydesign/react-editor/utils/unit';
// Target: ~1.2 m × 1.2 m (1200 × 1200 mm) @ 200 DPI
const widthMm = 1200;
const heightMm = 1200;
const targetDPI = 200;
const editorDPI = 72; // common screen baseline in the editor
// Editor pixels at 72 DPI for interactivity
const editorWidth = unitToPx({ unitVal: widthMm, unit: 'mm', dpi: editorDPI });
const editorHeight = unitToPx({ unitVal: heightMm, unit: 'mm', dpi: editorDPI });
// ≈ 3402 × 3402 px
store.setSize({ width: editorWidth, height: editorHeight });
store.setUnit({ unit: 'mm', dpi: editorDPI });
// Export sharpness: 200 / 72 ≈ 2.78
const multiplier = targetDPI / editorDPI;
await store.saveAsImage({
multiplier,
format: 'png',
fileName: 'banner.png',
});
Scaled editing (rulers & DPI)
Keep a smaller canvas for fluidity while rulers still show real physical size (mm):
import { unitToPx } from '@ydesign/react-editor/utils/unit';
// Target: 3 m × 3 m (3000 × 3000 mm), edit at 1:10
const scale = 0.1;
const targetMm = 3000;
const editorDPI = 72;
// Proxy canvas: 300 mm × 300 mm → ~850 × 850 px
const editorPx = unitToPx({
unitVal: targetMm * scale,
unit: 'mm',
dpi: editorDPI,
});
store.setSize({ width: editorPx, height: editorPx });
// Ruler reads 3000 mm (canvas is actually 300 mm)
store.setUnit({
unit: 'mm',
dpi: editorDPI * scale, // 7.2; px × 25.4 / 7.2 ≈ 3000 mm
});
On export, use the real target DPI (e.g. 200) with multiplier, or go server / vector. More in Units.
Raster PDF: page size vs sharpness
Client PDF (see PDF export) is usually a raster PDF (each page is a bitmap embedded in PDF). Separate two ideas:
- Physical page size: from canvas pixels and
dpi(when UI usesmm: ( mm = px \times 25.4 / dpi )) - Render sharpness: from
multiplier— effective DPI ≈dpi × multiplier(conceptually)
Image export example:
await store.saveAsImage({
fileName: 'design.png',
multiplier: 2,
});
Full raster PDF options, bleed, and crop marks: PDF export. For large format at very high multiplier, prefer server or vector PDF.
Asset swap strategy
Use low-res previews while editing; swap to high-res originals only before export.
Store high-res URLs in custom fields
// When adding an image, keep both preview and high-res
element.set({
src: 'https://example.com/preview-800px.jpg',
// Fabric / Ydesign custom field — serializes with toJSON
// (name is up to you: keyValues / custom / …)
custom: {
highResSrc: 'https://example.com/original-5000px.jpg',
},
});
Swap before export
import type { StoreType } from '@ydesign/react-editor/model/store';
const swapHighResAssets = async (store: StoreType) => {
const canvas = store.editor?.canvas;
if (!canvas) return () => {};
const originals: { obj: any; src: string }[] = [];
for (const obj of canvas.getObjects()) {
const highRes = obj.custom?.highResSrc;
if (obj.type === 'image' && highRes) {
originals.push({ obj, src: obj.getSrc?.() ?? obj.src });
await obj.setSrc(highRes);
obj.setCoords();
}
}
canvas.requestRenderAll();
return () => {
originals.forEach(({ obj, src }) => {
void obj.setSrc(src);
});
canvas.requestRenderAll();
};
};
const restore = await swapHighResAssets(store);
try {
await store.saveAsImage({ multiplier: 4, format: 'png' });
} finally {
restore();
}
In production, wrap swap/restore in a helper and wait for async
setSrcloads before exporting.
Validate image resolution
Use getImageSize:
import { getImageSize } from '@ydesign/react-editor/utils/image';
const validateImageResolution = async (src: string, targetDPI: number, widthMm: number, heightMm: number) => {
const requiredWidth = (widthMm * targetDPI) / 25.4;
const requiredHeight = (heightMm * targetDPI) / 25.4;
const { width, height } = await getImageSize(src);
const ok = width >= requiredWidth && height >= requiredHeight;
if (!ok) {
console.warn(
`Image (${width}×${height}) may be too small for ` +
`${widthMm}×${heightMm} mm @ ${targetDPI} DPI ` +
`(need ~${Math.ceil(requiredWidth)}×${Math.ceil(requiredHeight)} px)`
);
}
return ok;
};
Vector PDF export
Vector PDF ignores pixel caps. Use hosted cloud render or a self-hosted Node package.
Cloud Render API
Hosted endpoints today are mainly raster (format: jpeg | png | webp + multiplier). Full request shape: Cloud Render API.
const json = store.toJSON();
const res = await fetch('https://api.ydesign.com/api/render/image', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer YOUR_API_KEY',
},
body: JSON.stringify({
json,
format: 'png',
multiplier: 4,
fonts: [
/* custom font URLs used in the design */
],
}),
});
const { url } = await res.json();
Vector PDF (format: 'pdf', vector: true) is planned and will align with cloud render / @ydesign/pdf-export. Until then, prefer high-multiplier raster cloud render or self-hosted Node for large format.
Node packages (@ydesign/pdf-export / @ydesign/core/node)
// Planned vector PDF API shape
import { jsonToPDF } from '@ydesign/pdf-export';
const json = store.toJSON();
await jsonToPDF(json, './output.pdf');
Self-hosted headless: Server-side image generation. Client raster PDF: PDF export.
SVG / HTML alternatives
For resolution-independent intermediates, export SVG / HTML (element support varies; complex filters may not be 1:1). For pixel-perfect delivery, stick to PNG / JPEG or PDF. Overview: Export & Import.
Example: 3 m × 3 m banner (3000 × 3000 mm @ 200 DPI)
Recommended (server):
- Submit
store.toJSON()to the Cloud Render API and raisemultiplieras needed. - Source images need ~23,622 px per side ((3000 \times 200 / 25.4)) to stay sharp at 200 DPI.
Client trial (powerful desktops only):
- Edit on a small canvas (e.g. 1,200 × 1,200) and export at a very high
multiplier. Expect instability — production should use the server path.
Best practices
Editor performance
- Prefer small previews in the editor
- Keep high-res URLs in custom fields
- Swap to high-res only at export time
- Smaller source previews → smoother editing
Asset management
- Store high-res URLs separately — don’t load originals in the editor
- Validate resolution against target DPI and physical size (mm)
- Warn early when uploads are below print sharpness
- Serve high-res via CDN
Export strategy
- Measure client export on the target device
- Raise timeouts — huge server jobs can take minutes
- Watch memory — large exports are RAM-heavy
- Show progress — loading / polling for long jobs
Troubleshooting
Canvas exceeds browser limits
Symptoms: failed export, crash, or corrupted output.
Fixes:
- Lower canvas size or
multiplier - Switch to server (cloud or Node)
- Use vector PDF / SVG when possible
Server timeouts
Symptoms: request times out before completion.
Fixes:
- Raise HTTP timeout (5+ minutes for huge jobs)
- Prefer async jobs + poll / Webhook over long sync requests
- Simplify the design
- Use Cloud Render API hosted queues
Related docs
- PDF export — client raster PDF & selection guide
- Cloud Render API — hosted server render
- Server-side image generation — self-hosted Node
- Units — DPI,
mm, andmultiplier - Export & Import overview
- Scenes & I/O —
toDataURL/toBlob/saveAsImage