@anupamsahoo/react-modal
Stacked & themeable modal for Tailwind v4
A small React modal component with stacking, variants, animations, and a Tailwind v4–friendly CSS API.
npm install @anupamsahoo/react-modalimport "@anupamsahoo/react-modal/styles.css"Basic modal
A simple modal with header, body, and footer.
Variants, sizes & animations
Try different configurations of the modal.
Variant:
Size:
Animation:
Stacked modals
Open multiple modals. Only the top-most responds to ESC and overlay clicks.
useModalClose()
Close the modal from nested components without prop drilling.
API
<Modal /> props
| Prop | Type | Default | Description |
|---|---|---|---|
| open | boolean | — | Controls whether the modal is visible. |
| onOpenChange | (open: boolean) => void | — | Called when the modal wants to change its open state (ESC, overlay click, close icon). |
| size | "sm" | "md" | "lg" | "xl" | "full" | "lg" | Controls max-width of the modal. |
| animation | "scale" | "slide-up" | "slide-down" | "slide-left" | "slide-right" | "none" | "scale" | Entry animation for the modal panel. |
| variant | "default" | "danger" | "success" | "info" | "default" | Controls border + header accent styling. |
| showCloseIcon | boolean | true | Show the floating close icon in the top-right. |
| disableOutsideClose | boolean | false | If true, clicking the overlay does nothing. |
| disableEscClose | boolean | false | If true, pressing ESC does not close the modal. |
| className | string | — | Extra classes for the modal panel container. |
| children | React.ReactNode | — | Usually composed of ModalHeader / ModalBody / ModalFooter. |
Other components
ModalHeader– header area, usually title + description. Props:className?,children.ModalBody– scrollable body area. Props:className?,children.ModalFooter– footer with actions. Props:className?,children.useModalClose()– hook that returns a() => voidfunction to close the modal from inside.
Code snippets
Basic usage (Next.js + Tailwind)
import * as React from "react";
import {
Modal,
ModalHeader,
ModalBody,
ModalFooter,
} from "@anupamsahoo/react-modal";
import "@anupamsahoo/react-modal/styles.css";
export function BasicExample() {
const [open, setOpen] = React.useState(false);
return (
<>
<button
className="rounded-md px-3 py-2 text-sm bg-slate-900 text-white"
onClick={() => setOpen(true)}
>
Open modal
</button>
<Modal open={open} onOpenChange={setOpen}>
<ModalHeader>
<h2 className="text-lg font-semibold">Simple modal</h2>
<p className="text-sm text-slate-500">
This is a basic example of the modal.
</p>
</ModalHeader>
<ModalBody>
<p className="text-sm">
Put any content here – text, forms, lists, etc.
</p>
</ModalBody>
<ModalFooter>
<button
className="rounded-md px-3 py-2 text-sm border border-slate-300"
onClick={() => setOpen(false)}
>
Close
</button>
<button className="rounded-md px-3 py-2 text-sm bg-slate-900 text-white">
Confirm
</button>
</ModalFooter>
</Modal>
</>
);
}Variants, sizes, animations
<Modal
open={open}
onOpenChange={setOpen}
size="lg" // "sm" | "md" | "lg" | "xl" | "full"
variant="danger" // "default" | "danger" | "success" | "info"
animation="slide-up" // "scale" | "slide-*" | "none"
>
<ModalHeader>...</ModalHeader>
<ModalBody>...</ModalBody>
<ModalFooter>...</ModalFooter>
</Modal>Stacked modals
const [outerOpen, setOuterOpen] = React.useState(false);
const [innerOpen, setInnerOpen] = React.useState(false);
<Modal open={outerOpen} onOpenChange={setOuterOpen}>
...
<button onClick={() => setInnerOpen(true)}>Open inner</button>
</Modal>
<Modal open={innerOpen} onOpenChange={setInnerOpen}>
...
</Modal>useModalClose hook
import { useModalClose } from "@anupamsahoo/react-modal";
function DeepChild() {
const close = useModalClose();
return (
<button onClick={close}>
Close modal
</button>
);
}Tailwind v4 Setup (Recommended)
@anupamsahoo/react-modal is built for Tailwind v4. Follow these steps to ensure proper styling and dark mode support.
1. tailwind.css or app/globals.css
@import "tailwindcss";
/* Required for theme switching */
:root {
color-scheme: light dark;
}
html {
@apply bg-background text-foreground;
}
2. Recommended theme variables
:root {
/* Base colors */
--background: #ffffff;
--foreground: #0f172a;
--card: #ffffff;
--card-foreground: #0f172a;
--border: #e2e8f0;
--muted: #f1f5f9;
--muted-foreground: #64748b;
/* Modal surface + text */
--am-modal-bg: #ffffff;
--am-modal-fg: #0f172a;
--am-modal-border: #e2e8f0;
/* Header + footer */
--am-modal-header-bg: transparent;
--am-modal-header-border: #e2e8f0;
--am-modal-footer-border: #e2e8f0;
/* Overlay (40% opacity and blur from component) */
--am-modal-overlay-bg: rgba(0, 0, 0, 0.4);
/* Semantic variants */
--am-modal-danger-border: #ef4444;
--am-modal-success-border: #22c55e;
--am-modal-info-border: #0ea5e9;
/* Close button */
--am-modal-close-bg: #f1f5f9;
--am-modal-close-bg-hover: #e2e8f0;
--am-modal-close-fg: #0f172a;
/* Radius sync */
--am-modal-radius: 1rem;
}
.dark {
/* Base colors */
--background: #020617;
--foreground: #f8fafc;
--card: #020617;
--card-foreground: #f8fafc;
--border: #334155;
--muted: #1e293b;
--muted-foreground: #94a3b8;
/* Modal surface + text */
--am-modal-bg: #020617;
--am-modal-fg: #f8fafc;
--am-modal-border: #334155;
/* Header + footer */
--am-modal-header-bg: transparent;
--am-modal-header-border: #334155;
--am-modal-footer-border: #334155;
/* Overlay (40% opacity + dark tone) */
--am-modal-overlay-bg: rgba(2, 6, 23, 0.4);
/* Semantic variants */
--am-modal-danger-border: #f43f5e;
--am-modal-success-border: #4ade80;
--am-modal-info-border: #38bdf8;
/* Close button */
--am-modal-close-bg: rgba(255, 255, 255, 0.08);
--am-modal-close-bg-hover: rgba(255, 255, 255, 0.15);
--am-modal-close-fg: #f8fafc;
/* Radius sync */
--am-modal-radius: 1rem;
}3. Enable dark mode toggle
// ThemeToggle.tsx
"use client";
export default function ThemeToggle() {
const toggle = () => {
document.documentElement.classList.toggle("dark");
};
return (
<button
onClick={toggle}
className="fixed right-6 top-6 z-50 rounded-md
bg-slate-900 text-white px-3 py-2 text-xs
dark:bg-slate-100 dark:text-slate-900"
>
Toggle theme
</button>
);
}That’s it — your modal now automatically follows Tailwind v4 + dark/light theme without any extra config.