Skip to main content

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 multiplier to 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-export for 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; multiplier 2–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:

  1. Cloud Render API — hosted, no ops
  2. @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 uses mm: ( 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 setSrc loads 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 raise multiplier as 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