Native Modal
Built on the native HTML dialog element for performance and accessibility.
Installation
1. Copy the React component into your project:
tsx
"use client";
import { ReactNode, useEffect, useRef } from "react";
interface ModalProps {
isOpen: boolean;
onClose: () => void;
title?: string;
children: ReactNode;
}
/**
* A lightweight modal component using the native HTML <dialog> element.
*/
export const Modal = ({ isOpen, onClose, title, children }: ModalProps) => {
const dialogRef = useRef<HTMLDialogElement>(null);
useEffect(() => {
const dialog = dialogRef.current;
if (!dialog) return;
if (isOpen) {
if (!dialog.open) {
dialog.showModal();
}
} else {
if (dialog.open) {
dialog.close();
}
}
}, [isOpen]);
const handleBackdropClick = (e: React.MouseEvent) => {
if (e.target === dialogRef.current) {
onClose();
}
};
return (
<dialog
ref={dialogRef}
onClose={onClose}
onClick={handleBackdropClick}
style={{
padding: 0,
border: "none",
borderRadius: "1rem",
background: "var(--bg-primary)",
color: "var(--text-primary)",
boxShadow: "0 25px 50px -12px rgba(0, 0, 0, 0.25)",
maxWidth: "500px",
width: "90%",
margin: "auto",
}}
>
<div style={{ padding: "1.5rem" }}>
<div
style={{
display: "flex",
justifyContent: "space-between",
alignItems: "center",
marginBottom: "1.5rem",
borderBottom: "1px solid var(--border-color)",
paddingBottom: "1rem",
}}
>
{title && (
<h2 style={{ fontSize: "1.25rem", fontWeight: 700 }}>{title}</h2>
)}
<button
onClick={onClose}
aria-label="Close modal"
style={{
background: "none",
border: "none",
cursor: "pointer",
fontSize: "1.5rem",
color: "var(--text-muted)",
display: "flex",
padding: "0.25rem",
}}
>
×
</button>
</div>
<div>{children}</div>
</div>
<style>{`
dialog::backdrop {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
dialog[open] {
animation: modal-fade-in 0.3s ease-out;
}
@keyframes modal-fade-in {
from { opacity: 0; transform: translateY(-20px); }
to { opacity: 1; transform: translateY(0); }
}
`}</style>
</dialog>
);
};
Usage
tsx
import { useState } from 'react';
import { Modal } from '@/components/ui/modal';
import { Button } from '@/components/ui/button';
export default function MyComponent() {
const [isOpen, setIsOpen] = useState(false);
return (
<>
<Button onClick={() => setIsOpen(true)}>Open</Button>
<Modal isOpen={isOpen} onClose={() => setIsOpen(false)} title="Settings">
<p>Your settings go here.</p>
</Modal>
</>
);
}