v6.4.0-pre.2
useMatches

useMatches

Returns the current route matches on the page. This is most useful for creating abstractions in parent layouts to get access to their child route's data.

import { useMatches } from "react-router-dom";

function SomeComponent() {
  const matches = useMatches();
  // [match1, match2, ...]
}

A match has the following shape:

{
  // route id
  id,

  // the portion of the URL the route matched
  pathname,

  // the data from the loader
  data,

  // the parsed params from the URL
  params,

  // the <Route handle> with any app specific data
  handle,
};

Pairing <Route handle> with useMatches gets very powerful since you can put whatever you want on a route handle and have access to useMatches anywhere.

The proverbial use case here is adding breadcrumbs to a parent layout that uses data from the child routes.

ReactDOM.render(
  <DataBrowserRouter>
    <Route element={<Root />}>
      <Route
        path="messages"
        element={<Messages />}
        loader={loadMessages}
        handle={{
          // you can put whatever you want on a route handle
          // here we use "crumb" and return some elements,
          // this is what we'll render in the breadcrumbs
          // for this route
          crumb: () => <Link to="/message">Messages</Link>,
        }}
      >
        <Route
          path="conversation/:id"
          element={<Thread />}
          loader={loadThread}
          handle={{
            // `crumb` is your own abstraction, we decided
            // to make this one a function so we can pass
            // the data from the loader to it so that our
            // breadcrumb is made up of dynamic content
            crumb: (data) => <span>{data.threadName}</span>,
          }}
        />
      </Route>
    </Route>
  </DataBrowserRouter>,
  root
);

Now we can create a Breadcrumbs component that takes advantage of our home-grown crumb abstraction with useMatches and handle.

function Breadcrumbs() {
  let matches = useMatches();
  let crumbs = matches
    // first get rid of any matches that don't have handle and crumb
    .filter((match) => Boolean(match.handle?.crumb))
    // now map them into an array of elements, passing the loader
    // data to each one
    .map((match) => match.handle.crumb(match.data));

  return (
    <ol>
      {crumbs.map((crumb, index) => (
        <li key={index}>{crumb}</li>
      ))}
    </ol>
  );
}

Now you can render <Breadcrumbs/> anywhere you want, probably in the root component.

Docs and examples CC 4.0