Modern examples of creating a modal component in React: 3 examples that scale

If you’re building any kind of serious UI in 2024, you’ll need at least a few solid examples of creating a modal component in React. Modals handle everything from simple confirmations to complex multi-step flows, and getting them wrong can tank your user experience fast. In this guide, we’ll walk through 3 of the best examples of creating a modal component in React, then expand with several real examples you’ll see in production apps every day. We’ll start with a minimal, accessible modal, move into a reusable modal system with React Portals, and finish with a fully managed modal pattern that can power dashboards, admin tools, and SaaS products. Along the way, we’ll look at real examples from login dialogs, destructive action confirms, multi-step wizards, and more, so you can see how these patterns show up in the wild. If you’ve been copy-pasting random snippets, this is your chance to upgrade to clean, maintainable modal code.
Written by
Jamie
Published

1. The simplest example of creating a modal component in React

Let’s start with the most direct example of creating a modal component in React: a basic, accessible dialog that opens and closes with local state. This is the kind of modal you’d use for a simple “Are you sure?” confirmation or a small info popup.

import React, { useState, useEffect, useRef } from "react";

function BasicModal({ isOpen, onClose, title, children }) {
  const dialogRef = useRef(null);

  // Close on Escape key
  useEffect(() => {
    if (!isOpen) return;

    function handleKeyDown(event) {
      if (event.key === "Escape") {
        onClose();
      }
    }

    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [isOpen, onClose]);

  // Close when clicking the backdrop
  function handleBackdropClick(event) {
    if (event.target === dialogRef.current) {
      onClose();
    }
  }

  if (!isOpen) return null;

  return (
    <div
      ref={dialogRef}
      onClick={handleBackdropClick}
      style={styles.backdrop}
      aria-modal="true"
      role="dialog"
      aria-labelledby="modal-title"
    >
      <div style={styles.modal}>
        <header style={styles.header}>
          <h2 id="modal-title">{title}</h2>
          <button onClick={onClose} aria-label="Close modal">
            ×
          </button>
        </header>
        <div style={styles.body}>{children}</div>
      </div>
    </div>
  );
}

const styles = {
  backdrop: {
    position: "fixed",
    inset: 0,
    backgroundColor: "rgba(0, 0, 0, 0.5)",
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    zIndex: 1000
  },
  modal: {
    backgroundColor: "white",
    padding: "1.5rem",
    borderRadius: "0.5rem",
    minWidth: "320px",
    maxWidth: "90%"
  },
  header: {
    display: "flex",
    alignItems: "center",
    justifyContent: "space-between",
    marginBottom: "1rem"
  },
  body: {
    fontSize: "0.95rem"
  }
};

export default function BasicModalExample() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>Open basic modal</button>
      <BasicModal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        title="Delete item"
      >
        <p>Are you sure you want to delete this item? This action cannot be undone.</p>
        <div style={{ marginTop: "1rem", textAlign: "right" }}>
          <button onClick={() => setIsOpen(false)} style={{ marginRight: "0.5rem" }}>
            Cancel
          </button>
          <button style={{ backgroundColor: "#dc2626", color: "white" }}>
            Delete
          </button>
        </div>
      </BasicModal>
    </div>
  );
}

This is the first of our three core examples of creating a modal component in React. It covers:

  • Local state for isOpen
  • Escape key handling
  • Backdrop click to close
  • Basic ARIA attributes

Real example uses:

  • Delete confirmation in a todo app
  • “Unsaved changes” warning before navigating away
  • Short privacy notice or cookie preference prompt

Even at this level, you’re already using a pattern that’s common in production. Many of the best examples of small, focused modals look almost exactly like this.


2. Portal-based modal: a stronger example of creating a modal component in React

Once your app grows, you quickly hit layout problems: modals get clipped by overflow: hidden, or are affected by parent z-index values. That’s where React Portals come in.

This second example of creating a modal component in React shows how to render the modal at the end of document.body, no matter where the triggering component lives.

import React, { useEffect } from "react";
import { createPortal } from "react-dom";

