Skip to the solution
MediumMachine Coding11 min read

Build a Modal dialog

260 views

The problem

Question presented to candidate: "We need a reusable modal for confirmations. Build a 'Delete project?' dialog: a button opens it over the page, it has Cancel and Delete buttons and a close icon, and it can be dismissed with Escape or by clicking the dark overlay. The page should know how the dialog was closed."

Requirements:

  • A reusable modal component with a title, a description and caller-supplied action buttons (children, slot or projected content).
  • Rendered outside the app's DOM tree, directly under body (portal / Teleport), above everything else, with a full-screen overlay.
  • Closes on Escape, on the close icon, on Cancel, and on a click on the overlay, but not on clicks inside the panel, and not when a text selection is dragged from the panel onto the overlay.
  • Reports the reason it closed; the page shows the result.
  • role="dialog", aria-modal="true", aria-labelledby and aria-describedby; focus moves into the dialog on open and returns to the trigger on close.

What a strong answer should cover:

  • Why a portal: escaping overflow: hidden, z-index stacking contexts and transforms of ancestor elements.
  • Overlay click detection with event.target === event.currentTarget, and the press-started-on-the-overlay check.
  • A document-level Escape listener added only while open, with cleanup.
  • A controlled API: the parent owns open and receives a close reason.
  • Knowing what this version does not do yet (focus trap, inert background, scroll lock) and where that is handled.

Clarifying questions expected:

  • "Is this a confirmation (must answer) or an informational dialog (easy to dismiss)?"
  • "Can dialogs stack, one on top of another?"
  • "Should the page behind stop scrolling?"

Code / implementation expected: Yes. A reusable modal plus a demo page; around 100 to 150 lines.

  • React
  • Portals
  • Accessibility

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

11 min read ยท 9 sections

React solution

Every file of the working solution. Open it in the playground to run and change it.

ReactComplete solution
import { useRef, useState } from "react";
import Modal from "./src/components/Modal";

const REASONS = {
  escape: "Escape key",
  backdrop: "backdrop click",
  close: "close button",
  cancel: "Cancel button",
};

export default function App() {
  const [open, setOpen] = useState(false);
  const [result, setResult] = useState("nothing yet");
  const triggerRef = useRef(null);

  function close(reason) {
    setOpen(false);
    setResult(
      reason === "confirm"
        ? "project deleted"
        : "kept (" + REASONS[reason] + ")",
    );
    // give focus back to the button that opened the dialog
    triggerRef.current.focus();
  }

  return (
    <main className="page">
      <h1>Modal dialog</h1>
      <p>
        Project: <strong>apollo-web</strong>
      </p>
      <button
        ref={triggerRef}
        type="button"
        className="btn danger"
        data-testid="open"
        onClick={() => setOpen(true)}
      >
        Delete project
      </button>
      <p data-testid="result" aria-live="polite">
        Result: {result}
      </p>
      <Modal
        open={open}
        title="Delete apollo-web?"
        description="This removes the project and its 14 deployments. You cannot undo this."
        onClose={close}
      >
        <button
          type="button"
          className="btn"
          data-autofocus
          data-testid="cancel"
          onClick={() => close("cancel")}
        >
          Cancel
        </button>
        <button
          type="button"
          className="btn danger"
          data-testid="confirm"
          onClick={() => close("confirm")}
        >
          Delete
        </button>
      </Modal>
    </main>
  );
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 67 of 107 in Machine coding

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

Up nextMedium

Build a Pagination component

Continue