Morphing Dialog
PronewA card that grows into a dialog (its image, title and subtitle travel with it) and shrinks back on close.
Installation
npx shadcn@latest add @blank-pro/morphing-dialogNeeds a Blank Pro licence key and the @blank-pro registry. Set up Pro access
Usage
import {
MorphingDialog,
MorphingDialogClose,
MorphingDialogContainer,
MorphingDialogContent,
MorphingDialogDescription,
MorphingDialogImage,
MorphingDialogSubtitle,
MorphingDialogTitle,
MorphingDialogTrigger,
} from "@/components/ui/morphing-dialog"<MorphingDialog>
<MorphingDialogTrigger>
<MorphingDialogImage src="/lake.jpg" alt="" />
<MorphingDialogTitle>Moraine Lake</MorphingDialogTitle>
<MorphingDialogSubtitle>Banff, Alberta</MorphingDialogSubtitle>
</MorphingDialogTrigger>
<MorphingDialogContainer>
<MorphingDialogContent>
<MorphingDialogImage src="/lake.jpg" alt="" />
<MorphingDialogTitle>Moraine Lake</MorphingDialogTitle>
<MorphingDialogSubtitle>Banff, Alberta</MorphingDialogSubtitle>
<MorphingDialogDescription>…</MorphingDialogDescription>
<MorphingDialogClose />
</MorphingDialogContent>
</MorphingDialogContainer>
</MorphingDialog>Each part in the trigger has a twin in the content. The surface, image, title
and subtitle share a layout id with their twin, so motion animates one into the
other; MorphingDialogDescription exists only in the dialog and fades in once
the surface has grown.
The dialog is Radix Dialog underneath. Focus moves into it and returns to the
card on close; Escape, a click on the backdrop and the close button dismiss it;
the page stops scrolling. Inside the content, MorphingDialogTitle becomes the
dialog's accessible title and MorphingDialogDescription its description — the
same component in the card stays plain text.
Text morphs by position only, so a title can change size between card and
dialog without stretching. With prefers-reduced-motion, the shared layout
animation is replaced by a short cross-fade.
Examples
Gallery
One MorphingDialog per card. Layout ids are scoped to each instance.
Without media
Any card can morph — here a row turns into a list of shortcuts.
API Reference
MorphingDialog
| Prop | Type | Default |
|---|---|---|
open | boolean | — |
defaultOpen | boolean | false |
onOpenChange | (open: boolean) => void | — |
transition | Transition | { type: "spring", bounce: 0.05, duration: 0.35 } |
MorphingDialogTrigger, MorphingDialogContent
Motion button and div props. Both default to bg-card with a border; the
corner radius is set through style (20 and 24) because motion corrects
an inline radius for scale during the morph.
MorphingDialogContainer
| Prop | Type | Default |
|---|---|---|
className | string | — |
Renders the portal, the backdrop and a centring frame (p-4).
MorphingDialogImage
Motion img props; src and alt are required.
MorphingDialogTitle, MorphingDialogSubtitle
Motion div props.
MorphingDialogDescription
| Prop | Type | Default |
|---|---|---|
variants | { initial, animate, exit } | fade, rise and unblur |
MorphingDialogClose
Motion button props. Renders an X when it has no children.
useMorphingDialog
Returns { open, setOpen } for parts you build yourself — a "Next" button
inside the content, for example.