# @shadcn/designer > A composable, themeable design editor built with React, Tailwind CSS, and shadcn/ui. --- ## Welcome 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 business license to use shadcn/designer in your projects. ## Business License We offer a business license for **$4,999** (Launch price. Regular price is $7,999). This license is a one-time payment. It includes unlimited usage and comes with **3 months of 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 full 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) --- ## Roadmap Here's a list of features we're working on: - **Pages** - We're improving the API for working with multiple pages and multiple frames, especially history management. - **Layers Grouping** - This is a feature we're testing out. We're not sure if we'll keep it. --- ## Changelog ## 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.** ## 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](/images/pv3hRvKM.png) The new `ActionImage` now has support for plugins. You can build your own image plugin and use it in the image browser. ```tsx , }, { id: "browser", label: "Browse", description: "Search and select an image to set as the image for the selected layer.", component: ( ), }, ]} /> ``` See the [Image Browser](/docs/guides/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](/images/UGHEUXXN.png) 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. ```tsx import { useLayersAction } from "@shadcn/designer" export function LockLayerButton({ layer }: { layer: DesignerLayer }) { const layersAction = useLayersAction() return ( ) } ``` ## 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: ```tsx // 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](/images/font-picker-light.png) ![Font Picker](/images/font-picker-dark.png) ### 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](/images/image-browser-light.png) ![Image Browser](/images/image-browser-dark.png) ### 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](/images/snapping-light.png) ![Snapping](/images/snapping-dark.png) ### 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](/docs#download-template) to see the new features. See the [Roadmap](/docs/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](https://ds.shadcn.com/demo/dito/j73pj2qqr6nwdrs7/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](/images/bGL6tRkr.png) 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. ```tsx ( // ... ), } ]} /> ``` ### 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. ```tsx // 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](/docs/reference/hooks) for more information on available hooks. ### Frame Size We've made the frame size customizable. You can now pass a `frameSize` prop to the `` component to set the size of the frame. Useful for building social media images, banners, etc of different sizes. ```tsx ``` To programmatically change the frame size, you can use the `setFrameSize` hook. ```tsx const setFrameSize = useSetFrameSize() setFrameSize({ width: 1024, height: 1920 }) ``` ### Keyboard Shortcuts We've added a new `keybindings` prop to the `` component to handle keyboard shortcuts. You can pass a `keybindings` object to the component to provide your own keybindings or override the default ones. ```tsx ``` See the [reference](/docs/reference/keybindings) for more information on available keybindings. ### Debug Mode We've also added a new `debug` prop to the `` component to enable debug mode during development. Previous Releases ## v0.0.1 See the [latest documentation](/docs) for the most up-to-date information. Based on your valuable feedback, we've completely rebuilt [Designer](https://ds.shadcn.com) from the ground up. We would like to share an early preview that introduces a new level of composability and flexibility. We've redesigned the system to be highly composable while preserving flexibility. This means you can build a simple component designer to a full-fledged designer with drag-and-drop functionality. We're working on more documentation and examples, but we want to share an early preview with you. You can install and try it today. Let's get started. ### Project Setup We've put together a template to get you started. The template is a simple Vite app with Tailwind CSS, shadcn/ui and Designer. Download the template and extract it into your project folder. ```bash cd studio && pnpm install ``` ```bash pnpm dev ``` If you visit [http://localhost:5173](http://localhost:5173), you should see a white page displaying "Hello World". ![Hello World](/images/01.png) ### Setup the Designer We'll start by adding the `` and `` components to our page. Place the `` and `` components in the `src/App.tsx` file. This will setup the designer and an infinite canvas with zoom, pan and controls. ```tsx import { Designer, DesignerCanvas } from "@shadcn/designer" export default function App() { return ( ) } ``` Every design starts with a frame. The frame is the container that holds your layers. A frame has a width and height. Use a `` component to set the size of your design. This can be any size you want eg. 1024x1024, 1920x1080, 2048x2048, etc. Place the `` component inside the `` component. ```tsx import { Designer, DesignerCanvas, DesignerFrame, } from "@shadcn/designer" export default function App() { return ( ) } ``` ![Designer with frame](/images/02.png) ### Add a Layer Now that we have the basic setup, let's add a layer. ```tsx import { Designer, DesignerCanvas, DesignerFrame, type DesignerLayer, } from "@shadcn/designer" const layers: DesignerLayer[] = [ { id: "1", name: "Heading", type: "text", value: "Hello World", style: { fontSize: "124px", fontWeight: "bold", width: "900px", height: "200px", }, }, ] export default function App() { return ( ) } ``` This will add a `text` layer to the frame. You can click to drag the layer and use the resizer to change its width and height. ### Add an Action An action is a component that can be used to display and transform the style of a layer. For example, you can use an action to change the width and height of a layer or the font size. **@shadcn/designer** comes with a set of actions that you can use out of the box. Let's add an action to change the dimension of a layer. We'll place it in a `` component on the right side of the screen. ```tsx import { Designer, DesignerCanvas, DesignerFrame, type DesignerLayer, ActionDimension, DesignerPanel, DesignerPane, DesignerPaneTitle, DesignerPaneContent, } from "@shadcn/designer" const layers: DesignerLayer[] = [ { id: "1", name: "Heading", type: "text", value: "Hello World", style: { fontSize: "124px", fontWeight: "bold", width: "900px", height: "200px", }, }, ] export default function App() { return ( Layer ) } ``` That was easy, right? Let's add more actions. We'll add the `ActionPosition` and `ActionFill` actions to the panel. ```tsx import { Designer, DesignerCanvas, DesignerFrame, type DesignerLayer, ActionDimension, ActionPosition, ActionFill, DesignerPanel, DesignerPane, DesignerPaneTitle, DesignerPaneContent, } from "@shadcn/designer" const layers: DesignerLayer[] = [ { id: "1", name: "Heading", type: "text", value: "Hello World", style: { fontSize: "124px", fontWeight: "bold", width: "900px", height: "200px", }, }, ] export default function App() { return ( Layer ) } ``` Actions leverage the composable nature of the designer. This means you can add or remove actions and add multiple panels and panes to build custom designers. ### Add an Image Layer with Actions Now, let's add an image layer to our frame. We'll also add an action to apply filters to the image. ```tsx import { Designer, DesignerCanvas, DesignerFrame, type DesignerLayer, ActionDimension, ActionPosition, ActionFill, ActionImageFit, ActionImageFilter, DesignerPanel, DesignerPane, DesignerPaneTitle, DesignerPaneContent, } from "@shadcn/designer" const layers = [ { id: "1", name: "Heading", type: "text", value: "Hello World", style: { fontSize: "124px", fontWeight: "bold", width: "900px", height: "200px", }, }, { id: "2", name: "Image", type: "image", value: "https://images.unsplash.com/photo-1474966862828-c58886978c8c?q=50&w=3284&auto=format", style: { width: "500px", height: "500px", }, }, ] satisfies DesignerLayer[] export default function App() { return ( Text Image ) } ``` ### Custom Actions We also provide the necessary hooks and UI primitives to create your own actions. Let's create an action to change the text alignment of a layer. We'll call it `ActionTextAlign`. Place the following code in a `components/action-text-align.tsx` file. ```tsx import { useDesignerAction } from "@shadcn/designer" import { Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@shadcn/designer/components" export function ActionTextAlign() { const { value, updateLayers } = useDesignerAction({ property: "textAlign", deserialize: (value) => value ?? "left", serialize: (value) => value ?? "left", }) return (
) } ``` The code above uses the `useDesignerAction` hook to create an action. This hook provides the necessary state and methods to read (deserialize) and update (serialize) the layer style. Add the `ActionTextAlign` component to the designer. ```tsx import { ActionTextAlign } from "@/components/action-text-align" export default function App() { return ( Text Image ) } ``` ### What's Next? That's it for now. In the next update, we'll ship designer presets where you can import a full-fledged designer with a predefined set of actions. We're also working on more documentation and examples. We'd love for you to try it out and give us feedback. --- ## 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 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 | 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` | `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. | ## 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. ```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 ( ) } ``` ## 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 definition. Supply defaults, keyboard shortcuts, and a render function that outputs React nodes. ```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 (