Modal Gallery

This example shows how to render two different webpages at the same URL, depending on how you got there. Click the "featured images" and see them full screen. Then "visit the gallery" and click on the colors. Note the URL and the component remain the same but now we see them inside a modal on top of the gallery screen.


import React from "react";
import {
  BrowserRouter as Router, Routes, Route, Link,
  useNavigate, useLocation, useParams,
} from "react-router";

export default function ModalGalleryExample() {
  return (<Router><ModalSwitch /></Router>);
}
function ModalSwitch() {
  let location = useLocation();
  let background = location.state && location.state.background;
  return (
    <div>
      <Routes location={background || location}>
        <Route path="/" element={<Home />} />
        <Route path="/gallery" element={<Gallery />} />
        <Route path="/img/:id" element={<ImageView />} />
      </Routes>
      {/* Show the modal, in its own <Routes>, when a background page is set */}
      {background && (
        <Routes>
          <Route path="/img/:id" element={<Modal />} />
        </Routes>
      )}
    </div>
  );
}
const IMAGES = [
  { id: 0, title: "Dark Orchid", color: "DarkOrchid" },
  { id: 1, title: "Lime Green", color: "LimeGreen" },
  { id: 2, title: "Tomato", color: "Tomato" },
  { id: 3, title: "Seven Ate Nine", color: "#789" },
  { id: 4, title: "Crimson", color: "Crimson" },
];
function Thumbnail({ color }) {
  return (<div style={{ width: 50, height: 50, background: color }} />);
}
function Image({ color }) {
  return (<div style={{ width: "100%", height: 400, background: color }} />);
}
function Home() {
  return (
    <div>
      <Link to="/gallery">Visit the Gallery</Link>
      <h2>Featured Images</h2>
      <ul>
        <li><Link to="/img/2">Tomato</Link></li>
        <li><Link to="/img/4">Crimson</Link></li>
      </ul>
    </div>
  );
}
function Gallery() {
  let location = useLocation();
  return (
    <div>
      {IMAGES.map((i) => (
        // This is the trick! This link sets `background` in location state,
        // via the separate `state` attribute (it's no longer nested inside `to`).
        <Link key={i.id} to={`/img/${i.id}`} state={{ background: location }}>
          <Thumbnail color={i.color} />
          <p>{i.title}</p>
        </Link>
      ))}
    </div>
  );
}
function ImageView() {
  let { id } = useParams();
  let image = IMAGES[parseInt(id, 10)];
  if (!image) return <div>Image not found</div>;
  return (
    <div>
      <h1>{image.title}</h1>
      <Image color={image.color} />
    </div>
  );
}
function Modal() {
  let navigate = useNavigate();
  let { id } = useParams();
  let image = IMAGES[parseInt(id, 10)];
  if (!image) return null;
  let back = (e) => {
    e.stopPropagation();
    navigate(-1);
  };
  return (
    <div
      onClick={back}
      style={{ position: "absolute", top: 0, left: 0, bottom: 0, right: 0, background: "rgba(0, 0, 0, 0.15)" }}
    >
      <div
        className="modal"
        style={{ position: "absolute", background: "#fff", top: 25, left: "10%", right: "10%", padding: 15, border: "2px solid #444" }}
      >
        <h1>{image.title}</h1>
        <Image color={image.color} />
        <button type="button" onClick={back}>Close</button>
      </div>
    </div>
  );
}