Changelog

v2.0.0

We just shipped @shadcn/designer v2.0.0 with typed layer definitions, first-class nested groups, group layout controls, and new hooks for working with layer values and meta.

See the upgrade guide for details on how to update your project to v2.0.0.

Here's everything that's new in v2.0.0:

Type-Safe Layer Definitions

Layer types can now be defined with createLayerType. This keeps the type name and the shape of each layer's value and meta, so that you have fully typed layer definitions across your app including actions and hooks.

The setup has three parts:

  • Define each custom layer with createLayerType.
  • Export the final layerTypes array that your designer instance uses.
  • Register that array with InferLayerTypes through module augmentation.
import {
  DEFAULT_LAYER_TYPES,
  createLayerType,
  type InferLayerTypes,
} from "@shadcn/designer"
 
const chartLayer = createLayerType({
  type: "chart",
  name: "Chart",
  defaultValues: {
    name: "Chart",
    value: {
      type: "bar",
      data: [],
    },
    meta: {
      source: "manual",
    },
  },
  render: (layer) => {
    return <ChartRenderer value={layer.value} />
  },
})
 
// Include the default layer types and your custom layers.
export const layerTypes = [...DEFAULT_LAYER_TYPES, chartLayer]
 
// Tell @shadcn/designer about the layer registry for this app.
declare module "@shadcn/designer" {
  interface DesignerLayerTypes extends InferLayerTypes<typeof layerTypes> {}
}

Once registered, the designer narrows APIs such as Layer, showForLayerTypes, useSetLayersValue, and useSetLayersMeta to your app's layer registry.

Nested Layers and Groups

Layers now support hierarchy through parentId, and group is a built-in layer type. You can group, ungroup, lock, reorder, and render nested documents.

  • Added parentId to the Layer type.
  • Added the built-in group layer type.
  • Added GROUP_LAYERS and UNGROUP_LAYERS actions with default shortcuts.
  • Added useLayerTree, useRootLayers, and layer tree utilities such as buildLayerTree, getChildren, getDescendants, and getAncestors.
  • Updated the layer tree pane with drag-and-drop reordering, nested selection, expandable groups, and group-aware lock behavior.
import { useLayerTree, useLayersAction } from "@shadcn/designer"
 
function GroupSelection({ selectedLayerIds }: { selectedLayerIds: string[] }) {
  const layerTree = useLayerTree()
  const layersAction = useLayersAction()
 
  return (
    <button onClick={() => layersAction("GROUP_LAYERS", selectedLayerIds)}>
      Group {layerTree.length} root layers
    </button>
  )
}

Use ⌘ G or Ctrl G to group selected layers, and ⌘ ⇧ G or Ctrl Shift G to ungroup.

Group Layout Controls

Group layers can now work as freeform or flex containers. The new layout actions expose display, direction, alignment, justification, gap, padding, and wrapping controls for group-specific inspector panels.

import {
  ActionLayoutAlign,
  ActionLayoutDirection,
  ActionLayoutDisplay,
  ActionLayoutGap,
  ActionLayoutJustify,
  ActionLayoutPadding,
  ActionLayoutWrap,
  DesignerPane,
  DesignerPaneContent,
  DesignerPaneTitle,
} from "@shadcn/designer"
 
function GroupPanel() {
  return (
    <DesignerPane showForLayerTypes={["group"]}>
      <DesignerPaneTitle>Group</DesignerPaneTitle>
      <DesignerPaneContent>
        <ActionLayoutDisplay />
        <ActionLayoutDirection />
        <ActionLayoutAlign />
        <ActionLayoutJustify />
        <ActionLayoutGap />
        <ActionLayoutPadding />
        <ActionLayoutWrap />
      </DesignerPaneContent>
    </DesignerPane>
  )
}

Layer Value and Meta Hooks

useSetLayersValue and useSetLayersMeta provide typed setters for custom layer state. They accept a layer ID, an array of IDs, a layer object, or an array of layer objects, and support updater functions plus { history: false } for internal changes.

import { useSelectedLayers, useSetLayersMeta, useSetLayersValue } from "@shadcn/designer"
 
function ChartActions() {
  const selectedLayers = useSelectedLayers()
  const setLayersValue = useSetLayersValue()
  const setLayersMeta = useSetLayersMeta()
  const chartLayers = selectedLayers.filter((layer) => layer.type === "chart")
 
  return (
    <div>
      <button onClick={() => setLayersValue(chartLayers, { type: "line", data: [] })}>
        Line
      </button>
      <button
        onClick={() =>
          setLayersMeta(chartLayers, (prev) => ({
            ...prev,
            source: "toolbar",
          }))
        }
      >
        Mark source
      </button>
    </div>
  )
}

See the useSetLayersValue and useSetLayersMeta references for target forms and generic usage.

Shared Border, Corner, and Padding Controls

The border, corner, and padding actions have been rebuilt around shared control behavior. They now handle mixed values across multi-selection, expose grouped and per-side controls, and share the same input model used by other numeric designer actions.

  • ActionCorner supports grouped radius editing and independent corner values.
  • ActionPadding supports grouped padding and per-side padding.
  • ActionBorder supports border width and color controls with shared multi-selection behavior.
  • useSharedValues is available for custom controls that need the same "all selected values match" behavior.

See the upgrade guide for upgrading your project to v2.0.0.


Restructured Docs, llms.txt, and Markdown

We've reorganized the docs and added support for LLMs and markdown.

Docs

The docs are now split into four sections: Concepts, Guides, Examples, and Reference. Old URLs redirect automatically.

llms.txt

We've added /llms.txt and /llms-full.txt endpoints for LLMs and AI tools. Every docs page is also available as markdown by appending .md to the URL. You'll find a "View as Markdown" link in the sidebar on each page.

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.