All Docs
FeaturesMaking Tax DigitalUpdated February 20, 2026

Active Navigation State in the Sidebar — v1.0.19

Active Navigation State in the Sidebar

Introduced in v1.0.19

Overview

The dashboard sidebar now clearly highlights the link that corresponds to the page you are currently viewing. This makes it easy to orient yourself within the application at a glance, without needing to read the page heading or URL bar.

What Was the Problem?

Prior to v1.0.19, all 10 sidebar links were rendered with identical styling:

text-muted-foreground hover:bg-muted hover:text-foreground

No active state classes were applied. Every link looked the same whether you were on that page or not, making the sidebar effectively useless as a location indicator.

How It Works Now

The sidebar uses the Next.js usePathname() hook (via a dedicated SidebarNav client component) to read the current route on every navigation and apply the appropriate styles.

Active Link Styles

When a link's href matches the current route, it receives:

bg-muted text-foreground font-semibold

This gives the active link a filled background, full-contrast text colour, and bold weight — making it visually distinct from all inactive links.

Matching Logic

Match TypeConditionExample
Exact matchpathname === href/dashboard/submissions
Section matchpathname.startsWith(href)/dashboard/transactions/123 highlights the Transactions link

The startsWith check ensures that nested or detail pages (e.g. a specific transaction or submission record) still highlight the correct parent section in the sidebar.

Technical Implementation

Because usePathname() is a React client-side hook, it cannot be used directly inside a Next.js server component. The fix involved:

  1. Keeping layout.tsx as a server component — preserving server-side rendering benefits for the overall dashboard shell.
  2. Creating a SidebarNav client component — a dedicated 'use client' component responsible solely for rendering the list of nav links with active state awareness.
  3. Composing them togetherlayout.tsx renders <SidebarNav /> inside the sidebar, passing the route definitions as props.

This pattern keeps the client boundary as narrow as possible while enabling dynamic active state detection.

Before & After

Before (all links identical):

// Every link — same classes, no active awareness
<Link
  href="/dashboard/submissions"
  className="text-muted-foreground hover:bg-muted hover:text-foreground"
>
  Submissions
</Link>

After (active link highlighted):

// Inside SidebarNav client component
'use client';
import { usePathname } from 'next/navigation';

const pathname = usePathname();

<Link
  href={href}
  className={
    pathname === href || pathname.startsWith(href)
      ? 'bg-muted text-foreground font-semibold'
      : 'text-muted-foreground hover:bg-muted hover:text-foreground'
  }
>
  {label}
</Link>