Arihant jain

Dynamic Island

Component 21

About

An iOS-style pill notification that morphs and springs open into different live activity views like a timer, music player, or call screen.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Dynamic Island to your project and fits it to your theme.

Code

How Dynamic Island is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client";

import * as React from "react";
import {
  DynamicIsland,
  DynamicIslandView,
} from "@/components/ui/dynamic-island";
import { Mic, Music2, Phone, Timer } from "lucide-react";

const VIEWS = [
  { id: "timer", label: "Timer", icon: Timer },
  { id: "music", label: "Music", icon: Music2 },
  { id: "call", label: "Call", icon: Phone },
] as const;

export default function DynamicIslandDemo() {
  const [view, setView] = React.useState<string | null>("timer");

  return (
    <div className="flex w-full flex-col items-center gap-6 py-10">
      <div className="flex min-h-[120px] w-full items-start justify-center">
        <DynamicIsland
          view={view}
          compact={
            <span className="flex items-center gap-2 px-3 py-2">
              <Mic className="size-3.5 text-emerald-400" />
              <span className="size-1.5 rounded-full bg-orange-400" />
            </span>
          }
        >
          <DynamicIslandView id="timer">
            <div className="flex items-center gap-4 px-4 py-3">
              <span className="flex size-9 items-center justify-center rounded-full bg-orange-500/20 text-orange-400">
                <Timer className="size-4" />
              </span>
              <div className="leading-tight">
                <p className="text-[11px] uppercase tracking-wide opacity-60">
                  Focus
                </p>
                <p className="font-mono text-xl tabular-nums">24:59</p>
              </div>
              <button
                type="button"
                className="ms-2 rounded-full bg-background/15 px-3 py-1.5 text-xs font-medium hover:bg-background/25"
              >
                Pause
              </button>
            </div>
          </DynamicIslandView>
          <DynamicIslandView id="music">
            <div className="flex items-center gap-4 px-4 py-3">
              <span className="size-10 rounded-lg bg-gradient-to-br from-fuchsia-500 to-orange-400" />
              <div className="leading-tight">
                <p className="text-sm font-medium">Midnight City</p>
                <p className="text-xs opacity-60">M83</p>
              </div>
              <span className="ms-3 flex items-end gap-0.5">
                {[8, 14, 10, 16].map((h, i) => (
                  <span
                    key={i}
                    className="w-1 rounded-full bg-emerald-400"
                    style={{ height: h }}
                  />
                ))}
              </span>
            </div>
          </DynamicIslandView>
          <DynamicIslandView id="call">
            <div className="flex items-center gap-4 px-4 py-3">
              <div className="flex flex-col">
                <span className="text-[10px] uppercase tracking-wider opacity-60">
                  Incoming call
                </span>
                <span className="text-sm font-semibold">Saurabh</span>
              </div>
              <div className="flex items-center gap-2">
                <button
                  type="button"
                  aria-label="Decline"
                  onClick={() => setView(null)}
                  className="flex size-8 items-center justify-center rounded-full bg-red-500 text-white"
                >
                  <Phone className="size-3.5 rotate-[135deg]" />
                </button>
                <button
                  type="button"
                  aria-label="Accept"
                  onClick={() => setView(null)}
                  className="flex size-8 items-center justify-center rounded-full bg-emerald-500 text-white"
                >
                  <Phone className="size-3.5" />
                </button>
              </div>
            </div>
          </DynamicIslandView>
        </DynamicIsland>
      </div>

      <div className="flex flex-wrap items-center justify-center gap-2">
        <button
          type="button"
          onClick={() => setView(null)}
          className={
            "flex items-center gap-1.5 rounded-full border px-4 py-2 text-sm font-medium transition-colors " +
            (view === null
              ? "border-transparent bg-foreground text-background"
              : "border-border text-foreground hover:bg-accent")
          }
        >
          Compact
        </button>
        {VIEWS.map(({ id, label, icon: Icon }) => (
          <button
            key={id}
            type="button"
            onClick={() => setView(id)}
            className={
              "flex items-center gap-1.5 rounded-full border px-4 py-2 text-sm font-medium transition-colors " +
              (view === id
                ? "border-transparent bg-foreground text-background"
                : "border-border text-foreground hover:bg-accent")
            }
          >
            <Icon className="size-4" />
            {label}
          </button>
        ))}
      </div>
    </div>
  );
}

Dependencies

More by Arihant jain

View all
  • Footer section

    Footer section

    1.9K
  • Multistep Form

    Multistep Form

    701
  • Animated Drawer

    Animated Drawer

    687
  • Status Badge

    Status Badge

    498
  • Stacked Circular Footer

    Stacked Circular Footer

    416
  • Input Modal

    Input Modal

    317
  • Large Name Footer

    Large Name Footer

    194
  • Profile Dropdown

    Profile Dropdown

    189

Explore more

Part of Spectrum UI — browse the full library on 21st.dev.