You can add your own custom layer types to the designer. The following example shows how to add a custom logo
layer type.
The logo
layer type has a value
property that can be used to store the logo name and a render
function that can be used to render the logo.
Try it out: Click on the logo to select it and pick a different one using the logo picker. You can also change the fill color of the logo using the fill action.
Layer Type
Start by defining the layer type.
import { type LayerType } from "@shadcn/designer"
// Logo data.
const LOGOS = [
{
name: "Default",
path: `<svg .../>`,
},
{
name: "Anthropic",
path: `<svg .../>`,
},
// ...
]
// Custom layer type.
const customLayerTypes = [
{
type: "logo",
name: "Logo",
defaultValues: {
name: "Logo",
value: "Default",
},
render: (layer) => {
const logo = LOGOS.find((logo) => logo.name === layer.value)
if (!logo) {
return null
}
return (
<div
dangerouslySetInnerHTML={{ __html: logo?.path }}
style={{
...layer.contentStyle,
width: "100%",
height: "100%",
}}
/>
)
},
},
] satisfies LayerType[]
Designer
Create the designer with a default layer. This will render a frame with a logo
layer with the default logo.
function CustomDesigner() {
return (
<Designer
layerTypes={customLayerTypes}
defaultLayers={[
{
id: "logo-1",
name: "Logo",
type: "logo",
value: "Default",
cssVars: {
"--width": "400px",
"--height": "400px",
"--translate-x": "312px",
"--translate-y": "312px",
},
},
]}
>
<DesignerContent>
<DesignerCanvas>
<DesignerFrame />
</DesignerCanvas>
</DesignerContent>
</Designer>
)
}
ActionLogoPicker
To add a logo picker action, we can use the <Action
component and the useSetLayersProperty
hook to update the value
property of the logo
layer.
function ActionLogoPicker() {
const selectedLayers = useSelectedLayers()
const layerIds = useSelectedLayerIds()
const setLayersProperty = useSetLayersProperty()
return (
<Action className="gap-0">
<ActionLabel className="sr-only" htmlFor="logo-picker">
Select Logo
</ActionLabel>
<ActionControls>
<Select
disabled={!layerIds.length}
value={selectedLayers[0]?.value ?? ""}
onValueChange={(value) => {
setLayersProperty(layerIds, "value", value)
}}
>
<SelectTrigger id="logo-picker" className="w-28">
<SelectValue placeholder="Select Logo" />
</SelectTrigger>
<SelectContent>
{LOGOS.map((logo) => (
<SelectItem key={logo.name} value={logo.name}>
<span dangerouslySetInnerHTML={{ __html: logo.path }} />
{logo.name}
</SelectItem>
))}
</SelectContent>
</Select>
</ActionControls>
</Action>
)
}
ActionLogoFill
To control the fill color of the logo, we use createLayerCssVarAction
to create an action that can be used to update the fill
CSS variable.
const DEFAULT_FILL = "#000000"
const fillAction = createLayerCssVarAction("--fill", DEFAULT_FILL)
function ActionLogoFill() {
const selectedLayers = useSelectedLayers()
const [fill, setFill] = useLayerCssVarAction(fillAction)
return (
<Action>
<ActionLabel className="sr-only" htmlFor="logo-fill">
Fill
</ActionLabel>
<ActionControls>
<Popover>
<PopoverTrigger asChild>
<Button
className="flex-1 uppercase data-[empty=true]:text-muted-foreground data-[empty=true]:capitalize "
data-empty={fill === ""}
disabled={!selectedLayers.length}
>
<span
className="ml-auto flex size-3 shrink-0 rounded-xs"
style={{ backgroundColor: fill === "" ? DEFAULT_FILL : fill }}
/>
</Button>
</PopoverTrigger>
<PopoverContent side="bottom" className="w-48 p-2" sideOffset={12}>
<ColorPicker value={fill} onValueChange={setFill} />
</PopoverContent>
</Popover>
</ActionControls>
</Action>
)
}