function PortalModal({ isOpen, onClose, children }) {
  const modalRoot = document.getElementById("modal-root") || document.body;

  useEffect(() => {
    if (!isOpen) return;

    function handleKeyDown(event) {
      if (event.key === "Escape") {
        onClose();
      }
    }

    document.addEventListener("keydown", handleKeyDown);
    return () => document.removeEventListener("keydown", handleKeyDown);
  }, [isOpen, onClose]);

  if (!isOpen) return null;

  return createPortal(
    <div style={backdropStyles} onClick={onClose}>
      <div style={modalStyles} onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    modalRoot
  );
}

const backdropStyles = {
  position: "fixed",
  inset: 0,
  backgroundColor: "rgba(0,0,0,0.6)",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  zIndex: 2000
};

const modalStyles = {
  backgroundColor: "#111827",
  color: "white",
  padding: "2rem",
  borderRadius: "0.75rem",
  maxWidth: "480px",
  width: "100%"
};

export default function PortalModalExample() {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <div style={{ padding: "3rem", position: "relative", zIndex: 1 }}>
      <button onClick={() => setIsOpen(true)}>Open portal modal</button>

      <div style={{ marginTop: "2rem", height: "120px", overflow: "hidden" }}>
        <p>
          This container has <code>overflow: hidden</code>, but the portal modal still appears
          above everything because it renders at the document root.
        </p>
      </div>

      <PortalModal isOpen={isOpen} onClose={() => setIsOpen(false)}>
        <h2 style={{ marginTop: 0 }}>Login required</h2>
        <p>You need to log in to continue. Please sign in with your account.</p>
        <form style={{ marginTop: "1rem" }}>
          <label style={{ display: "block", marginBottom: "0.5rem" }}>
            Email
            <input type="email" style={{ display: "block", width: "100%", marginTop: "0.25rem" }} />
          </label>
          <label style={{ display: "block", marginBottom: "0.5rem" }}>
            Password
            <input type="password" style={{ display: "block", width: "100%", marginTop: "0.25rem" }} />
          </label>
          <button type="submit" style={{ marginTop: "0.75rem" }}>
            Sign in
          </button>
        </form>
      </PortalModal>
    </div>
  );
}

In many real examples from large dashboards and design systems, this portal-based pattern is the default. It avoids layout bugs and makes the modal visually consistent across the app.

Where this pattern shines:

  • Login or signup dialogs
  • Payment or subscription popups
  • Global notifications that should always appear on top

If you’re looking for the best examples of creating a modal component in React that scale past toy projects, you want something like this portal version.


3. A managed modal system: the most flexible example of creating a modal component in React

The third of our 3 core examples of creating a modal component in React is a modal system managed by React Context. Instead of each component owning its own isOpen state, you centralize modal control.

This is the pattern you’ll see in many production apps where:

  • Any component can open a modal without prop drilling
  • Only one modal should be open at a time
  • You want consistent animations, theming, and behavior

Context-based modal provider

import React, { createContext, useContext, useState, useCallback } from "react";
import { createPortal } from "react-dom";

const ModalContext = createContext(null);

export function useModal() {
  const ctx = useContext(ModalContext);
  if (!ctx) {
    throw new Error("useModal must be used within a ModalProvider");
  }
  return ctx;
}

export function ModalProvider({ children }) {
  const [modalContent, setModalContent] = useState(null);

  const openModal = useCallback(content => {
    setModalContent(() => content);
  }, []);

  const closeModal = useCallback(() => {
    setModalContent(null);
  }, []);

  return (
    <ModalContext.Provider value={{ openModal, closeModal }}>
      {children}
      {modalContent && (
        <ModalRoot onClose={closeModal}>{modalContent}</ModalRoot>
      )}
    </ModalContext.Provider>
  );
}

function ModalRoot({ children, onClose }) {
  const modalRoot = document.getElementById("modal-root") || document.body;

  return createPortal(
    <div style={rootBackdrop} onClick={onClose}>
      <div style={rootModal} onClick={e => e.stopPropagation()}>
        {children}
      </div>
    </div>,
    modalRoot
  );
}

