Skip to Content
PatternsResource Detail

Resource Detail

The resource detail pattern is used for pages that display a single item in depth — an event page, a news article, a weather location, or a user profile. It complements the Resource Index pattern.

Anatomy

A resource detail page consists of:

  1. Breadcrumb — navigation context (e.g., Events > Music > Harare Jazz Festival)
  2. Header — title, metadata, primary action
  3. Content body — the main content area
  4. Sidebar — related information, actions, metadata (optional, desktop only)
  5. Related items — suggestions at the bottom

Layout

Use the DetailLayout component for consistent detail pages:

<DetailLayout> {/* Breadcrumb */} <Breadcrumb> <BreadcrumbList> <BreadcrumbItem> <BreadcrumbLink href="/events">Events</BreadcrumbLink> </BreadcrumbItem> <BreadcrumbSeparator /> <BreadcrumbItem> <BreadcrumbPage>Harare Jazz Festival</BreadcrumbPage> </BreadcrumbItem> </BreadcrumbList> </Breadcrumb> {/* Header */} <div className="mt-6"> <Badge variant="outline">Music</Badge> <h1 className="mt-2 font-serif text-3xl font-bold">Harare Jazz Festival</h1> <p className="mt-2 text-muted-foreground"> Annual jazz celebration featuring artists from across Southern Africa. </p> </div> {/* Two-column layout */} <div className="mt-8 grid gap-8 lg:grid-cols-[1fr_300px]"> <main>{/* Content body */}</main> <aside className="hidden lg:block">{/* Sidebar */}</aside> </div> </DetailLayout>

Header patterns

With status and actions

<div className="flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between"> <div> <div className="flex items-center gap-2"> <h1 className="font-serif text-3xl font-bold">Event Title</h1> <StatusIndicator status="active" /> </div> <p className="mt-1 text-muted-foreground">Created 3 days ago</p> </div> <div className="flex gap-2"> <Button variant="outline">Edit</Button> <ShareDialog /> </div> </div>

With image

<div className="overflow-hidden rounded-xl"> <AspectRatio ratio={16 / 9}> <img src={imageUrl} alt={title} className="size-full object-cover" /> </AspectRatio> </div> <h1 className="mt-6 font-serif text-3xl font-bold">{title}</h1>

The sidebar typically contains:

<aside className="space-y-6"> {/* Key details card */} <Card> <CardHeader> <CardTitle className="text-base">Details</CardTitle> </CardHeader> <CardContent className="space-y-3"> <div className="flex items-center gap-2 text-sm"> <Calendar className="size-4 text-muted-foreground" /> <span>15 June 2026</span> </div> <div className="flex items-center gap-2 text-sm"> <MapPin className="size-4 text-muted-foreground" /> <span>Harare, Zimbabwe</span> </div> </CardContent> </Card> {/* Actions */} <Card> <CardContent className="pt-6"> <Button className="w-full">Register</Button> </CardContent> </Card> </aside>

Mobile considerations

On mobile, the sidebar content moves below the main content:

<div className="grid gap-8 lg:grid-cols-[1fr_300px]"> <main>{/* Content — always first */}</main> <aside>{/* Sidebar — below on mobile, right on desktop */}</aside> </div>

For critical sidebar content (like a “Register” button), consider duplicating it as a sticky bottom bar on mobile:

<div className="fixed inset-x-0 bottom-0 border-t border-border bg-background p-4 lg:hidden"> <Button className="w-full">Register</Button> </div>

At the bottom of the detail page, show related items using the card grid pattern:

<section className="mt-12"> <h2 className="text-xl font-semibold">Related events</h2> <div className="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3"> {relatedItems.map((item) => ( <Card key={item.id}> <CardHeader> <CardTitle className="text-base">{item.title}</CardTitle> </CardHeader> <CardContent> <p className="text-sm text-muted-foreground">{item.description}</p> </CardContent> </Card> ))} </div> </section>

Back navigation

Always provide a way to return to the index page:

<Button variant="ghost" size="sm" asChild> <a href="/events" className="gap-2"> <ArrowLeft className="size-4" /> Back to events </a> </Button>
Last updated on