DynamicIsland

PreviousNext

Composable, animated Dynamic Island primitives for building adaptive notification, status, and action surfaces.

prev - empty
cur -default

References

Installation

pnpm dlx shadcn@latest add https://cult-ui.com/r/dynamic-island.json

Usage

import { DynamicIsland, DynamicIslandProvider, DynamicContainer, DynamicDiv, DynamicDescription, DynamicTitle, useDynamicIslandSize, } from "@/components/ui/dynamic-island"
const STATES = ["compact", "large", "tall", "long", "medium"] as const function DynamicIslandContent() { const { state, setSize } = useDynamicIslandSize() const cycleState = () => { const currentIndex = STATES.indexOf(state.size as (typeof STATES)[number]) const nextIndex = (currentIndex + 1) % STATES.length setSize(STATES[nextIndex]) } const renderState = () => { switch (state.size) { case "compact": return ( <DynamicContainer className="flex h-full w-full items-center justify-between px-4 text-white"> <DynamicDescription className="text-sm opacity-80">Now playing</DynamicDescription> <DynamicTitle className="text-sm font-semibold">Cult FM</DynamicTitle> </DynamicContainer> ) case "large": return ( <DynamicContainer className="flex h-full w-full items-center justify-center"> <DynamicTitle className="text-2xl font-black text-white">Syncing...</DynamicTitle> </DynamicContainer> ) case "tall": return ( <DynamicContainer className="flex h-full w-full flex-col items-start gap-2 p-5 text-white"> <DynamicTitle className="text-xl font-bold">New message</DynamicTitle> <DynamicDescription className="text-sm text-neutral-300"> Your team invited you to review the latest release. </DynamicDescription> </DynamicContainer> ) default: return ( <DynamicContainer className="flex h-full w-full items-center justify-center"> <DynamicDiv className="text-sm text-white/90">Tap to cycle states</DynamicDiv> </DynamicContainer> ) } } return ( <> <button onClick={cycleState}>Cycle</button> <DynamicIsland id="example-island">{renderState()}</DynamicIsland> </> ) } export default function Example() { return ( <DynamicIslandProvider initialSize="default"> <DynamicIslandContent /> </DynamicIslandProvider> ) }

Anatomy

The Dynamic Island is built from small primitives you compose together:

  • DynamicIslandProvider manages size state and transition queue.
  • DynamicIsland renders the animated shell (width, height, radius).
  • DynamicContainer wraps each state view with enter/exit motion.
  • DynamicTitle, DynamicDescription, and DynamicDiv animate content blocks.
  • useDynamicIslandSize gives you state, setSize, and scheduleAnimation.

Size Presets

Use these built-in shape presets with setSize(...) or initialSize.

PresetIntended use
defaultIdle base state
compactSmall status indicator
compactLongCompact but wider row
compactMediumMedium compact card
largeProminent status/action
longHorizontal content row
mediumMulti-line panel
tallStacked content
ultraLarge immersive panel
massiveFull, oversized layout
minimalLeadingTiny leading indicator
minimalTrailingTiny trailing indicator
resetInternal reset shape
emptyHidden/zero layout

Controlled State Transitions

Switch island layouts directly from app events:

const { setSize } = useDynamicIslandSize() setSize("compact") setSize("medium")

Use the current and previous state to render context-aware UI:

const { state } = useDynamicIslandSize() <span>Current: {state.size}</span> <span>Previous: {state.previousSize}</span>

Scheduled Animations

Queue a sequence of size transitions for guided flows:

import { useScheduledAnimations } from "@/components/ui/dynamic-island" function IntroSequence() { useScheduledAnimations([ { size: "compact", delay: 800 }, { size: "large", delay: 1200 }, { size: "tall", delay: 1400 }, { size: "medium", delay: 1000 }, ]) return null }

delay is the wait time (in milliseconds) before each step.

API Reference

DynamicIslandProvider

PropTypeDefaultDescription
childrenReactNodeProvider contents
initialSizeSizePresets"default"Initial island size
initialAnimation{ size: SizePresets; delay: number }[][]Optional initial transition queue

DynamicIsland

PropTypeDefaultDescription
idstringRequired id for the animated island element
childrenReactNodeState-specific content
...propsHTMLMotionProps<"div">Passed to the root motion container

DynamicContainer

PropTypeDefaultDescription
classNamestringAdditional classes for the animated wrapper
childrenReactNodeContent for current size state

DynamicTitle, DynamicDescription, DynamicDiv

PropTypeDefaultDescription
classNamestringStyling classes
childrenReactNodeAnimated content

useDynamicIslandSize()

Returns:

KeyTypeDescription
state.sizeSizePresetsCurrent active preset
state.previousSizeSizePresets | undefinedPreviously rendered preset
state.animationQueue{ size: SizePresets; delay: number }[]Pending queued transitions
state.isAnimatingbooleanWhether a queued animation sequence is in progress
setSize(size: SizePresets) => voidImmediately switch to a preset
scheduleAnimation(steps: { size: SizePresets; delay: number }[]) => voidQueue timed transitions
presetsRecord<SizePresets, Preset>Raw preset dimensions and radii

Best Practices

  • Keep each size state focused on one job (status, CTA, detail, confirmation).
  • Prefer DynamicContainer per state to keep transitions smooth and predictable.
  • Use state.isAnimating to disable controls during scripted animation sequences.
  • Keep heavy effects or expensive rendering outside frequently changing state views.
  • Place this component in client-rendered UI ("use client"), since it depends on browser size and motion.