const rootBackdrop = {
  position: "fixed",
  inset: 0,
  backgroundColor: "rgba(15,23,42,0.75)",
  display: "flex",
  alignItems: "center",
  justifyContent: "center",
  zIndex: 3000
};

const rootModal = {
  backgroundColor: "white",
  borderRadius: "0.75rem",
  padding: "1.5rem",
  maxWidth: "600px",
  width: "100%",
  boxShadow: "0 20px 40px rgba(15,23,42,0.35)"
};

Using the managed modal

function DeleteUserButton({ user }) {
  const { openModal, closeModal } = useModal();

  function handleClick() {
    openModal(
      <div>
        <h2>Delete {user.name}?</h2>
        <p>
          This will permanently remove the user and their data. This is often used in real
          admin panel examples where destructive actions must be confirmed.
        </p>
        <div style={{ marginTop: "1rem", textAlign: "right" }}>
          <button onClick={closeModal} style={{ marginRight: "0.5rem" }}>
            Cancel
          </button>
          <button
            style={{ backgroundColor: "#b91c1c", color: "white" }}
            onClick={() => {
              // Perform delete logic here
              closeModal();
            }}
          >
            Confirm delete
          </button>
        </div>
      </div>
    );
  }

  return <button onClick={handleClick}>Delete user</button>;
}

export default function ManagedModalExample() {
  const user = { id: 1, name: "Ada Lovelace" };

  return (
    <ModalProvider>
      <div style={{ padding: "2rem" }}>
        <h1>User settings</h1>
        <DeleteUserButton user={user} />
      </div>
    </ModalProvider>
  );
}

This managed system is one of the best examples of creating a modal component in React for larger apps because it:

  • Keeps modal logic in one place
  • Supports many different modal layouts
  • Makes it trivial to open modals from deep in the component tree

Real-world examples include:

  • Bulk action confirmation in admin dashboards
  • “Invite team member” forms in SaaS products
  • “Upgrade plan” paywalls in subscription apps

6+ real examples of modal usage patterns you’ll actually build

So far you’ve seen 3 core examples of creating a modal component in React. Let’s anchor those with at least six specific, real examples of how these patterns show up in modern apps.

1. Delete confirmation modal
Uses: Basic or managed modal.
Behavior: Short text, two buttons, keyboard support.
Why it matters: Reduces accidental destructive actions. Even government and healthcare sites often use this pattern for sensitive data operations.

2. Login / authentication modal
Uses: Portal or managed modal.
Behavior: Form fields, validation messages, social login buttons.
Real examples include: OAuth sign-in prompts, “Continue with Google” overlays, or 2FA entry dialogs.

3. Multi-step onboarding wizard
Uses: Managed modal with internal step state.
Behavior: Next/Back navigation, progress indicator, maybe autosave.
You’ll see this in analytics tools guiding users through first-time setup.

4. Settings / preferences modal
Uses: Portal modal with tabs or sections.
Behavior: Toggle switches, dropdowns, save/cancel.
Great for notification settings, theme preferences, or privacy controls.

5. Full-screen mobile modal (drawer)
Uses: Portal modal styled as full-screen on small viewports.
Behavior: Slide-in animation, close on swipe or tap.
Common in responsive designs where the same component is a centered dialog on desktop and a full-screen overlay on mobile.

6. Data-rich detail modal
Uses: Managed modal system.
Behavior: Shows charts, tables, and related data without navigating away.
Real examples include: Clicking a row in a metrics table to open a modal with time-series charts and filters.

7. Regulatory / consent modal
Uses: Basic or portal modal.
Behavior: Presents legal or compliance text, requires explicit consent.
You’ll see this in healthcare or financial apps that must display disclosures before letting users proceed. These often draw on regulatory guidance from sources like the U.S. Department of Health and Human Services (HHS) for health privacy and consent patterns: https://www.hhs.gov.

These real examples make it easier to choose which of the 3 examples of creating a modal component in React you should start from in your own project.


Accessibility: making your React modal usable for everyone

