Code To Learn logo

Code To Learn

M7: Forms & Authentication

L14: Update Routes with Protection

Apply route guards across your application

Let's protect all routes that require authentication! 🛡️

Route Organization Strategy

Good practice: Group routes by access level:

<Routes>
  {/* ===== PUBLIC ROUTES ===== */}
  <Route path="/" element={<HomePage />} />
  <Route path="/about" element={<AboutPage />} />
  <Route path="/venues/:id" element={<VenueDetailPage />} />
  
  {/* ===== AUTH ROUTES ===== */}
  <Route path="/sign-in" element={<SignInPage />} />
  <Route path="/sign-up" element={<SignUpPage />} />
  
  {/* ===== PROTECTED ROUTES ===== */}
  <Route path="/favorites" element={<Protected><FavoritesPage /></Protected>} />
  <Route path="/bookings" element={<Protected><BookingsPage /></Protected>} />
  <Route path="/profile" element={<Protected><ProfilePage /></Protected>} />
  
  {/* ===== 404 ===== */}
  <Route path="*" element={<NotFoundPage />} />
</Routes>

Complete App with Protected Routes

Update your App component with all routes:

src/App.jsx
import { Routes, Route } from 'react-router-dom';
import { useAuth } from '@/contexts/AuthContext';
import Protected from '@/components/Protected';

// Public pages
import HomePage from '@/pages/HomePage';
import AboutPage from '@/pages/AboutPage';
import VenueDetailPage from '@/pages/VenueDetailPage';

// Auth pages
import SignInPage from '@/pages/SignInPage';
import SignUpPage from '@/pages/SignUpPage';
import ForgotPasswordPage from '@/pages/ForgotPasswordPage';

// Protected pages
import FavoritesPage from '@/pages/FavoritesPage';
import BookingsPage from '@/pages/BookingsPage';
import ProfilePage from '@/pages/ProfilePage';
import CreateVenuePage from '@/pages/CreateVenuePage';
import MyVenuesPage from '@/pages/MyVenuesPage';
import SettingsPage from '@/pages/SettingsPage';

// Other
import NotFoundPage from '@/pages/NotFoundPage';

function AppContent() {
  const { user, isLoading } = useAuth();

  if (isLoading) {
    return (
      <div className="loading-container">
        <div className="spinner"></div>
        <p>Loading application...</p>
      </div>
    );
  }

  return (
    <div className="app">
      {user && <Navbar />}
      
      <main className="main-content">
        <Routes>
          {/* ===== PUBLIC ROUTES ===== */}
          <Route path="/" element={<HomePage />} />
          <Route path="/about" element={<AboutPage />} />
          <Route path="/venues/:id" element={<VenueDetailPage />} />
          
          {/* ===== AUTHENTICATION ROUTES ===== */}
          <Route path="/sign-in" element={<SignInPage />} />
          <Route path="/sign-up" element={<SignUpPage />} />
          <Route path="/forgot-password" element={<ForgotPasswordPage />} />
          
          {/* ===== PROTECTED ROUTES ===== */}
          {/* User Account */}
          <Route 
            path="/profile" 
            element={
              <Protected>
                <ProfilePage />
              </Protected>
            } 
          />
          <Route 
            path="/settings" 
            element={
              <Protected>
                <SettingsPage />
              </Protected>
            } 
          />
          
          {/* User Actions */}
          <Route 
            path="/favorites" 
            element={
              <Protected>
                <FavoritesPage />
              </Protected>
            } 
          />
          <Route 
            path="/bookings" 
            element={
              <Protected>
                <BookingsPage />
              </Protected>
            } 
          />
          
          {/* Venue Management */}
          <Route 
            path="/venues/create" 
            element={
              <Protected>
                <CreateVenuePage />
              </Protected>
            } 
          />
          <Route 
            path="/venues/mine" 
            element={
              <Protected>
                <MyVenuesPage />
              </Protected>
            } 
          />
          
          {/* ===== 404 NOT FOUND ===== */}
          <Route path="*" element={<NotFoundPage />} />
        </Routes>
      </main>
    </div>
  );
}

function App() {
  return (
    <AuthProvider>
      <BrowserRouter>
        <AppContent />
      </BrowserRouter>
    </AuthProvider>
  );
}

export default App;

Route Structure Breakdown

Routes anyone can access:

{/* ===== PUBLIC ROUTES ===== */}
<Route path="/" element={<HomePage />} />
<Route path="/about" element={<AboutPage />} />
<Route path="/contact" element={<ContactPage />} />
<Route path="/venues" element={<VenuesPage />} />
<Route path="/venues/:id" element={<VenueDetailPage />} />

