One Logo Pool Ball
wwwwwwwwwwwwwwwwwww

useMatches

Returns an array of all matched routes from the root layout down to the current page. Each match includes the route’s loader data, params, and route ID.

This is useful for:

Basic Usage

loaderData is typed unknown, since a match can come from any route. Give it the type of the loader that produced it:

import { useMatches } from 'one'
type PageData = { title?: string }
function Breadcrumbs() {
const matches = useMatches()
return (
<nav>
{matches.map((match) => (
<a key={match.routeId} href={match.pathname}>
{(match.loaderData as PageData | undefined)?.title ?? match.routeId}
</a>
))}
</nav>
)
}

Accessing Layout Data from a Page

A common use case is accessing a layout’s loader data from a page component:

import { useMatches } from 'one'
import type { DocsLayoutData } from './_layout'
// in a page component
function DocsPage() {
const matches = useMatches()
// find the docs layout match
const docsLayout = matches.find((m) => m.routeId.includes('docs/_layout'))
const navItems = (docsLayout?.loaderData as DocsLayoutData | undefined)?.navItems
return (
<div>
<Sidebar items={navItems} />
{/* page content */}
</div>
)
}

Accessing Page Data from a Layout

Layouts can access their child page’s loader data to render things like a table of contents:

app/docs/_layout.tsx
import { Slot, useMatches } from 'one'
export async function loader() {
return {
navItems: await fetchDocsNav(),
}
}
export type DocsLayoutData = Awaited<ReturnType<typeof loader>>
type DocsPageData = { headings: { id: string; title: string }[] }
export default function DocsLayout() {
const matches = useMatches()
// the last match is the current page
const pageMatch = matches[matches.length - 1]
const headings = (pageMatch?.loaderData as DocsPageData | undefined)?.headings
return (
<div>
<Sidebar />
<main>
<Slot />
</main>
<TableOfContents headings={headings} />
</div>
)
}

RouteMatch Type

Each match in the array has this shape:

type RouteMatch = {
routeId: string // e.g., './docs/_layout.tsx'
pathname: string // e.g., '/docs/intro'
params: Record<string, string | string[]> // URL params like { slug: 'intro' }
loaderData: unknown // data from the route's loader
}

useMatch

Find a specific route’s match by its route ID:

import { useMatch } from 'one'
import type { DocsLayoutData } from './docs/_layout'
function Component() {
// find a specific layout's data
const docsMatch = useMatch('./docs/_layout.tsx')
const navItems = (docsMatch?.loaderData as DocsLayoutData | undefined)?.navItems
}

Returns undefined if the route is not in the current match list.

usePageMatch

Get the current page’s match (the last/deepest match):

import { Head, usePageMatch } from 'one'
type PageMeta = { title?: string; description?: string }
function Layout() {
const pageMatch = usePageMatch()
const { title, description } = (pageMatch?.loaderData as PageMeta | undefined) ?? {}
return (
<Head>
<title>{title}</title>
<meta name="description" content={description} />
</Head>
)
}

Server vs Client Behavior

Edit this page on GitHub.