Changelog

v1.1.0

We just tagged @shadcn/designer v1.1.0 with a new unit system and unified Designer Tool API for managing canvas tools.

Unit System

We now let you work in pixels, millimeters, inches, centimeters, or points with DPI-aware conversions.

  • Introduced hooks for reacting to unit changes: useUnitSystem, useSetUnitSystem, useDPI, and useSetDPI.
  • Added ActionUnitSelector for swapping units directly from the toolbar.
  • Added a shared units library with helpers like toPixels, fromPixels, parseValueWithUnit, and formatValue plus DEFAULT_DPI and DPI_PRESETS presets.
  • Updated editor defaults to store unitSystem and dpi, convert paper sizes from millimeters, and clamp dimension inputs at 0.
  • Refined dimension displays to show converted values with trimmed decimals and localized unit abbreviations.
import { useUnitSystem, useSetUnitSystem } from "@shadcn/designer"
 
function UnitSwitcher() {
  const unitSystem = useUnitSystem()
  const setUnitSystem = useSetUnitSystem()
 
  return (
    <div className="flex items-center gap-2">
      <span>Units: {unitSystem}</span>
      <button onClick={() => setUnitSystem("mm")}>Millimeters</button>
    </div>
  )
}

Example

This example demonstrates how to use the unit system to display and work with measurements in different units.

Designer Tool

We've introduced a new tool selection system that allows you to switch between different interaction modes on the canvas. This release includes two core tools:

  • Move Tool (V) - The default tool for selecting, moving, resizing, and rotating layers
  • Hand Tool (H) - Pan around the canvas without selecting or moving layers

Hooks

useDesignerTool() - Get the currently active tool

import { useDesignerTool } from "@shadcn/designer"
 
function ToolIndicator() {
  const tool = useDesignerTool()
 
  return (
    <div>
      Active: {tool === "move" ? "Move Tool" : "Hand Tool"}
    </div>
  )
}

useSetDesignerTool() - Change the active tool programmatically

import { useSetDesignerTool } from "@shadcn/designer"
 
function ToolSwitcher() {
  const setTool = useSetDesignerTool()
 
  return (
    <div>
      <button onClick={() => setTool("move")}>Move (V)</button>
      <button onClick={() => setTool("hand")}>Hand (H)</button>
    </div>
  )
}

ActionToolbarTool

Dropdown toolbar button for switching between tools

import { ActionToolbarTool } from "@shadcn/designer"
 
<DesignerToolbar>
  <DesignerToolbarGroup>
    <ActionToolbarTool />
  </DesignerToolbarGroup>
</DesignerToolbar>

See the Hooks Reference for complete API documentation.

Example

Input Scrubber

The InputGroup component now supports scrubbing for faster adjustments without losing precision.

  • Hold the mouse button and drag to scrub an input value.
  • Added a useScrubber hook that handles pointer locking, acceleration, and modifier shortcuts for fine or coarse adjustments.
  • Exposed the hook through the designer entry point so custom controls can share the same interaction model as built-in actions.
  • Bundled a visual scrubber cursor that renders while dragging to make state changes easier to track.
import * as React from "react"
import { useScrubber } from "@shadcn/designer"
 
function DimensionScrubber() {
  const { scrubProps, value } = useScrubber({
    value: 120,
    onChange: (next) => console.log("Width", next),
  })
 
  return (
    <button className="flex items-center gap-2" {...scrubProps}>
      Width: {value}px
    </button>
  )
}

Continuous Button Press

We've added hold-to-repeat controls to make numeric inputs feel more responsive.

  • Added a continuous press controller that emits repeated onChange events while increment or decrement buttons remain pressed.
  • Integrated the behavior into InputNumber, matching keyboard repeat cadence and respecting min/max constraints.
  • Tuned acceleration so long presses ramp quickly without overshooting small adjustments.

v1.0.0

We're excited to announce the release of @shadcn/designer v1.0.0 (stable). If you do not have a license, you can get one here.

v1.0.0

Here's a list of the new features and improvements:

  • mode - new mode prop to the <Designer /> component to switch between different layer modes. See the docs for more information.
  • onMount - We've added a new onMount prop to the <Designer /> component to run a callback when the designer is mounted. Useful for doing additional setup when the designer is ready.
  • defaultLayers - the layers prop has been renamed to defaultLayers for uncontrolled components.
  • layers and onLayersChange - We've added new props for controlled <Designer /> components.
  • We've also introduced a layer signature optimization to reduce the number of re-renders.
  • We've improved layer snapping to be more accurate.

See the roadmap for what's coming next.

Thank you to all the early adopters who have been using the designer and providing feedback: Mikkel, Sebastian, Piotr, Santosh, Jenny, Alex, Maria, David K., David E., and Sarah.

v0.4.0

We've released a new version of @shadcn/designer with several improvements to the image browser, a new image cropper and layer locking.

Image Browser

Image Browser

The new ActionImage now has support for plugins. You can build your own image plugin and use it in the image browser.

<ActionImage
  plugins={[
    {
      id: "cropper",
      label: "Crop",
      description:
        "Move and scale the image to crop it. The white frame represents the layer.",
      component: <ActionImageCropper />,
    },
    {
      id: "browser",
      label: "Browse",
      description:
        "Search and select an image to set as the image for the selected layer.",
      component: (
        <ActionImageBrowser
          apiUrl="/api/images"
        />
      ),
    },
  ]}
