Routing Architecture

WI-CARPOOL uses React Router DOM v6 for client-side routing with protected routes, role-based access control, and smooth navigation between pages.

🛣️ React Router v6

Modern routing with data loading and error boundaries.

🔒 Protected Routes

Authentication-based route protection and redirects.

👥 Role-Based Access

Different route sets for passengers and drivers.

📱 Dynamic Navigation

Context-aware navigation and breadcrumbs.

Route Configuration

Main App Router

Central router configuration with nested routes and layouts.

App Router Setup

import { createBrowserRouter, RouterProvider } from "react-router-dom"
import { RootLayout } from "@/components/layouts/RootLayout"
import { DashboardLayout } from "@/components/layouts/DashboardLayout"
import { ProtectedRoute } from "@/components/ProtectedRoute"

// Public pages
import { Index } from "@/pages/Index"
import { About } from "@/pages/About"
import { SignIn } from "@/pages/SignIn"
import { SignUp } from "@/pages/SignUp"

// Protected pages
import { PassengerDashboard } from "@/pages/passenger/Dashboard"
import { DriverDashboard } from "@/pages/driver/Dashboard"
import { Profile } from "@/pages/Profile"

const router = createBrowserRouter([
  {
    path: "/",
    element: <RootLayout />,
    children: [
      // Public routes
      {
        index: true,
        element: <Index />,
      },
      {
        path: "about",
        element: <About />,
      },
      {
        path: "signin",
        element: <SignIn />,
      },
      {
        path: "signup",
        element: <SignUp />,
      },
      
      // Protected passenger routes
      {
        path: "passenger",
        element: (
          <ProtectedRoute requiredRole="passenger">
            <DashboardLayout userType="passenger" />
          </ProtectedRoute>
        ),
        children: [
          {
            path: "dashboard",
            element: <PassengerDashboard />,
          },
          {
            path: "find-ride",
            element: <FindRide />,
          },
          // ... more passenger routes
        ],
      },
      
      // Protected driver routes
      {
        path: "driver",
        element: (
          <ProtectedRoute requiredRole="driver">
            <DashboardLayout userType="driver" />
          </ProtectedRoute>
        ),
        children: [
          {
            path: "dashboard",
            element: <DriverDashboard />,
          },
          {
            path: "post-ride",
            element: <PostRide />,
          },
          // ... more driver routes
        ],
      },
      
      // Shared protected routes
      {
        path: "profile",
        element: (
          <ProtectedRoute>
            <Profile />
          </ProtectedRoute>
        ),
      },
    ],
  },
])

function App() {
  return <RouterProvider router={router} />
}

Protected Routes

Route Protection Component

Wrapper component for protecting routes based on authentication and user roles.

ProtectedRoute Implementation

import { Navigate, useLocation } from "react-router-dom"
import { useAuth } from "@/contexts/auth/useAuth"

interface ProtectedRouteProps {
  children: React.ReactNode
  requiredRole?: "passenger" | "driver"
  fallback?: string
}

export function ProtectedRoute({ 
  children, 
  requiredRole,
  fallback = "/signin" 
}: ProtectedRouteProps) {
  const { user, isLoading } = useAuth()
  const location = useLocation()

  // Show loading spinner while checking authentication
  if (isLoading) {
    return (
      <div className="flex items-center justify-center min-h-screen">
        <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
      </div>
    )
  }

  // Redirect to sign in if not authenticated
  if (!user) {
    return (
      <Navigate 
        to={fallback} 
        state={{ from: location }} 
        replace 
      />
    )
  }

  // Check role-based access
  if (requiredRole && user.role !== requiredRole) {
    const redirectPath = user.role === "passenger" 
      ? "/passenger/dashboard" 
      : "/driver/dashboard"
    
    return <Navigate to={redirectPath} replace />
  }

  return <>{children}</>
}

Role-Based Redirection

Automatic redirection based on user roles after authentication.

Role Redirect Hook

import { useEffect } from "react"
import { useNavigate, useLocation } from "react-router-dom"
import { useAuth } from "@/contexts/auth/useAuth"

export function useRoleRedirect() {
  const { user } = useAuth()
  const navigate = useNavigate()
  const location = useLocation()

  useEffect(() => {
    if (user) {
      const from = location.state?.from?.pathname || getDefaultRoute(user.role)
      navigate(from, { replace: true })
    }
  }, [user, navigate, location])
}

function getDefaultRoute(role: string): string {
  switch (role) {
    case "passenger":
      return "/passenger/dashboard"
    case "driver":
      return "/driver/dashboard"
    default:
      return "/"
  }
}

Navigation Components

Navigation Helper Hooks

Custom hooks for common navigation patterns.

Navigation Hooks