Characteristics:

  • ✅ No authentication required
  • ✅ Accessible to everyone
  • ✅ Good for marketing/info pages
  • ✅ Search engines can index

Use cases:

Homepage: Landing, hero, features
About: Company info, team
Venues: Browse all venues (public gallery)
Venue Detail: View specific venue
Contact: Contact form
Terms: Legal documents

Conditional content within public pages:

function HomePage() {
  const { user } = useAuth();
  
  return (
    <div>
      <h1>Welcome to Holidaze</h1>
      
      {/* Public content */}
      <VenueGrid venues={allVenues} />
      
      {/* Show different CTAs based on auth */}
      {user ? (
        <Button to="/venues/create">List Your Venue</Button>
      ) : (
        <Button to="/sign-up">Get Started</Button>
      )}
    </div>
  );
}

Routes for authentication flow:

{/* ===== AUTHENTICATION ROUTES ===== */}
<Route path="/sign-in" element={<SignInPage />} />
<Route path="/sign-up" element={<SignUpPage />} />
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
<Route path="/reset-password/:token" element={<ResetPasswordPage />} />
<Route path="/verify-email/:token" element={<VerifyEmailPage />} />

Special behavior: Redirect if already signed in

function SignInPage() {
  const { user } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    if (user) {
      // Already signed in, redirect to home
      navigate('/', { replace: true });
    }
  }, [user, navigate]);

  if (user) {
    return null; // or <Navigate to="/" />
  }

  return <SignInForm />;
}

Why redirect?

User is signed in → Visits /sign-in
Without redirect: Shows sign-in form (confusing)
With redirect: Sends to home (makes sense)

Auth route patterns:

/sign-in        → Sign in to existing account
/sign-up        → Create new account
/forgot-password → Request password reset
/reset-password/:token → Set new password
/verify-email/:token → Verify email address
/logout         → Sign out (redirect to home)

Routes requiring authentication:

{/* ===== PROTECTED ROUTES ===== */}

{/* User Profile */}
<Route path="/profile" element={<Protected><ProfilePage /></Protected>} />
<Route path="/profile/edit" element={<Protected><EditProfilePage /></Protected>} />
<Route path="/settings" element={<Protected><SettingsPage /></Protected>} />

{/* User Content */}
<Route path="/favorites" element={<Protected><FavoritesPage /></Protected>} />
<Route path="/bookings" element={<Protected><BookingsPage /></Protected>} />
<Route path="/bookings/:id" element={<Protected><BookingDetailPage /></Protected>} />

{/* Venue Management */}
<Route path="/venues/create" element={<Protected><CreateVenuePage /></Protected>} />
<Route path="/venues/mine" element={<Protected><MyVenuesPage /></Protected>} />
<Route path="/venues/:id/edit" element={<Protected><EditVenuePage /></Protected>} />

{/* Messages/Notifications */}
<Route path="/messages" element={<Protected><MessagesPage /></Protected>} />
<Route path="/notifications" element={<Protected><NotificationsPage /></Protected>} />

Route organization tips:

By feature:
  /profile, /profile/edit, /settings  → User management
  /favorites, /bookings               → User content
  /venues/create, /venues/mine        → Venue management

By hierarchy:
  /dashboard                         → Main dashboard
  /dashboard/analytics              → Sub-section
  /dashboard/analytics/reports      → Nested section

Common route patterns:

1. Resource routes:

<Route path="/venues" element={<VenuesPage />} />          // List
<Route path="/venues/:id" element={<VenueDetailPage />} /> // Detail
<Route path="/venues/create" element={<CreateVenuePage />} /> // Create
<Route path="/venues/:id/edit" element={<EditVenuePage />} /> // Edit

Order matters:

// ✅ Correct order
<Route path="/venues/create" />  // Matches first
<Route path="/venues/:id" />     // Matches after

// ❌ Wrong order
<Route path="/venues/:id" />     // Matches "create" as id!
<Route path="/venues/create" />  // Never matches

2. Nested routes:

<Route path="/dashboard" element={<DashboardLayout />}>
  <Route index element={<DashboardHome />} />
  <Route path="analytics" element={<Analytics />} />
  <Route path="reports" element={<Reports />} />
</Route>

// URLs:
// /dashboard           → DashboardHome
// /dashboard/analytics → Analytics
// /dashboard/reports   → Reports

3. Optional parameters:

<Route path="/search" element={<SearchPage />} />
// URL: /search?q=beach&guests=4

function SearchPage() {
  const [searchParams] = useSearchParams();
  const query = searchParams.get('q');
  const guests = searchParams.get('guests');
}

4. Multiple parameters:

<Route path="/venues/:venueId/reviews/:reviewId" />