Modals are not just a design problem; they’re an accessibility problem. Poorly implemented dialogs can trap keyboard users or confuse screen readers.

When you look at the best examples of creating a modal component in React from serious teams, you’ll notice a few consistent patterns:

  • Focus management: Move focus into the modal when it opens, and return focus to the triggering element when it closes.
  • Focus trapping: Keep Tab navigation inside the modal while it’s open.
  • ARIA roles and labels: Use role="dialog" or role="alertdialog", aria-modal="true", and connect your title with aria-labelledby.
  • No background scroll: Prevent the page behind the modal from scrolling when the dialog is open.

For more detail on accessible dialogs, the W3C’s WAI-ARIA Authoring Practices includes real examples and patterns for modal dialogs:
https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/

If you want production-grade accessibility without reinventing the wheel, consider:

  • Reach UI Dialog: https://reach.tech/dialog
  • Radix UI Dialog: https://www.radix-ui.com/primitives/docs/components/dialog

Both projects are widely used and keep up with modern React patterns as of 2024–2025.


React in 2024–2025 is heavily shaped by concurrent rendering, Server Components, and client-side transitions. That changes how you think about modals a bit, especially in larger apps.

Some current trends you’ll see in modern examples of creating a modal component in React:

  • Code-splitting modal content: Heavy modal content (charts, editors) is loaded lazily only when needed using React.lazy and Suspense.
  • Optimistic UI for modals: Actions triggered from modals (like “Archive project”) show instant optimistic updates while the server call runs.
  • Integration with routing: Modals mapped to routes (e.g., /items/123?modal=details) so users can share URLs that open a specific modal.
  • Design system integration: Many teams define a single <Modal /> primitive and build all other dialogs on top of it to keep behavior consistent.

Here’s a quick sketch of lazy-loading a heavy modal:

const HeavyReportModal = React.lazy(() => import("./HeavyReportModal"));

function ReportsPage() {
  const [isOpen, setIsOpen] = React.useState(false);

  return (
    <div>
      <button onClick={() => setIsOpen(true)}>Open report</button>
      {isOpen && (
        <React.Suspense fallback={<div>Loading report…</div>}>
          <HeavyReportModal onClose={() => setIsOpen(false)} />
        </React.Suspense>
      )}
    </div>
  );
}

This pattern shows up in many of the best examples of data-heavy dashboards where modals might contain charts, maps, or WYSIWYG editors.


FAQ: common questions about React modals

What are some real examples of creating a modal component in React?

Real examples of creating a modal component in React include delete confirmations, login dialogs, multi-step onboarding wizards, settings modals, full-screen mobile drawers, and data-rich detail views that open from tables or charts. The three patterns in this article—basic modal, portal modal, and managed modal system—cover almost all of these use cases.

Which example of modal implementation should I start with?

If you’re working on a small app or learning, start with the basic example of creating a modal component in React using local state. If your layout gets more complex or you hit z-index issues, move to the portal-based example. For larger apps with many dialogs, a managed modal system with context usually pays off quickly.

How do I make my React modal accessible?

Use role="dialog" or role="alertdialog", aria-modal="true", and link your heading with aria-labelledby. Move focus into the modal when it opens, trap focus while it’s open, and return focus to the trigger when it closes. The W3C’s dialog pattern page (https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/) includes detailed guidance and examples of accessible dialogs.

Can I reuse these examples of creating a modal component in React with libraries like Redux or React Query?

Yes. The examples of creating a modal component in React shown here are state-management agnostic. You can trigger modals from Redux actions, React Query mutations, or any other data layer. For complex flows, many teams combine a managed modal system with their global state to coordinate what’s open, what data is loaded, and what happens after a user confirms or cancels.

Are there production-ready libraries with good examples of modals?

Yes. Libraries like Reach UI and Radix UI provide production-grade dialog components with strong accessibility defaults. Their documentation includes real examples of creating a modal component in React, including nested dialogs, scrollable content, and animated transitions. Studying those patterns is a good way to sharpen your own implementation.

Explore More React Code Snippets

Discover more examples and insights in this category.

View All React Code Snippets