Overview
Villa Buena uses React Router DOM v7.13.1 with the modern createBrowserRouter API for type-safe, data-driven routing. The application implements nested routes with a shared layout component.
Router Configuration
The router is defined in src/app/router.jsx:12-27 using the new router API:
import { createBrowserRouter } from "react-router-dom" ;
import { Home } from "../pages/Home" ;
import { ProductDetail } from "../pages/productDetail/ProductDetail" ;
import { Cart } from "../pages/cart/Cart" ;
import { CheckoutShipping } from "../pages/checkoutShipping/CheckoutShipping" ;
import { CheckoutPayment } from "../pages/checkoutpayment/CheckoutPayment" ;
import { PaymentSuccess } from "../pages/paymentSuccess/PaymentSuccess" ;
import { AccountPage } from "../pages/account/AccountPage" ;
import { OrderHistory } from "../pages/orders/OrderHistory" ;
import { Layout } from "./layout" ;
export const router = createBrowserRouter ([
{
path: "/" ,
element: < Layout /> ,
children: [
{ index: true , element: < Home /> },
{ path: "product/:id" , element: < ProductDetail /> },
{ path: "cart" , element: < Cart /> },
{ path: "checkout/shipping" , element: < CheckoutShipping /> },
{ path: "checkout/payment" , element: < CheckoutPayment /> },
{ path: "payment/success" , element: < PaymentSuccess /> },
{ path: "account" , element: < AccountPage /> },
{ path: "orders" , element: < OrderHistory /> },
],
},
]);
The createBrowserRouter API provides better support for data loading, error boundaries, and optimistic UI updates compared to the legacy BrowserRouter component.
Route Structure
The application follows a hierarchical route structure:
Root Layout Route
The root path / renders the Layout component which provides the shared UI shell (navbar, toast, cart drawer).
Nested Child Routes
All page routes are nested under the layout, automatically inheriting the shared navigation and UI components.
Outlet Rendering
The Layout component uses <Outlet /> to render the matched child route.
Route Definitions
Index Route
{ index : true , element : < Home /> }
The index route renders the home page at the root path /. This is equivalent to defining path: "/" but explicitly marks it as the default child route.
Dynamic Product Route
{ path : "product/:id" , element : < ProductDetail /> }
The :id parameter enables dynamic routing for individual product pages. Access the parameter using the useParams hook:
import { useParams } from 'react-router-dom' ;
function ProductDetail () {
const { id } = useParams ();
// id contains the URL parameter value
}
Checkout Flow Routes
{ path : "checkout/shipping" , element : < CheckoutShipping /> },
{ path : "checkout/payment" , element : < CheckoutPayment /> },
{ path : "payment/success" , element : < PaymentSuccess /> }
These routes implement a multi-step checkout process:
Step 1: Shipping /checkout/shipping - Collect shipping information
Step 2: Payment /checkout/payment - Process payment details
Step 3: Success /payment/success - Confirmation screen
User Account Routes
{ path : "account" , element : < AccountPage /> },
{ path : "orders" , element : < OrderHistory /> }
Protected routes for authenticated user functionality.
Layout Component
The Layout component (src/app/Layout.jsx) provides the shared UI structure:
import { Outlet } from "react-router-dom" ;
import Navbar from "../components/navbar/Navbar" ;
import { useUIStore } from "../store/uiStore" ;
import { useEffect } from "react" ;
import Toast from "../components/toast/Toast" ;
import { CartDrawer } from "../components/cart/CartDrawer" ;
export const Layout = () => {
const darkMode = useUIStore (( state ) => state . darkMode );
useEffect (() => {
document . body . classList . remove ( "light-mode" , "dark-mode" );
document . body . classList . add ( darkMode ? "dark-mode" : "light-mode" );
}, [ darkMode ]);
return (
<>
< Toast />
< Navbar />
< CartDrawer />
< Outlet />
</>
);
};
Layout Responsibilities
Global UI Components
Renders navbar, toast notifications, and cart drawer that persist across route changes.
Dark Mode Management
Applies dark/light mode classes to the document body based on Zustand store state.
Route Outlet
The <Outlet /> component (Layout.jsx:20) renders the matched child route component.
Using a layout route eliminates the need to import and render Navbar/Toast in every page component, following the DRY principle.
Router Provider Integration
The router is integrated at the application root in main.jsx:22:
import { RouterProvider } from "react-router-dom" ;
import { router } from "./app/router" ;
ReactDOM . createRoot ( document . getElementById ( "root" )). render (
< QueryProvider >
< Auth0Provider
domain = "dev-rafaelval.us.auth0.com"
clientId = "ripKJ8Jjq1c3gLEOcusOGUTBTFVGoVdG"
authorizationParams = { { redirect_uri: window . location . origin } }
onRedirectCallback = { onRedirectCallback }
cacheLocation = "localstorage"
>
< RouterProvider router = { router } />
</ Auth0Provider >
</ QueryProvider > ,
);
Navigation Patterns
Programmatic Navigation
Use the useNavigate hook for programmatic route changes:
import { useNavigate } from 'react-router-dom' ;
function CheckoutButton () {
const navigate = useNavigate ();
const handleCheckout = () => {
// Perform validation
navigate ( '/checkout/shipping' );
};
return < button onClick = { handleCheckout } > Proceed to Checkout </ button > ;
}
Declarative Navigation
Use the Link or NavLink component for declarative navigation:
import { Link } from 'react-router-dom' ;
< Link to = "/cart" > View Cart </ Link >
< Link to = { `/product/ ${ productId } ` } > View Product </ Link >
Auth0 Redirect Callback
The application handles Auth0 redirects using a custom callback (main.jsx:9-11):
const onRedirectCallback = ( appState ) => {
router . navigate ( appState ?. returnTo ?? window . location . pathname );
};
This ensures users return to their intended destination after authentication.
The router instance is accessed directly via router.navigate() instead of using the hook, since this callback runs outside the React component tree.
Route Organization Best Practices
Collocate Related Routes
Group related routes (e.g., checkout steps) under a common path prefix for clarity.
Use Nested Routes
Leverage nested routes and layouts to share UI components and reduce code duplication.
Lazy Loading
For larger applications, use React.lazy() to code-split route components: const ProductDetail = lazy (() => import ( '../pages/productDetail/ProductDetail' ));
Error Boundaries
Add error boundaries to route definitions for better error handling: {
path : "/" ,
element : < Layout /> ,
errorElement : < ErrorPage /> ,
children : [ ... ]
}
Routing Flow
URL Parameters
The product detail route demonstrates dynamic parameter usage:
// Route definition
{ path : "product/:id" , element : < ProductDetail /> }
// Component implementation
import { useParams } from 'react-router-dom' ;
import { useProduct } from '../../hooks/useProducts' ;
function ProductDetail () {
const { id } = useParams ();
const { data : product , isLoading } = useProduct ( id );
// Render product details
}
Combine useParams with React Query hooks to automatically fetch data based on URL parameters. React Query will cache and refetch as needed.
Future Enhancements
Potential routing improvements:
Route Guards : Implement authentication guards for protected routes
Data Loaders : Use React Router’s loader function for prefetching data
Actions : Implement form submissions using React Router action functions
Breadcrumbs : Generate automatic breadcrumbs from route hierarchy
Scroll Restoration : Preserve scroll position across navigation