Designer

Learn how to use the designer component.

The Designer component is the heart of the @shadcn/designer package. It gives you the canvas, frame, tooling, and data model you need to build custom editors, from a single framed artboard to a multi-pane design suite.

Quick start

Use Designer, DesignerContent, DesignerCanvas, and DesignerFrame together to render the default editor experience. This mirrors the setup used in the basic example.

components/custom-designer.tsx
import * as React from "react"
import {
  Designer,
  DesignerCanvas,
  DesignerContent,
  DesignerFrame,
} from "@shadcn/designer"
 
export function CustomDesigner() {
  return (
    <Designer className="flex h-svh flex-col gap-4">
      <DesignerContent>
        <DesignerCanvas>
          <DesignerFrame />
        </DesignerCanvas>
      </DesignerContent>
    </Designer>
  )
}

Layout primitives

  • DesignerContent renders the scrollable workspace that holds the canvas.
  • DesignerCanvas provides pan, zoom, and selection handling for all layers.
  • DesignerFrame draws a transformable artboard. Add multiple frames or swap in DesignerStaticFrame when you want a fixed preview.
  • DesignerToolbar, DesignerPanel, and DesignerPane help you compose custom UI around the canvas.

Combine these building blocks with your own UI from @shadcn/ui to design the editor shell that fits your product.

Core props

PropTypeDescription
defaultLayersLayer[]Pre-populated layers for uncontrolled mode. Useful for seeding from persisted data.
layersLayer[]Enables controlled mode. Mirror state updates with onLayersChange.
onLayersChange(layers: Layer[]) => voidCalled whenever the editor mutates layers. Required when layers is provided.
layerTypesLayerType[]Extends or replaces the default layer registry. Tie custom renderers and tooling to your data.
frameSize{ width: number; height: number; unit?: Unit }Sets the initial dimensions and unit for DesignerFrame. Combine with unit actions for print workflows.
unitSystemUnitOverrides the global unit (px, in, mm, etc.). Drives rulers, snapping, and measurement UI.
dpinumberDefine dots per inch for pixel-to-unit conversions.
mode`"single""multiple"`
keybindingsRecord<string, Keybinding>Customize shortcuts or provide localized variations.
debugbooleanSurface debug visuals while developing integrations.
onMount() => voidRun side effects (analytics, focus) after the editor mounts.

Seeding layers in uncontrolled mode

Designer manages its own layer state when you pass defaultLayers. This is ideal when you only need to load an initial document.

components/designer-with-defaults.tsx
import * as React from "react"
import {
  Designer,
  DesignerCanvas,
  DesignerContent,
  DesignerFrame,
  type Layer,
} from "@shadcn/designer"
 
const DEFAULT_LAYERS: Layer[] = [
  {
    id: "layer-1",
    name: "Hero",
    type: "frame",
    value: null,
    cssVars: {
      "--width": "640px",
      "--height": "480px",
      "--background-color": "#dbeafe",
      "--translate-x": "80px",
      "--translate-y": "56px",
    },
  },
]
 
export function DesignerWithDefaults() {
  return (
    <Designer defaultLayers={DEFAULT_LAYERS} className="flex h-[720px] flex-col gap-4">
      <DesignerContent>
        <DesignerCanvas>
          <DesignerFrame />
        </DesignerCanvas>
      </DesignerContent>
    </Designer>
  )
}

Controlled mode

When you need to sync layers with your own state or a backend, switch to controlled mode by providing layers and onLayersChange.

components/designer-controlled.tsx
import * as React from "react"
import {
  Designer,
  DesignerCanvas,
  DesignerContent,
  DesignerFrame,
  type Layer,
} from "@shadcn/designer"
 
export function DesignerControlled() {
  const [layers, setLayers] = React.useState<Layer[]>([])
 
  return (
    <Designer
      layers={layers}
      onLayersChange={setLayers}
      className="flex h-[720px] flex-col gap-4"
    >
      <DesignerContent>
        <DesignerCanvas>
          <DesignerFrame />
        </DesignerCanvas>
      </DesignerContent>
    </Designer>
  )
}

Custom layer types

Augment the editor by extending DEFAULT_LAYER_TYPES with your own definition. Supply defaults, keyboard shortcuts, and a render function that outputs React nodes.

components/designer-video-layer.tsx
import * as React from "react"
import {
  DEFAULT_LAYER_TYPES,
  Designer,
  DesignerContent,
  DesignerCanvas,
  DesignerFrame,
  type LayerType,
} from "@shadcn/designer"
import { IconPlayerPlay } from "@tabler/icons-react"
 
const VIDEO_LAYER_TYPE = {
  type: "video",
  name: "Video",
  icon: IconPlayerPlay,
  defaultValues: {
    name: "Video",
    value: {
      src: "https://cdn.example.com/video.mp4",
    },
    cssVars: {
      "--width": "320px",
      "--height": "180px",
      "--background-color": "#000000",
    },
  },
  keybinding: {
    key: "v",
    label: "V",
    labelMac: "V",
    description: "Add video layer",
    group: "New Layer",
  },
  render: (layer) => {
    return (
      <video
        controls
        style={layer.contentStyle}
        src={layer.value.src}
      />
    )
  },
} satisfies LayerType
 
export function DesignerWithVideoLayer() {
  return (
    <Designer
      layerTypes={[...DEFAULT_LAYER_TYPES, VIDEO_LAYER_TYPE]}
      className="flex h-[720px] flex-col gap-4"
    >
      <DesignerContent>
        <DesignerCanvas>
          <DesignerFrame />
        </DesignerCanvas>
      </DesignerContent>
    </Designer>
  )
}

Composing the UI

Add panels, toolbars, and inspector panes around the canvas to create a full editor experience.

components/designer-with-shell.tsx
import * as React from "react"
import {
  Designer,
  DesignerCanvas,
  DesignerContent,
  DesignerFrame,
  DesignerHeader,
  DesignerPanel,
  DesignerToolbar,
  DesignerToolbarGroup,
  DesignerToolbarButton,
} from "@shadcn/designer"
import { Button } from "@shadcn/ui/button"
 
export function DesignerWithShell() {
  return (
    <Designer className="flex h-svh flex-col gap-4">
      <DesignerHeader className="flex items-center justify-between gap-2 p-4">
        <div className="text-sm font-medium">Brand Poster</div>
        <div className="flex gap-2">
          <Button size="sm">Preview</Button>
          <Button size="sm" variant="outline">
            Export
          </Button>
        </div>
      </DesignerHeader>
 
      <DesignerToolbar className="flex items-center gap-2 px-4">
        <DesignerToolbarGroup className="flex items-center gap-2">
          <DesignerToolbarButton size="sm">Add</DesignerToolbarButton>
          <DesignerToolbarButton size="sm">Duplicate</DesignerToolbarButton>
        </DesignerToolbarGroup>
      </DesignerToolbar>
 
      <div className="grid flex-1 grid-cols-[1fr_320px] gap-4">
        <DesignerContent>
          <DesignerCanvas>
            <DesignerFrame />
          </DesignerCanvas>
        </DesignerContent>
 
        <DesignerPanel className="flex flex-col gap-6 p-4">
          <div>
            <p className="text-sm font-semibold">Layer</p>
            <p className="text-sm text-muted-foreground">Select a layer to edit properties.</p>
          </div>
        </DesignerPanel>
      </div>
    </Designer>
  )
}

Next steps

  • Walk through Layers to learn how layer data, transforms, and inspectors work together.
  • Dive into Unit System for precision control over print and physical products.
  • Explore the /examples directory for more complete editor shells, including print and marketing templates.