import { useNavigate, useLocation } from "react-router-dom"
import { useAuth } from "@/contexts/auth/useAuth"

export function useAppNavigation() {
  const navigate = useNavigate()
  const { user } = useAuth()

  const goToProfile = () => navigate("/profile")
  
  const goToDashboard = () => {
    const dashboardPath = user?.role === "passenger" 
      ? "/passenger/dashboard" 
      : "/driver/dashboard"
    navigate(dashboardPath)
  }

  const goToRideSearch = () => navigate("/search")
  
  const goToRideDetail = (rideId: string) => navigate(`/rides/${rideId}`)
  
  const goBack = () => navigate(-1)

  return {
    goToProfile,
    goToDashboard,
    goToRideSearch,
    goToRideDetail,
    goBack,
  }
}

export function useBreadcrumbs() {
  const location = useLocation()
  
  const getBreadcrumbs = () => {
    const pathSegments = location.pathname.split("/").filter(Boolean)
    const breadcrumbs = []
    
    let path = ""
    for (const segment of pathSegments) {
      path += `/${segment}`
      breadcrumbs.push({
        label: formatSegment(segment),
        path,
        isActive: path === location.pathname
      })
    }
    
    return breadcrumbs
  }

  return { breadcrumbs: getBreadcrumbs() }
}

function formatSegment(segment: string): string {
  return segment
    .split("-")
    .map(word => word.charAt(0).toUpperCase() + word.slice(1))
    .join(" ")
}

Dynamic Navigation Menu

Navigation menu that adapts based on user authentication and role.

Dynamic Navigation Component

import { Link, useLocation } from "react-router-dom"
import { useAuth } from "@/contexts/auth/useAuth"
import { Button } from "@/components/ui/button"

const publicNavItems = [
  { label: "Home", path: "/" },
  { label: "About", path: "/about" },
  { label: "How It Works", path: "/how-it-works" },
]

const passengerNavItems = [
  { label: "Dashboard", path: "/passenger/dashboard" },
  { label: "Find Rides", path: "/passenger/find-ride" },
  { label: "My Trips", path: "/passenger/trips" },
  { label: "Messages", path: "/passenger/messages" },
]

const driverNavItems = [
  { label: "Dashboard", path: "/driver/dashboard" },
  { label: "Post Ride", path: "/driver/post-ride" },
  { label: "My Rides", path: "/driver/rides" },
  { label: "Earnings", path: "/driver/earnings" },
]

export function NavigationMenu() {
  const { user, signOut } = useAuth()
  const location = useLocation()

  const getNavItems = () => {
    if (!user) return publicNavItems
    
    return user.role === "passenger" 
      ? passengerNavItems 
      : driverNavItems
  }

  const navItems = getNavItems()

  return (
    <nav className="flex items-center space-x-4">
      {navItems.map((item) => (
        <Link
          key={item.path}
          to={item.path}
          className={`px-3 py-2 rounded-md text-sm font-medium transition-colors ${
            location.pathname === item.path
              ? "bg-primary text-primary-foreground"
              : "text-gray-600 hover:text-gray-900 hover:bg-gray-100"
          }`}
        >
          {item.label}
        </Link>
      ))}
      
      {user ? (
        <div className="flex items-center space-x-2">
          <Link to="/profile">
            <Button variant="ghost" size="sm">
              Profile
            </Button>
          </Link>
          <Button variant="outline" size="sm" onClick={signOut}>
            Sign Out
          </Button>
        </div>
      ) : (
        <div className="flex items-center space-x-2">
          <Link to="/signin">
            <Button variant="ghost" size="sm">
              Sign In
            </Button>
          </Link>
          <Link to="/signup">
            <Button size="sm">
              Sign Up
            </Button>
          </Link>
        </div>
      )}
    </nav>
  )
}

Route Parameters and Query Strings

Dynamic Route Parameters

Handling dynamic route parameters for resource-specific pages.

Route Parameters Example

import { useParams, useSearchParams } from "react-router-dom"
import { useQuery } from "@tanstack/react-query"
import { rideService } from "@/api/services/rideService"

// Route: /rides/:rideId
function RideDetailPage() {
  const { rideId } = useParams()
  const [searchParams] = useSearchParams()
  
  // Optional query parameters
  const view = searchParams.get("view") || "details"
  const tab = searchParams.get("tab") || "overview"

  const { data: ride, isLoading, error } = useQuery({
    queryKey: ["ride", rideId],
    queryFn: () => rideService.getRideById(rideId!),
    enabled: !!rideId,
  })

  if (isLoading) return <div>Loading ride details...</div>
  if (error) return <div>Error loading ride</div>
  if (!ride) return <div>Ride not found</div>

  return (
    <div>
      <h1>{ride.destination}</h1>
      <RideDetailTabs activeTab={tab} view={view} ride={ride} />
    </div>
  )
}