function ReviewPage() {
  const { venueId, reviewId } = useParams();
  // venueId = "123"
  // reviewId = "456"
}

5. Catch-all routes:

<Route path="/docs/*" element={<DocsLayout />}>
  <Route path="*" element={<DocPage />} />
</Route>

// Matches:
// /docs/getting-started
// /docs/api/reference
// /docs/any/nested/path

Show different links based on authentication:

src/components/Navbar.jsx
import { Link } from 'react-router-dom';
import { useAuth } from '@/contexts/AuthContext';

function Navbar() {
  const { user } = useAuth();

  return (
    <nav className="navbar">
      <Link to="/" className="nav-brand">
        🏖️ Holidaze
      </Link>

      <div className="nav-links">
        {/* Always visible */}
        <Link to="/">Home</Link>
        <Link to="/venues">Venues</Link>

        {user ? (
          // Signed in: Show protected links
          <>
            <Link to="/bookings">My Bookings</Link>
            <Link to="/favorites">Favorites</Link>
            <Link to="/venues/create">List Venue</Link>
            
            <div className="nav-user">
              <Link to="/profile">
                <img src={user.avatar?.url} alt={user.name} />
                <span>{user.name}</span>
              </Link>
            </div>
          </>
        ) : (
          // Not signed in: Show auth links
          <>
            <Link to="/sign-in" className="nav-link-secondary">
              Sign In
            </Link>
            <Link to="/sign-up" className="nav-link-primary">
              Sign Up
            </Link>
          </>
        )}
      </div>
    </nav>
  );
}

export default Navbar;

Handle 404 on Protected Routes

Make 404 page aware of authentication:

src/pages/NotFoundPage.jsx
import { Link } from 'react-router-dom';
import { useAuth } from '@/contexts/AuthContext';

function NotFoundPage() {
  const { user } = useAuth();

  return (
    <div className="not-found-page">
      <div className="not-found-content">
        <h1>404</h1>
        <h2>Page Not Found</h2>
        <p>The page you're looking for doesn't exist.</p>

        <div className="not-found-actions">
          <Link to="/" className="button-primary">
            Go Home
          </Link>

          {user ? (
            <Link to="/profile" className="button-secondary">
              View Profile
            </Link>
          ) : (
            <Link to="/sign-in" className="button-secondary">
              Sign In
            </Link>
          )}
        </div>

        {/* Helpful links */}
        <div className="not-found-links">
          <h3>You might be looking for:</h3>
          <ul>
            <Link to="/venues">Browse Venues</Link>
            {user && <Link to="/bookings">My Bookings</Link>}
            {user && <Link to="/favorites">My Favorites</Link>}
            <Link to="/about">About Us</Link>
            <Link to="/contact">Contact</Link>
          </ul>
        </div>
      </div>
    </div>
  );
}

export default NotFoundPage;

Testing All Routes

Test public routes (signed out):

Visit these URLs while signed out:

  • / → HomePage ✅
  • /about → AboutPage ✅
  • /venues → VenuesPage ✅
  • /venues/123 → VenueDetailPage ✅

All should work without redirecting.

Test protected routes (signed out):

Visit these URLs while signed out:

  • /favorites → Redirects to /sign-in
  • /bookings → Redirects to /sign-in
  • /profile → Redirects to /sign-in
  • /venues/create → Redirects to /sign-in

All should redirect to sign-in page.

Test auth routes (signed in):

Sign in, then visit:

  • /sign-in → Redirects to /
  • /sign-up → Redirects to /

Should redirect away from auth pages.

Test protected routes (signed in):

Visit these URLs while signed in:

  • /favorites → FavoritesPage ✅
  • /bookings → BookingsPage ✅
  • /profile → ProfilePage ✅
  • /venues/create → CreateVenuePage ✅

All should show without redirecting.

Test 404 handling:

Visit invalid URLs:

  • /invalid → NotFoundPage ✅
  • /venues/invalid/nested → NotFoundPage ✅

Should show 404 page with helpful links.

Route Protection Best Practices

What's Next?

In Lesson 15, we'll:

  1. Add redirect logic after sign-in
  2. Handle redirect back to intended page
  3. Improve user experience with smart redirects
  4. Handle edge cases (already signed in, etc.)

✅ Lesson Complete! All routes properly protected!

Key Takeaways

  • Organize routes by access level (public, auth, protected)
  • Wrap protected routes with <Protected> component
  • Use nested routes to avoid repetition
  • Conditional Navbar shows different links based on auth
  • Auth routes redirect if already signed in
  • 404 page adapts to authentication state
  • Route order matters (specific before dynamic)
  • Shared layouts work with protected routes
  • Feature-based organization improves maintainability
  • Test all scenarios (signed in, signed out, invalid routes)