Animated Dialog
motiq/animated-dialogFreeComponent
Radix Dialog with Motion enter/exit choreography (scale, slide-up, fade). Preserves portal, focus trap, Esc, and overlay. Reduced-motion safe.
Live preview
live · rendered by the registry
Rendered by motiq on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://www.motiq.dev/r/animated-dialog.jsonSource
1"use client";23import * as React from "react";4import {5 AnimatePresence,6 motion,7 useReducedMotion,8 type Transition,9} from "motion/react";10import * as DialogPrimitive from "@radix-ui/react-dialog";1112import { cn } from "@/lib/utils";1314type Animation = "scale" | "slide-up" | "fade";15type Origin = "center" | "top" | "bottom";16type MobileVariant = "sheet" | "centered";1718const EASE: Transition["ease"] = [0.2, 0, 0, 1];19/** Critically-damped spring — physical settle, no visible bounce. */20const SPRING: Transition = { type: "spring", stiffness: 460, damping: 38, mass: 0.9 };2122/** Desktop content variants keyed by animation style. */23function contentMotion(animation: Animation) {24 switch (animation) {25 case "slide-up":26 return { initial: { opacity: 0, y: 16, scale: 1 }, animate: { opacity: 1, y: 0, scale: 1 }, exit: { opacity: 0, y: 16, scale: 1 } };27 case "fade":28 return { initial: { opacity: 0, y: 0, scale: 1 }, animate: { opacity: 1, y: 0, scale: 1 }, exit: { opacity: 0, y: 0, scale: 1 } };29 case "scale":30 default:31 return { initial: { opacity: 0, y: 0, scale: 0.96 }, animate: { opacity: 1, y: 0, scale: 1 }, exit: { opacity: 0, y: 0, scale: 0.96 } };32 }33}3435/** Mobile bottom-sheet slides up from the bottom edge. */36const SHEET_MOTION = {37 initial: { y: "100%" },38 animate: { y: 0 },39 exit: { y: "100%" },40} as const;4142const ORIGIN_CLASS: Record<Origin, string> = {43 center: "origin-center",44 top: "origin-top",45 bottom: "origin-bottom",46};4748/** SSR-safe media query (defaults to false on the server / first paint). */49function useIsMobile(query = "(max-width: 640px)") {50 const [matches, setMatches] = React.useState(false);51 React.useEffect(() => {52 const mq = window.matchMedia(query);53 const on = () => setMatches(mq.matches);54 on();55 mq.addEventListener("change", on);56 return () => mq.removeEventListener("change", on);57 }, [query]);58 return matches;59}6061export interface AnimatedDialogProps extends React.ComponentPropsWithoutRef<typeof DialogPrimitive.Root> {62 /** Desktop content entrance style. */63 animation?: Animation;64 /** Enter/exit duration in seconds. */65 duration?: number;66 /** Transform origin for the scaling content (desktop). */67 origin?: Origin;68 /** On ≤640px, render as a bottom sheet ("sheet", default) or keep it centered. */69 mobileVariant?: MobileVariant;70}7172interface DialogAnimationContextValue {73 animation: Animation;74 duration: number;75 origin: Origin;76// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from motiq
All 100 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Activity Streamactivity-stream | motiq | Components | Free | 1 dep | |
| Adaptive Safe-Zone Gridadaptive-safe-zone-grid | motiq | Components | Free | no deps | |
| Agent Run Timelineagent-run-timeline | motiq | Components | Free | 1 dep | |
| AI Response Streamai-response-stream | motiq | Components | Free | 1 dep | |
| Animated Accordionanimated-accordion | motiq | Components | Free | 2 deps | |
| Animated Buttonanimated-button | motiq | Components | Free | 1 dep | |
| Animated Gridanimated-grid | motiq | Components | Free | 2 deps | |
| Animated Iconsanimated-icons | motiq | Components | Free | 1 dep |