Search and Filter Parameters

Managing search and filter state through URL parameters.

Search Parameters Hook

import { useSearchParams } from "react-router-dom"
import { useMemo } from "react"

interface SearchFilters {
  from?: string
  to?: string
  date?: string
  passengers?: number
  priceMin?: number
  priceMax?: number
}

export function useSearchFilters() {
  const [searchParams, setSearchParams] = useSearchParams()

  const filters = useMemo((): SearchFilters => ({
    from: searchParams.get("from") || "",
    to: searchParams.get("to") || "",
    date: searchParams.get("date") || "",
    passengers: Number(searchParams.get("passengers")) || 1,
    priceMin: Number(searchParams.get("priceMin")) || undefined,
    priceMax: Number(searchParams.get("priceMax")) || undefined,
  }), [searchParams])

  const updateFilters = (newFilters: Partial<SearchFilters>) => {
    const updatedParams = new URLSearchParams(searchParams)
    
    Object.entries(newFilters).forEach(([key, value]) => {
      if (value === undefined || value === "" || value === 0) {
        updatedParams.delete(key)
      } else {
        updatedParams.set(key, String(value))
      }
    })
    
    setSearchParams(updatedParams)
  }

  const clearFilters = () => {
    setSearchParams({})
  }

  return {
    filters,
    updateFilters,
    clearFilters,
  }
}

Error Handling and 404 Pages

Error Boundary Routes

Handling routing errors and providing fallback UI.

Error Boundary Implementation

import { useRouteError, Link } from "react-router-dom"
import { Button } from "@/components/ui/button"

export function ErrorBoundary() {
  const error = useRouteError()

  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50">
      <div className="max-w-md w-full bg-white shadow-lg rounded-lg p-6 text-center">
        <div className="w-16 h-16 mx-auto mb-4 bg-red-100 rounded-full flex items-center justify-center">
          <svg className="w-8 h-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
            <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z" />
          </svg>
        </div>
        
        <h1 className="text-2xl font-bold text-gray-900 mb-2">
          Something went wrong
        </h1>
        
        <p className="text-gray-600 mb-6">
          {error instanceof Error 
            ? error.message 
            : "An unexpected error occurred"
          }
        </p>
        
        <div className="space-y-3">
          <Button onClick={() => window.location.reload()} className="w-full">
            Try Again
          </Button>
          
          <Link to="/">
            <Button variant="outline" className="w-full">
              Go Home
            </Button>
          </Link>
        </div>
      </div>
    </div>
  )
}

// 404 Not Found page
export function NotFoundPage() {
  return (
    <div className="min-h-screen flex items-center justify-center bg-gray-50">
      <div className="text-center">
        <h1 className="text-6xl font-bold text-gray-900 mb-4">404</h1>
        <p className="text-xl text-gray-600 mb-8">
          The page you're looking for doesn't exist.
        </p>
        <Link to="/">
          <Button>Go Back Home</Button>
        </Link>
      </div>
    </div>
  )
}

Route Animation and Transitions

Page Transitions

Smooth transitions between route changes.

Animated Route Wrapper

import { useLocation } from "react-router-dom"
import { AnimatePresence, motion } from "framer-motion"

const pageVariants = {
  initial: {
    opacity: 0,
    x: -20,
  },
  in: {
    opacity: 1,
    x: 0,
  },
  out: {
    opacity: 0,
    x: 20,
  },
}

const pageTransition = {
  type: "tween",
  ease: "anticipate",
  duration: 0.3,
}

export function AnimatedRoute({ children }: { children: React.ReactNode }) {
  const location = useLocation()

  return (
    <AnimatePresence mode="wait">
      <motion.div
        key={location.pathname}
        initial="initial"
        animate="in"
        exit="out"
        variants={pageVariants}
        transition={pageTransition}
        className="w-full"
      >
        {children}
      </motion.div>
    </AnimatePresence>
  )
}

Best Practices

Route Organization

  • Group related routes under common parent routes
  • Use nested routes for shared layouts
  • Implement lazy loading for route components
  • Follow consistent URL naming conventions

Performance

  • Implement code splitting at the route level
  • Use React.lazy() for dynamic imports
  • Preload critical routes on initial load
  • Minimize re-renders during navigation

User Experience

  • Provide loading states during route transitions
  • Implement proper error boundaries
  • Use meaningful URLs that reflect content
  • Support browser back/forward navigation

Security

  • Validate route parameters on the server
  • Implement proper authorization checks
  • Sanitize URL parameters to prevent XSS
  • Use HTTPS for all authenticated routes