suiss/ui
  • Docs
  • Components
  • Patterns
  • Interactions
  • Charts
  • Blocks
  • AI
GitHubCreate
DocsComponentsPatternsInteractionsChartsBlocksAICreate
Patterns19
  • Overview
  • Action Bar
  • Action Menu
  • App Shell
  • Command Palette
  • Confirm
  • Context Actions
  • Date Picker
  • Flyout
  • Modal
  • Navbar
  • Paged List
  • Panel
  • Picker
  • Preview
  • Search Picker
  • Segment Picker
  • Split View
  • Table List
  • Trail

suiss/ui · Pattern

Trail

A navigation trail that shows breadcrumbs on desktop and a back link with history on mobile.

Trail is an open-source pattern from suiss/ui for React. The @esuiss/ui CLI copies its source into your project, so you own the code.

import * as React from "react"
import { Trail, type TrailItem } from "@/components/patterns/trail"

const article: TrailItem[] = [
  { label: "Support", href: "#support" },
  { label: "Account", href: "#account" },
  { label: "Security", href: "#security" },
  { label: "Turn on two-factor authentication" },
]

type Page = { label: string; parent?: string; children: string[] }

const pages: Record<string, Page> = {
  support: { label: "Support", children: ["account", "billing"] },
  account: {
    label: "Account",

Trail shows where the current page sits in a hierarchy. On desktop it renders a suiss/ui Breadcrumb that links every ancestor and marks the last item as the current page; with maxItems, it keeps the root and the nearest ancestors and folds the rest into an ellipsis.

On mobile (below 48rem, via useIsMobile) it renders a single back link to the parent page, like an iOS navigation bar. When there is more than one ancestor, a long press, right-click, the Context Menu key or Shift+F10 opens a menu of all ancestors, nearest first. Use it on documentation, support and settings pages with nested structure.

Installation

CLI

$ pnpm dlx shadcn@latest add @esuiss/trail

Works in any project with a components.json. See Installation for the one-line registry setup. Or use the suiss/ui CLI:

$ pnpm dlx @esuiss/ui@latest add patterns trail

Manual

  1. Install the dependencies and add the cn helper:
$ pnpm add @base-ui/react @phosphor-icons/react
  1. Add the pieces it builds on: Breadcrumb, use-mobile.

  2. Copy the source into components/patterns/trail.tsx:

components/patterns/trail.tsx
"use client"

import { Menu as MenuPrimitive } from "@base-ui/react/menu"
import { useRender } from "@base-ui/react/use-render"
import { ArrowBendUpLeftIcon, CaretLeftIcon } from "@phosphor-icons/react"
import * as React from "react"
import {
  Breadcrumb,
  BreadcrumbEllipsis,
  BreadcrumbItem,
  BreadcrumbLink,
  BreadcrumbList,
  BreadcrumbPage,
  BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { useIsMobile } from "@/hooks/use-mobile"
  1. Update the import paths to match your project.

Usage

import { Trail } from "@/components/patterns/trail"
<TrailExample items={article} />
  • Pass items in order from the root to the current page. Each TrailItem has a label and an optional href; the last item is the current page.
  • On mobile, nothing renders when there is only one item.
  • Use render to output links through your router, for example a framework Link element. It applies to breadcrumb links, the mobile back link and the history menu items.

Examples

Collapsed

Folds the middle of a deep trail into an ellipsis with maxItems.

import { Trail, type TrailItem } from "@/components/patterns/trail"

const deep: TrailItem[] = [
  { label: "Support", href: "#support" },
  { label: "Products", href: "#products" },
  { label: "Workspace", href: "#workspace" },
  { label: "Billing", href: "#billing" },
  { label: "Invoices", href: "#invoices" },
  { label: "Download an invoice as PDF" },
]

export default function TrailCollapsed() {
  return <Trail items={deep} maxItems={4} />
}

Back label

Labels the mobile back link Back instead of the parent's name.

import { Trail, type TrailItem } from "@/components/patterns/trail"

const article: TrailItem[] = [
  { label: "Support", href: "#support" },
  { label: "Account", href: "#account" },
  { label: "Security", href: "#security" },
  { label: "Turn on two-factor authentication" },
]

export default function TrailBackLabel() {
  return <Trail items={article} backLabel="Back" />
}

Two levels

Shows a trail with one parent and the current page.

import { Trail, type TrailItem } from "@/components/patterns/trail"

const article: TrailItem[] = [
  { label: "Support", href: "#support" },
  { label: "Account", href: "#account" },
  { label: "Security", href: "#security" },
  { label: "Turn on two-factor authentication" },
]

export default function TrailTwoLevels() {
  return <Trail items={article.slice(2)} />
}

API Reference

01

Trail

The navigation element that renders the breadcrumbs or the mobile back link.

PropTypeDefaultDescription
itemsTrailItem[]RequiredThe pages from the root to the current page.
maxItemsnumber-The maximum number of items shown on desktop before the middle collapses into an ellipsis.
backLabelstring-Text for the mobile back link; defaults to the parent's label.
ellipsisLabelstring"More"The accessible label of the collapsed ellipsis.
renderReact.ReactElement<TrailAnchor />The element used to render links.

Also accepts the props of React.ComponentProps<"nav">.

Accessibility

  • On desktop the trail is a navigation landmark named breadcrumb, and the current page has aria-current="page".
  • On mobile it is a navigation landmark named Back. When a history menu is available, the back link has aria-haspopup="menu" and aria-expanded.
  • The history menu opens from the keyboard with the Context Menu key or Shift+F10.

Notes

Styling hooks: data-slot="trail", trail-back, trail-history and trail-history-item. A long press opens the history only for touch and pen input, and cancels if the pointer moves more than 10 pixels; the press does not also follow the back link.

NextInteractionsPreviousTable List

On this page

  1. 01Installation
    • CLI
    • Manual
  2. 02Usage
  3. 03Examples
    • Collapsed
    • Back label
    • Two levels
  4. 04API Reference
    • Trail
  5. 05Accessibility
  6. 06Notes