---
title: "Header and Navigation"
description: "Complete the header component with auth state awareness, sign-out functionality, and build the protected area sidebar navigation."
canonical_url: "https://vercel.com/academy/subscription-store/header-and-navigation"
md_url: "https://vercel.com/academy/subscription-store/header-and-navigation.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-04-11T20:15:35.246Z"
content_type: "lesson"
course: "subscription-store"
course_title: "Launch a Subscription Store with Vercel and Stripe"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Header and Navigation

# Header and Navigation

Navigation tells users where they are and where they can go. An auth-aware header shows different options based on login state—sign in buttons for guests, account links for users. A sidebar in the protected area helps users navigate between account, billing, and premium features.

## Outcome

Understand how the auth-aware header and protected area sidebar work in the starter.

## Fast Track

1. Review `components/header.tsx` - already in the starter with auth state check
2. Review `components/protected-sidebar.tsx` - already in the starter with navigation links
3. Understand how auth-aware navigation works

## Hands-on Exercise 4.2

Review and understand the navigation system (pre-built in starter):

**What's already implemented:**

1. Header shows different content based on auth state
2. User email displays when signed in
3. Sidebar in protected area links to Account, Membership, Subscription, Field Guide
4. Active state indicated on current page link
5. Field Guide link disabled until user has active subscription

**Review hints:**

- Header is a Server Component that checks auth with `createSupabaseClient()`
- Sidebar uses an `InPageSidebar` component that handles active states
- The `AuthPageSignOutButton` component is used throughout for sign-out

## Try It

1. **Test header (signed out):**
   - Sign out of the app
   - Visit <http://localhost:3000>
   - Header should show "Sign In" button

2. **Test header (signed in):**
   - Sign in to the app
   - Header should show your email and account link

3. **Test sidebar navigation:**
   - Visit <http://localhost:3000/protected>
   - Sidebar should show all navigation links
   - Current page link should be highlighted

4. **Test active states:**
   - Click each sidebar link
   - Active indicator should update to current page

## Done-When

- [ ] Understand how the Header checks auth state server-side
- [ ] Understand how the sidebar gates the Field Guide link
- [ ] Header shows different content when signed in vs signed out
- [ ] Sidebar navigation links work correctly
- [ ] Active page is visually indicated in sidebar

## Solution

The header and sidebar are pre-built in the starter. Here's how they work:

### Header Component

The header in `components/header.tsx` checks auth state server-side:

```typescript title="components/header.tsx"
import Link from "next/link";
import { Button } from "@/components/ui/button";
import { createSupabaseClient } from "@/utils/supabase/server";

export default async function Header() {
  // Try to get the user, gracefully handle if Supabase isn't implemented
  let user = null;
  try {
    const supabase = await createSupabaseClient();
    const { data } = await supabase.auth.getUser();
    user = data.user;
  } catch {
    // Supabase client not implemented yet - show logged out state
  }

  return (
    <nav className="border-b w-full h-16 shrink-0 flex items-center">
      <div className="px-6 w-full flex items-center justify-between mx-auto">
        <Link href="/" className="text-sm font-medium flex items-center gap-2">
          <span>🌿</span>
          Forager's Guild
        </Link>
        <div className="flex items-center gap-2">
          {user ? (
            <>
              <span className="text-sm text-muted-foreground hidden sm:inline">
                {user.email}
              </span>
              <Button variant="outline" size="sm" asChild>
                <Link href="/protected">My Guild</Link>
              </Button>
            </>
          ) : (
            <>
              <Button variant="outline" asChild>
                <Link href="/sign-in">Sign In</Link>
              </Button>
              <Button asChild>
                <Link href="/sign-up">Join Guild</Link>
              </Button>
            </>
          )}
        </div>
      </div>
    </nav>
  );
}
```

Key patterns:

- Server Component uses `createSupabaseClient()` for auth check
- Try/catch wrapper allows the header to work before Supabase is implemented
- Conditional rendering based on `user` state

### Protected Sidebar Component

The sidebar in `components/protected-sidebar.tsx` uses a reusable `InPageSidebar` component:

```typescript title="components/protected-sidebar.tsx"
import InPageSidebar from "@/components/in-page-sidebar";

export default async function ProtectedSidebar() {
  // TODO: Uncomment when hasActiveSubscription is implemented in Section 2:
  // const supabase = await createSupabaseClient();
  // const hasAccess = await hasActiveSubscription(supabase);
  const hasAccess = false; // Hardcoded until Section 2

  return (
    <InPageSidebar
      basePath="/protected"
      items={[
        { label: "My Account", href: "/" },
        { label: "Membership", href: "/pricing" },
        { label: "Subscription", href: "/subscription" },
        { label: "Field Guide", href: "/paid-content", disabled: !hasAccess },
      ]}
    />
  );
}
```

Key patterns:

- `InPageSidebar` handles active state detection automatically
- `disabled` prop gates the Field Guide until subscription is active
- Will integrate with `hasActiveSubscription()` after Section 2

### Layout Structure

The layouts are already configured in the starter:

**Root Layout** (`app/layout.tsx`) includes the Header in the component tree.

**Protected Layout** (`app/protected/layout.tsx`) includes the sidebar:

```typescript title="app/protected/layout.tsx"
import Content from "@/components/content";
import ProtectedSidebar from "@/components/protected-sidebar";

export default function ProtectedLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <Content>
      <div className="flex w-full h-full">
        <ProtectedSidebar />
        <div className="flex-1">{children}</div>
      </div>
    </Content>
  );
}
```

## Navigation Architecture

```
app/layout.tsx
    ↓
<Header /> (Server Component)
    ↓
Check auth with createSupabaseClient()
    ↓
Render: 🌿 Forager's Guild | [Sign In] [Join Guild]   (guest)
        🌿 Forager's Guild | email | [My Guild]       (signed in)

app/protected/layout.tsx
    ↓
<ProtectedSidebar /> (Server Component)
    ↓
<InPageSidebar /> handles active state
    ↓
Render navigation links with active indicators
```

## File Structure

These components are pre-built in the starter:

```
components/
├── header.tsx              ← Auth-aware header
├── protected-sidebar.tsx   ← Protected area sidebar
├── in-page-sidebar.tsx     ← Reusable sidebar component
├── auth-sign-out-button.tsx
├── field-guide-card.tsx
├── pricing-card.tsx
└── ui/
    ├── button.tsx
    └── card.tsx

app/
├── layout.tsx              ← Includes Header
└── protected/
    └── layout.tsx          ← Includes ProtectedSidebar
```

## Troubleshooting

**Header shows wrong auth state:**

- Server components cache aggressively in dev
- Try hard refresh (Cmd+Shift+R / Ctrl+Shift+R)
- Clear cookies and sign in again
- Make sure you've implemented `createSupabaseClient` in Section 1

**Header shows signed-out state even when signed in:**

- Verify `createSupabaseClient` is implemented (not throwing an error)
- The header has a try/catch that falls back to logged-out state on error

**Sidebar active state not updating:**

- The `InPageSidebar` component handles this automatically
- Try navigating via links rather than direct URL entry
- Hard refresh if state seems stuck

**Field Guide link always disabled:**

- This is expected until you implement `hasActiveSubscription()` in Section 3
- Once implemented, uncomment the subscription check in `protected-sidebar.tsx`


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
