# @shadcn/designer > A composable, themeable design editor built with React, Tailwind CSS, and shadcn/ui. --- ## Introduction This is the documentation for the @shadcn/designer package. ## I'm new here Take a look at some examples, follow the quickstart, and get to know what the designer can do. --- ## I want to try it out Play around with the demo, see what plans we offer, or just ask us anything. --- ## I bought a license, now what? Get it set up, learn how to add your own layers, and create actions that fit how you work. --- ## I'm ready to build Customize the designer's look, see available components, and check out the hooks. --- ## I need help Look through working code, find what you need in the API docs, or just shoot us a message. --- ## Pricing Get a Yearly Business License to use shadcn/designer in your projects. ## Yearly Business License The Yearly Business License is a **$1,499 yearly subscription**. It includes unlimited products and comes with **1 month of free support via Slack**. If you'd like to try before purchasing, we also offer a 1-month paid trial. - You will receive a license key after purchase. Use this license key to install the package from our private registry. You can read more about the [license here](/docs/license). - The **1-month paid trial** includes a full-featured license that gives you access to all features. This license is intended for testing and evaluation only, not for production use. - After the 1-month period ends, you'll retain access to all features and can continue using and testing the designer, but you won't receive new updates. - When you're ready to upgrade to the Yearly Business License, reach out to us and we'll credit your trial payment toward your purchase. > **We do not offer refunds at this time.** If you have any questions about the license, supported features, roadmap, or anything else, please [reach out to us](/docs/contact) before purchasing. ## Frequently Asked Questions --- ## License SOFTWARE LICENSE AGREEMENT Copyright © shadcn 2025 This Software License Agreement ("Agreement") governs the use of the software package `@shadcn/designer` (the "Software") provided by shadcn ("Licensor"). By purchasing, downloading, or using the Software, you ("Licensee") agree to be bound by the terms of this Agreement. 1. LICENSE GRANT Subject to full payment and compliance with this Agreement, Licensor grants Licensee a non-exclusive, non-transferable, revocable license to: - Use the Software in unlimited internal or commercial applications; - Modify the Software for internal use within those applications; - Bundle and distribute the Software as part of compiled production builds. 2. RESTRICTIONS Licensee may not: - Redistribute, publish, or share the Software source code, in whole or in part; - Include the Software in any open-source project, public code repository, or package registry; - Use the Software to create a competing product or component library; - Sublicense, resell, rent, lease, or otherwise provide the Software to third parties, except as part of a compiled application. 3. OWNERSHIP The Software is licensed, not sold. All rights, title, and interest in the Software remain the exclusive property of Licensor. No rights are granted other than those expressly stated in this Agreement. 4. TERMINATION If Licensee breaches any term of this Agreement, the license terminates automatically and immediately. Upon termination, Licensee must delete all copies of the Software in their possession, including in all applications, backups, or systems. 5. NO WARRANTY The Software is provided “as is”, without warranties of any kind, express or implied, including but not limited to fitness for a particular purpose or non-infringement. Licensor shall not be liable for any damages arising from the use or inability to use the Software. 6. SUPPORT Unless explicitly stated at the time of purchase, this license does not include guaranteed support, maintenance, bug fixes, or future updates. Licensor may, at its sole discretion, choose to offer support or updates. 7. FUTURE CHANGES Licensor reserves the right to modify, discontinue, open-source, relicense, or cease support for the Software at any time, without obligation to Licensee. This includes the right to make future versions of the Software available under different terms, including open-source licenses. --- For licensing questions or custom terms, contact: [m@shadcn.com](mailto:m@shadcn.com) --- ## 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](/docs/guides/update) 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](#type-safe-layer-definitions) - [Nested Layers and Groups](#nested-layers-and-groups) - [Group Layout Controls](#group-layout-controls) - [Layer Value and Meta Hooks](#layer-value-and-meta-hooks) - [Shared Border, Corner, and Padding Controls](#shared-border-corner-and-padding-controls) ### 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. ```tsx 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 }, }) // 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 {} } ``` Once registered, the designer narrows APIs such as `Layer`, `showForLayerTypes`, `useSetLayersValue`, and `useSetLayersMeta` to your app's layer registry. - See [Type Safety](/docs/concepts/type-safety) and the [`createLayerType`](/docs/reference/types#createlayertype) reference for the full setup. - See also the [detailed guide](/docs/concepts/layers) for working with layers. ### 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. ```tsx import { useLayerTree, useLayersAction } from "@shadcn/designer" function GroupSelection({ selectedLayerIds }: { selectedLayerIds: string[] }) { const layerTree = useLayerTree() const layersAction = useLayersAction() return ( ) } ``` 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. ```tsx import { ActionLayoutAlign, ActionLayoutDirection, ActionLayoutDisplay, ActionLayoutGap, ActionLayoutJustify, ActionLayoutPadding, ActionLayoutWrap, DesignerPane, DesignerPaneContent, DesignerPaneTitle, } from "@shadcn/designer" function GroupPanel() { return ( Group ) } ``` ### 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. ```tsx 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 (
) } ``` See the [`useSetLayersValue`](/docs/reference/hooks#usesetlayersvalue) and [`useSetLayersMeta`](/docs/reference/hooks#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](/docs/guides/update) 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`](/llms.txt) and [`/llms-full.txt`](/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](#unit-system) - [Designer Tool](#designer-tool) - [Input Scrubber](#input-scrubber) ### 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. ```tsx import { useUnitSystem, useSetUnitSystem } from "@shadcn/designer" function UnitSwitcher() { const unitSystem = useUnitSystem() const setUnitSystem = useSetUnitSystem() return (
Units: {unitSystem}
) } ``` ### 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 ```tsx import { useDesignerTool } from "@shadcn/designer" function ToolIndicator() { const tool = useDesignerTool() return (
Active: {tool === "move" ? "Move Tool" : "Hand Tool"}
) } ``` **`useSetDesignerTool()`** - Change the active tool programmatically ```tsx import { useSetDesignerTool } from "@shadcn/designer" function ToolSwitcher() { const setTool = useSetDesignerTool() return (
) } ``` #### ActionToolbarTool Dropdown toolbar button for switching between tools ```tsx import { ActionToolbarTool } from "@shadcn/designer" ``` See the [Hooks Reference](/docs/reference/hooks#usedesignertool) for complete API documentation. #### Example Use the Cursor and Hand buttons in the toolbar to switch between the move and hand tools. ### Input Scrubber The [`InputGroup`](/docs/reference/ui#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. ```tsx 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 ( ) } ``` #### 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](/docs/pricing). ![v1.0.0](/opengraph-image.jpg) Here's a list of the new features and improvements: - **mode** - new `mode` prop to the `` component to switch between different layer modes. See the [docs](/docs/reference/designer#mode) for more information. - **onMount** - We've added a new `onMount` prop to the `` 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 `` 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](/docs/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.** --- ## Installation Learn how to install the @shadcn/designer package and get started with the designer. ## License Required You will need a license key to install the package. You can learn more about the pricing and purchase a Yearly Business License [here](/docs/pricing). ## Create Project First, let's setup a new Vite + Tailwind project. > **Note:** You can use any React framework with the designer. This guide uses > Vite + React + Tailwind. The only requirement is that you have Tailwind > installed. ```bash npx create-vite@latest my-designer --template react-ts ``` ## Configure Tailwind The `@shadcn/designer` package is built with Tailwind CSS. You will need to install Tailwind and configure it in your project. ### Install Tailwind Add the following dependencies to your project. ```bash npm install tailwindcss @tailwindcss/vite ``` ### Configure the Vite plugin Import the Tailwind Vite plugin in your `vite.config.ts` file. ```tsx import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; import tailwindcss from "@tailwindcss/vite"; export default defineConfig({ plugins: [react(), tailwindcss()], }); ``` ### Import Tailwind CSS Replace the content of your `src/index.css` file with the following. ```tsx @import "tailwindcss"; ``` ## Install Package Next, let's install the package. You will need a license key to install the package. You can purchase a Yearly Business License [here](/docs/pricing). ```bash npm install https://ds.shadcn.com/registry\?license\=YOUR_LICENSE_KEY ``` For a standalone setup, import the `Designer` component from `@shadcn/designer` and the full stylesheet from `@shadcn/designer/styles.css`. ```tsx import { Designer } from "@shadcn/designer"; import "@shadcn/designer/styles.css"; export default function App() { return ; } ``` Make sure you import `@shadcn/designer/styles.css` once in standalone apps. This should be imported after your own styles. > **Existing Tailwind app:** If your app already has its own Tailwind CSS 4 > theme, base reset, and utilities, do not import > `@shadcn/designer/styles.css`. Import `@shadcn/designer/core.css` from your > global CSS file and add `@source "../node_modules/@shadcn/designer";` so your > app generates the Designer utility classes. ```css @import "tailwindcss"; @import "@shadcn/designer/core.css"; @source "../node_modules/@shadcn/designer"; ``` The examples below omit the stylesheet import. Configure one of the stylesheet options above once in your app. > **Note:** The Designer will take up the height of its container. The `h-svh` class is used to make the designer take up the full height of the viewport. ## Components The `Designer` is built to be composable. You build your custom designer by composing the components that you need. The following will setup a basic designer with a canvas and a frame. The default frame size is `1024x1024`. ```tsx import { Designer, DesignerContent, DesignerCanvas, DesignerFrame, } from "@shadcn/designer"; export default function App() { return ( ); } ``` ## Layers Now that your designer is setup, you can add layers to it. Press T on your keyboard to add a new `text` layer. You will see a new text layer added to the canvas. You can drag the text layer around the canvas and use the handles to resize it. The designer comes with the following layer types by default: `frame`, `text` and `image`. You can extend the designer by adding your own layer types. See the [Custom Layer](/docs/concepts/layers) example for more information. ## Toolbar Let's place a toolbar with buttons to add layers to the canvas. ```tsx import { Designer, DesignerContent, DesignerCanvas, DesignerFrame, DesignerToolbar, DesignerToolbarGroup, ActionToolbarAddLayer, } from "@shadcn/designer"; export default function App() { return ( ); } ``` You should see a new toolbar at the bottom of the canvas. The toolbar has one action to add a new layer. ## Panels Now that we have a canvas and we can add layers to it, let's add a `` to display layer controls. ```tsx import { Designer, DesignerContent, DesignerCanvas, DesignerFrame, DesignerToolbar, DesignerToolbarGroup, ActionToolbarAddLayer, DesignerPanel, } from "@shadcn/designer"; export default function App() { return ( ); } ``` This will place a panel on the right side of the canvas. We can have multiple panels in the designer. Let's add a panel to the left side of the canvas. ```tsx import { Designer, DesignerContent, DesignerCanvas, DesignerFrame, DesignerToolbar, DesignerToolbarGroup, ActionToolbarAddLayer, DesignerPanel, } from "@shadcn/designer"; export default function App() { return ( ); } ``` ## Panes We use `` to display and group controls. Let's add a new pane to the right panel. ```tsx import { Designer, DesignerContent, DesignerCanvas, DesignerFrame, DesignerToolbar, DesignerToolbarGroup, ActionToolbarAddLayer, DesignerPanel, DesignerPane, DesignerPaneTitle, DesignerPaneContent, } from "@shadcn/designer"; export default function App() { return ( Layers Hello World ); } ``` You should see a new pane with the title "Layers" and the content "Hello World" in the right panel. ## Actions and Controls To display and control the properties of a layer, we use [``](/docs/reference/actions) components. The `@shadcn/designer` package ships with several actions out of the box. **We provide the necessary API and components to create your own custom actions.** Let's add a [``](/docs/reference/actions#actionposition) to the right panel to display the position of the layer. ```tsx import { Designer, DesignerContent, DesignerCanvas, DesignerFrame, DesignerToolbar, DesignerToolbarGroup, ActionPosition, ActionToolbarAddLayer, DesignerPanel, DesignerPane, DesignerPaneTitle, DesignerPaneContent, } from "@shadcn/designer"; export default function App() { return ( Layers ); } ``` You should see a new _Position_ action in the right panel. It comes with `X` and `Y` inputs to control the position of the layer. If you click and drag a layer you should see the `X` and `Y` values change. ## More Actions Let's build a custom panel with more actions and import it into our designer. Create a new file called `src/panel-right.tsx` and add the following code. ```tsx import { ActionFill, ActionPadding, ActionBoxShadow, DesignerPanel, DesignerPane, DesignerPaneTitle, DesignerPaneContent, ActionPosition, ActionSize, ActionCorner, ActionBorder, ActionRotate, ActionDirection, ActionTextValue, ActionImageFit, ActionImageFilter, ActionFont, ActionColor, ActionFontSize, ActionLineHeight, ActionLetterSpacing, ActionTextAlign, ActionAlignItems, ActionFontStyle, ActionTextDecoration, ActionTextTransform, ActionTextShadow, ActionTextStroke, } from "@shadcn/designer"; export function PanelRight() { return ( Layer Styles Transforms Image Filters Text ); } ``` Then import the `PanelRight` component into your `src/App.tsx` file. ```tsx import { Designer, DesignerContent, DesignerCanvas, DesignerFrame, DesignerToolbar, DesignerToolbarGroup, ActionToolbarAddLayer, DesignerPanel, } from "@shadcn/designer"; import { PanelRight } from "./panel-right"; export default function App() { return ( ); } ``` You should see a new panel on the right side of the canvas. The panel has the actions to control the layer. See the [Actions](/docs/reference/actions) section for more information on available actions. ## Next Steps - [Add a custom layer to the designer](/docs/concepts/layers) - [Browse more examples](/docs/examples) - Read more about [components](/docs/reference/designer) and [hooks](/docs/reference/hooks) --- ## Update Guide How to update shadcn/designer to the latest version. ## Upgrade to 2.0 Version 2.0 adds typed layer utilities, group layers, and new setters for layer values and meta. Follow the steps below to update to 2.0.0. ### Update package.json to 2.0.0 In your `package.json` file, update the registry URL to `version=2.0.0`: ```json { "name": "my-designer", "dependencies": { "@shadcn/designer": "https://ds.shadcn.com/registry?version=2.0.0&license=YOUR_LICENSE_KEY" } } ``` Then run `pnpm install` to pull version 2.0.0 from the registry. ### Use createLayerType Custom layer definitions should now use `createLayerType`. ```tsx import { createLayerType } from "@shadcn/designer" export const shapeLayer = createLayerType({ type: "shape", name: "Shape", defaultValues: { name: "Shape", value: { type: "square" }, cssVars: { "--width": "100px", "--height": "100px", "--background-color": "#000000", }, }, render: (layer) => { return
}, }) ``` Keep your layer types in an inferred array. Do not annotate the array as `LayerType[]`. ```ts import { DEFAULT_LAYER_TYPES } from "@shadcn/designer" import { shapeLayer } from "./shape" export const layerTypes = [...DEFAULT_LAYER_TYPES, shapeLayer] ``` Register the array for type-safe `Layer`, `showForLayerTypes`, and layer setter usage: ```ts import type { InferLayerTypes } from "@shadcn/designer" import type { layerTypes } from "./layers" declare module "@shadcn/designer" { interface DesignerLayerTypes extends InferLayerTypes {} } ``` ### Review group layers `DEFAULT_LAYER_TYPES` now includes a built-in `group` layer type. Groups use `parentId` to store nested layers: ```ts type Layer = { id: string type: string parentId?: string } ``` 2.0 adds: - `GROUP_LAYERS` and `UNGROUP_LAYERS` actions. - Ctrl/Cmd + G to group selected layers. - Ctrl/Cmd + Shift + G to ungroup. - Nested layer rendering and drag-and-drop parenting in ``. - Layout controls for display, direction, alignment, wrapping, and gap. New actions are available for working with groups: ```tsx import { ActionLayoutWrap, ActionLayoutAlign, ActionLayoutDirection, ActionLayoutDisplay, ActionLayoutGap, ActionLayoutJustify, ActionLayoutPadding } from "@shadcn/designer" ``` ### Replace value and meta writes Use the new `useSetLayersValue` and `useSetLayersMeta` hooks to set layer values and meta. Keep `useSetLayersProperty` for shared fields such as `name` and `isLocked`. ```tsx import { useSetLayersProperty } from "@shadcn/designer" const setLayersProperty = useSetLayersProperty() setLayersProperty(selectedLayerIds, "value", { type: "circle" }) ``` ```tsx import { useSetLayersValue } from "@shadcn/designer" const setLayersValue = useSetLayersValue() setLayersValue<"shape">(selectedLayerIds, { type: "circle" }) ``` Use `useSetLayersMeta` for `layer.meta`: ```tsx import { useSetLayersProperty } from "@shadcn/designer" const setLayersProperty = useSetLayersProperty() setLayersProperty(selectedLayerIds, "meta", { approved: true }) ``` ```tsx import { useSetLayersMeta } from "@shadcn/designer" const setLayersMeta = useSetLayersMeta() setLayersMeta<"card">(selectedLayerIds, { approved: true }) ``` When you only have IDs, pass the layer type as a generic: ```tsx setLayersValue<"shape">(selectedLayerIds, { type: "square" }) setLayersMeta<"card">(selectedLayerIds, { approved: true }) ``` ### 2.0 checklist - Update the registry URL version and run `pnpm install`. - Convert custom layer definitions to `createLayerType`. - Register custom layer types with `InferLayerTypes`. - Preserve and handle `parentId` in custom layer-list logic. - Replace `value` writes with `useSetLayersValue`. - Replace `meta` writes with `useSetLayersMeta`. --- ## Update to the latest minor version In your `package.json` file, update the `version={PACKAGE_VERSION}` to the latest version. The latest version is **{PACKAGE_VERSION}**. ```json { "name": "my-designer", "dependencies": { "@shadcn/designer": "https://ds.shadcn.com/registry?version=PACKAGE_VERSION&license=YOUR_LICENSE_KEY" } } ``` Then run `pnpm install` to pull the latest version from the registry. --- ## 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. ```tsx import * as React from "react" import { Designer, DesignerCanvas, DesignerContent, DesignerFrame, } from "@shadcn/designer" export function CustomDesigner() { return ( ) } ``` > **Tip:** `Designer` is layout agnostic. Wrap it inside your own flex or grid containers to create headers, panels, and footers around the canvas. The `Designer` component currently targets a single frame workflow. Support for multiple frames is in development and will land soon. ## Layout primitives - `DesignerContent` renders the scrollable workspace that holds the canvas. - `DesignerCanvas` provides pan, zoom, and selection handling for all layers. - `DesignerFrame` draws the editable artboard and renders the current layer tree. - `DesignerStaticFrame` renders a fixed, read-only preview from layer data. - `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 | Prop | Type | Description | | --- | --- | --- | | `defaultLayers` | `Layer[]` | Pre-populated layers for uncontrolled mode. Useful for seeding from persisted data. | | `layers` | `Layer[]` | Enables controlled mode. Mirror state updates with `onLayersChange`. | | `onLayersChange` | `(layers: Layer[]) => void` | Called whenever the editor mutates layers. Required when `layers` is provided. | | `layerTypes` | `readonly LayerType[]` | 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. | | `unitSystem` | `Unit` | Overrides the global unit (px, in, mm, etc.). Drives rulers, snapping, and measurement UI. | | `dpi` | `number` | Define dots per inch for pixel-to-unit conversions. | | `mode` | `"single" | "multiple"` | Toggle between editing one layer at a time or multi-select workflows. | | `keybindings` | `Record` | Customize shortcuts or provide localized variations. | | `debug` | `boolean` | Surface debug visuals while developing integrations. | | `onMount` | `() => void` | Run side effects (analytics, focus) after the editor mounts. | ## Uncontrolled mode `Designer` manages its own layer state when you pass `defaultLayers`. This is ideal when you only need to load an initial document. ```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: "text", value: "Hero", cssVars: { "--width": "300px", "--height": "100px", "--content-font-size": "64px", "--content-font-weight": "700", "--content-color": "#000000", "--translate-x": "80px", "--translate-y": "56px", }, }, ] export function DesignerWithDefaults() { return ( ) } ``` ## Controlled mode When you need to sync layers with your own state or a backend, switch to controlled mode by providing `layers` and `onLayersChange`. ```tsx import * as React from "react" import { Designer, DesignerCanvas, DesignerContent, DesignerFrame, type Layer, } from "@shadcn/designer" export function DesignerControlled() { const [layers, setLayers] = React.useState([]) return ( ) } ``` ## Custom layer types Augment the editor by extending `DEFAULT_LAYER_TYPES` with your own definitions. Use `createLayerType` so the layer value is inferred in `render`, then pass the final array to `Designer`. ```tsx import * as React from "react" import { DEFAULT_LAYER_TYPES, Designer, DesignerContent, DesignerCanvas, DesignerFrame, createLayerType, } from "@shadcn/designer" import { IconPlayerPlay } from "@tabler/icons-react" const videoLayer = createLayerType({ type: "video", name: "Video", icon: , 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 (