Skip to main content

Size Panel (Canvas Size)

The default SizePanel provides common size presets (Instagram / poster / business card / A4) and a custom width/height input.

Two common customizations:

  1. Change the preset list (your company's standard spec sheet)
  2. Replace the UI completely (group by business, show thumbnails)

Scenario 1: custom size list

Override the default size Section with your own:

import { observer } from 'mobx-react-lite';
import { Button } from 'antd';
import { ImageUpscale } from 'lucide-react';
import {
SectionTab,
DEFAULT_SECTIONS,
SidePanel,
} from '@ydesign/react-editor/side-panel';
import type { Section } from '@ydesign/react-editor/side-panel';

// Your preset sizes (in pixels)
const AVAILABLE_SIZES = [
{ label: 'Square · 1000 × 1000', width: 1000, height: 1000 },
{ label: 'Vertical · 750 × 1334', width: 750, height: 1334 },
{ label: 'Landscape · 1920 × 1080', width: 1920, height: 1080 },
{ label: 'Print · A4 (300dpi)', width: 2480, height: 3508 },
{ label: 'Business card · 90×54mm', width: 1063, height: 638 },
];

const CustomSizeSection: Section = {
name: 'size', // 👈 override the built-in
Tab: observer(props => (
<SectionTab name="Canvas size" {...props}>
<ImageUpscale size={20} />
</SectionTab>
)),
Panel: observer(({ store }) => (
<div style={{ display: 'flex', flexDirection: 'column', gap: 8, padding: 4 }}>
{AVAILABLE_SIZES.map(({ label, width, height }) => {
const active = store.width === width && store.height === height;
return (
<Button
key={label}
block
type={active ? 'primary' : 'default'}
onClick={() => store.setSize({ width, height })}
>
{label}
</Button>
);
})}
</div>
)),
};

const sections = DEFAULT_SECTIONS.map(s =>
s.name === 'size' ? CustomSizeSection : s
);

<SidePanel store={store} sections={sections} />;

Key points:

  • store.setSize({ width, height }) triggers the resize
  • Internally it calls workareaHandler.setSize, fits to screen, and pushes history
  • store.width / store.height are reactive, so observer keeps the "active" button correct

Scenario 2: unit conversion

Print projects often think in mm / inches but Fabric's canvas is pixels. Use store.setUnit({ unit, dpi }):

const AVAILABLE_PRINT_SIZES = [
{ label: 'A4 Portrait', unit: 'mm', dpi: 300, width: 210, height: 297 },
{ label: 'A4 Landscape', unit: 'mm', dpi: 300, width: 297, height: 210 },
{ label: 'Business card', unit: 'mm', dpi: 300, width: 90, height: 54 },
];

// On click
onClick={() => {
store.setUnit({ unit: 'mm', dpi: 300 });
// setSize still takes pixels — convert with: mm * dpi / 25.4
const mmToPx = (mm: number) => Math.round((mm * 300) / 25.4);
store.setSize({
width: mmToPx(210),
height: mmToPx(297),
});
}}

store.unit / store.dpi are reactive — rulers and related UI will pick up the change.


Scenario 3: grouped size hub

For products with multiple categories (social / print / e-commerce / …), a more structured UI:

const SIZE_GROUPS = [
{
group: 'Social media',
items: [
{ label: 'Instagram square', width: 1080, height: 1080 },
{ label: 'Instagram story', width: 1080, height: 1920 },
{ label: 'WeChat Moments', width: 1242, height: 828 },
],
},
{
group: 'Print',
items: [
{ label: 'Poster · A2', width: 4961, height: 7016 },
{ label: 'A4', width: 2480, height: 3508 },
],
},
];

const GroupedSizeSection: Section = {
name: 'size',
Tab: /* ... */,
Panel: observer(({ store }) => (
<div>
{SIZE_GROUPS.map(({ group, items }) => (
<div key={group} style={{ marginBottom: 16 }}>
<h4>{group}</h4>
{items.map(it => (
<Button key={it.label} block onClick={() => store.setSize(it)}>
{it.label} · {it.width}×{it.height}
</Button>
))}
</div>
))}
</div>
)),
};

APIPurpose
store.setSize({ width, height })Resize the canvas (pushes history)
store.setUnit({ unit, dpi })Change canvas unit
store.width / store.heightCurrent pixel dimensions (reactive)
store.unit / store.dpiCurrent unit + DPI (reactive)
store.editor?.workareaHandler.setSize(...)Low-level; store.setSize is enough for most cases

Next