ADR-009: URL-Driven Tab State With Layout-Scoped Context

Prev Next

Context

The Content Detail page maintains multiple tabs with dynamic, registry-driven modules.
Some tabs (e.g. Version History) update query parameters for pagination.

Initially, tab state was stored in a React Context provided at the page level.
This caused the entire detail module to reinitialize on query parameter changes,
resetting UI state and breaking pagination flows.

A clear pattern was needed to:

  • prevent unnecessary state resets
  • support deep linking and reloads
  • work naturally with the Next.js App Router lifecycle

Decision

Tab selection state is treated as navigation state and stored in the URL query parameters.

The Content Detail context provider is mounted at the layout.tsx level to ensure
it remains stable across query parameter changes.

The page.tsx component is allowed to re-run on query changes, enabling automatic
route-level loading behavior.

Any similar tabbed or paginated detail views must follow this pattern:

  • navigation state → URL
  • shared domain state → layout-scoped context
  • data refetching → page remount

Consequences

Enables

  • Tab state survives reloads and supports deep linking
  • Pagination works without resetting shared state
  • Route-level loaders (loading.tsx) work automatically
  • Cleaner separation between navigation state and domain state

Restricts

  • Tab state is not persisted across different detail pages
  • Context must not be relied on for navigation continuity

Trade-offs

  • Accepts page remounts on query changes instead of suppressing navigation
  • Slightly more reliance on URL parsing in UI components

Notes

  • This pattern should be reused for other detail pages with tabs, filters, or pagination
  • Avoid storing navigation-related UI state in React Context
  • Layout boundaries are the primary mechanism for preserving state in App Router