Routing
Client-side routing configuration and navigation patterns using React Router
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