/>

See the Image Browser guide to learn how to build your own image browser plugin.

Image Cropper

We've added a new ActionImageCropper component for image cropping.

Image Cropper

The ActionImageCropper can be used on its own or as a plugin for the ActionImage component.

Locked Layers

We've also added a new locked property to layers. This allows you to lock a layer in place.

You can use the layersAction("LOCK_UNLOCK_LAYER", [layer.id]) action to lock and unlock layers.

import { useLayersAction } from "@shadcn/designer"
 
export function LockLayerButton({ layer }: { layer: DesignerLayer }) {
  const layersAction = useLayersAction()
 
  return (
   <Button onClick={() => layersAction("LOCK_UNLOCK_LAYER", [layer.id])}>
      {layer.isLocked ? <IconLock /> : <IconLockOpen />}
      <span className="sr-only">Toggle lock</span>
    </Button> 
  )
}
 

v0.3.0

We've released a new version of @shadcn/designer with several improvements to the core functionality and user experience.

History API

We've implemented a new History API that allows you to undo and redo changes in your designs. The API is available through three new hooks:

// Get access to the history state
const history = useHistory()
 
// Undo the last change
const undo = useUndo()
 
// Redo the last undone change
const redo = useRedo()

Improvements

We've made several improvements to the canvas interaction:

  • Added support for wheel and pinch zoom gestures
  • Fixed adaptive zoom behavior for better viewport handling
  • Resolved keyboard shortcut issues related to zoom controls
  • Fixed layer update flickering issues for smoother rendering

What's Next?

We are building more examples and documentation to help you, including image generation with AI. Stay tuned!

v0.2.0

We just tagged a new release of @shadcn/designer. This release brings a lot of new features and improvements.

Font Picker

We've added a Google Font picker to the designer.

The picker is decoupled from the designer so that you're free to implement your own picker or use the one we provide.

Font Picker

Image Browser

We've also added an image browser using the Unsplash API. Use this as a reference to build your own image picker.

Image Browser

Snapping

We've added snapping to the canvas. This means that layers will snap to the nearest grid point when you move them. This makes it easier to align layers to the canvas. We've also added distance guidelines for horizontal and vertical snapping.

Snapping

Template Updates

  • We've updated the template to use the new font picker and image browser.
  • We added examples for fetching fonts and images from the Unsplash API.
  • The template now ships with a Playground to test image layers. This is the same playground that you see in the demo.
  • We also added an Inspector to test the static frame rendering. This is useful for turning posts into images.

That's it for now. Grab the latest template to see the new features. See the Roadmap for what's coming next.

v0.1.0

We are excited to bring you this update as we are getting close to General Availability (GA).

This update brings a lot of changes to the core system. We've added a new set of hooks and components to make it easier to extend and build custom designers.

There are still a few things we're wrapping up, but we're excited to finally show you a demo of what we've been working on.

Designer Demo

First, let's take a look at a demo of what we've been working on. Click on Launch Demo in the header to create your own personalized playground to test the editor.

Use this playground to test the editor features, layers, controls and actions.

API

We've also built a demo to show how you can turn any design into an API. Using the API turns your designs into templates that can be used to generate designs for different contexts by passing in different layer values.

Try the API

Export to Image

When you're done customizing your layer values, click the Download button to download the design as an image.

Export to Image

This can be used to generate images for social media, banners, etc and serve them on demand over a CDN.

Performance Improvements

We've made a lot of performance improvements.

We reworked the internal system to have better rendering performance. Updates are granular and only trigger when necessary. We also fixed an issue with dropped frames.

Layer Types

You can now define your own layer types and override the default ones. A layer type can bring in its own keybindings, default values and render method.

<Designer layerTypes={[
  {
    type: "custom",
    name: "Custom",
    icon: IconCustom,
    defaultValues: {
      // ...
    },
    keybinding: {
      // ...
    },
    render: (layer) => (
      // ...
    ),
  }
]} />

Hooks

Every state and action are now available as hooks. We provide all the necessary hooks to read and update the state of the designer.

// Get the layers.
const layers = useLayers()
 
// Set the zoom level
const designerAction = useDesignerAction()
designerAction("ZOOM_IN")
 
// Get the selected layers
const selectedLayers = useSelectedLayers()
 
// Change the name of a layers
const setLayersProperty = useSetLayersProperty()
setLayersProperty([ID_OF_LAYERS], "name", "New Name")

See the reference for more information on available hooks.

Frame Size

We've made the frame size customizable. You can now pass a frameSize prop to the <Designer /> component to set the size of the frame. Useful for building social media images, banners, etc of different sizes.

<Designer frameSize={{ width: 1024, height: 1024 }} />

To programmatically change the frame size, you can use the setFrameSize hook.

const setFrameSize = useSetFrameSize()
setFrameSize({ width: 1024, height: 1920 })

Keyboard Shortcuts

We've added a new keybindings prop to the <Designer /> component to handle keyboard shortcuts. You can pass a keybindings object to the component to provide your own keybindings or override the default ones.

<Designer keybindings={{
  DUPLICATE_LAYER: {
    key: "meta+d",
    label: "Ctrl D",
    labelMac: "⌘ D",
    description: "Duplicate selected layers",
    group: "Layer",
  },
}} />

See the reference for more information on available keybindings.

Debug Mode

We've also added a new debug prop to the <Designer /> component to enable debug mode during development.