Components
An accessible, animated modal dialog with focus trapping, scroll locking, backdrop and escape dismissal, and title/description/footer slots.

"use client";
import { Modal } from "@/components/ui/modal";
import { useRef, useState } from "react";
export default function ModalDemo() {
const [confirming, setConfirming] = useState(false);
const keepRef = useRef<HTMLButtonElement>(null);
return (
<div className="flex min-h-[360px] items-center justify-center p-6">
<button
type="button"
onClick={() => setConfirming(true)}
className="rounded-lg border border-stone-200 bg-white px-3.5 py-2 text-[13px] font-medium text-stone-700 shadow-sm transition-colors hover:bg-stone-50 dark:border-white/[0.16] dark:bg-[#1D1D1A] dark:text-stone-200 dark:hover:bg-white/[0.06]"
>
Cancel subscription
</button>
<Modal
open={confirming}
onClose={() => setConfirming(false)}
initialFocusRef={keepRef}
title="Cancel the Team plan?"
description="Billing stops at the end of the period. Projects stay read-only after that."
footer={
<>
<button
ref={keepRef}
type="button"
onClick={() => setConfirming(false)}
className="rounded-[7px] px-3 py-1.5 text-[13px] font-medium text-stone-600 transition-colors hover:bg-stone-100 dark:text-stone-300 dark:hover:bg-white/10"
>
Keep plan
</button>
<button
type="button"
onClick={() => setConfirming(false)}
className="rounded-[7px] bg-stone-900 px-3 py-1.5 text-[13px] font-medium text-white transition-colors hover:bg-stone-800 dark:bg-white dark:text-stone-900 dark:hover:bg-stone-200"
>
Cancel plan
</button>
</>
}
>
<p>Four seats and two production projects are attached to this plan.</p>
</Modal>
</div>
);
}
Part of interior.dev — browse the full library on 21st.dev.