6 min read

Dynamic routing and code splitting

Dynamic routing and code splitting

Dynamic routing and code splitting supercharge React apps by handling variable paths efficiently and loading code only when needed, reducing initial bundle sizes for faster performance. In order management systems these techniques shine for managing routes like /orders/:id or /inventory/:category, building on concepts from previous articles.

Why Dynamic Routing Matters

Dynamic routing uses parameters to create flexible paths, enabling single routes to handle multiple similar pages without repetition. For instance, /orders/123 displays details for order 123, while /orders/456 shows order 456—perfect for e-commerce dashboards.

This extends props vs state use cases by passing route params as props to components, like order IDs to fetch data. Unlike static routes covered in React Router v6 nested routes, dynamic ones use :param syntax for variability.​

In order management, imagine viewing shipments: /shipments/:status/active lists active ones, scaling as your app grows.​

Code Splitting Basics

Code splitting divides your app's JavaScript into smaller chunks loaded on demand, preventing large initial downloads. React.lazy() and Suspense make this seamless: const LazyComponent = React.lazy(() => import('./Component'));.

Pairing with dynamic routing, routes load only when navigated to, ideal for feature-heavy apps like inventory systems with heavy charts or forms. This builds on local vs global state, keeping bundles lean even with Redux Toolkit slices.​

Benefits include 50-70% smaller initial loads, improving Lighthouse scores for real-world OMS dashboards.​

Complete Example Setup

Create a full order management app demonstrating these by using React Router v6+, React.lazy for splitting, and dynamic params. Builds on function/class componentsprops/stateuseState pitfallsuseEffectcustom hooksContext, tying into nested routes.

First, install dependencies: npm install react-router-dom.

src/index.js (Minimal Update)

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

src/App.js (Main Entry with Lazy Routes)

```jsximport React, { Suspense } from 'react';
import { BrowserRouter as Router, Routes, Route, Link, Navigate } from 'react-router-dom';
import { OrderContextProvider } from './contexts/OrderContext'; // From previous Context article
import Dashboard from './Dashboard'; // Eager load for home

// Lazy load split chunks - dynamic routes below

const OrdersList = React.lazy(() => import('./orders/OrdersList'));
const OrderDetail = React.lazy(() => import('./orders/OrderDetail'));
const InventoryList = React.lazy(() => import('./inventory/InventoryList'));
const InventoryCategory = React.lazy(() => import('./inventory/InventoryCategory'));

// Loading fallback with spinner
const LoadingSpinner = () => (
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>

Loading order management module...
</div>
);

function App() {
return (
<OrderContextProvider>
<Router>
<div className="App">
<nav style={{ padding: '1rem', background: '#f0f0f0', marginBottom: '1rem' }}>
<Link to="/" style={{ marginRight: '1rem' }}>Dashboard</Link>
<Link to="/orders" style={{ marginRight: '1rem' }}>Orders</Link>
<Link to="/inventory" style={{ marginRight: '1rem' }}>Inventory</Link>
</nav>
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/" element={<Dashboard />} />
<Route path="/orders" element={<OrdersList />} />
<Route path="/orders/:orderId" element={<OrderDetail />} />
<Route path="/orders/:orderId/shipments" element={<OrderDetail />} /> {/* Nested dynamic */}
<Route path="/inventory" element={<InventoryList />} />
<Route path="/inventory/:category" element={<InventoryCategory />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</Suspense>
</div>
</Router>
</OrderContextProvider>
);
}

export default App;

src/Dashboard.js (Eager, Builds on useEffect)

import React, { useEffect, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { useOrderContext } from './contexts/OrderContext';

const Dashboard = () => {
  const { orders, inventorySummary, fetchDashboardData } = useContext(useOrderContext);
  const navigate = useNavigate();

  useEffect(() => {
    fetchDashboardData(); // From previous useEffect article, fetches mock data
  }, [fetchDashboardData]);

  const recentOrder = orders[0];

  return (
    <div>
      <h1>Order Management Dashboard</h1>
      <p>Total Orders: {orders.length} | Inventory Items: {Object.keys(inventorySummary).length}</p>
      {recentOrder && (
        <div>
          <h2>Recent Order #{recentOrder.id}</h2>
          <button onClick={() => navigate(`/orders/${recentOrder.id}`)}>
            View Details (Triggers Dynamic Route + Split)
          </button>
        </div>
      )}
    </div>
  );
};

export default Dashboard;

src/contexts/OrderContext.js (From Context API Article, Enhanced)

import React, { createContext, useContext, useReducer, useEffect } from 'react';

const OrderContext = createContext();

const initialState = {
  orders: [
    { id: 'OMS-123', status: 'pending', items: ['Laptop'], total: 999 },
    { id: 'OMS-456', status: 'shipped', items: ['Phone'], total: 499 },
    { id: 'OMS-789', status: 'delivered', items: ['Tablet'], total: 299 }
  ],
  inventorySummary: { electronics: 50, clothing: 100 },
  loading: false
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'FETCH_DASHBOARD':
      return { ...state, loading: false };
    default:
      return state;
  }
};

export const OrderContextProvider = ({ children }) => {
  const [state, dispatch] = useReducer(reducer, initialState);

  const fetchDashboardData = async () => {
    // Simulate API call for OMS backend
    dispatch({ type: 'FETCH_DASHBOARD' });
  };

  return (
    <OrderContext.Provider value={{ ...state, fetchDashboardData }}>
      {children}
    </OrderContext.Provider>
  );
};

export const useOrderContext = () => useContext(OrderContext);

src/orders/OrdersList.js (Lazy Chunk 1: List with Keys)

import React, { useContext } from 'react';
import { Link } from 'react-router-dom';
import { useOrderContext } from '../contexts/OrderContext';
// Builds on [keys in lists](https://thedevlearnings.com/keys-in-lists-and-why-they-matter-for-dynamic-uis/)

const OrdersList = () => {
  const { orders } = useContext(useOrderContext);

  return (
    <div>
      <h1>All Orders</h1>
      <ul>
        {orders.map((order) => (
          <li key={order.id} style={{ padding: '0.5rem', borderBottom: '1px solid #ccc' }}>
            Order {order.id} - {order.status}
            <Link to={`/orders/${order.id}`} style={{ marginLeft: '1rem' }}>
              View (Lazy Loads Detail)
            </Link>
          </li>
        ))}
      </ul>
    </div>
  );
};

export default OrdersList;

src/orders/OrderDetail.js (Lazy Chunk 2: Dynamic Param + Nested)

import React, { useEffect, useContext, useState } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import { useOrderContext } from '../contexts/OrderContext';
// Integrates [lifting state up](https://thedevlearnings.com/reacts-chain-of-command-parent-child-communication-and-lifting-state-up/), controlled inputs

const OrderDetail = () => {
  const { orderId } = useParams(); // Dynamic param
  const navigate = useNavigate();
  const { orders } = useContext(useOrderContext);
  const [order, setOrder] = useState(null);
  const [notes, setNotes] = useState(''); // Local state for form

  useEffect(() => {
    const foundOrder = orders.find(o => o.id === orderId);
    setOrder(foundOrder || null);
  }, [orderId, orders]);

  if (!order) return <div>Order not found</div>;

  return (
    <div>
      <h1>Order Detail: {order.id}</h1>
      <p>Status: {order.status}</p>
      <p>Items: {order.items.join(', ')}</p>
      <p>Total: ${order.total}</p>

      {/* Controlled input from previous article */}
      <div>
        <label>Update Notes:</label>
        <input
          type="text"
          value={notes}
          onChange={(e) => setNotes(e.target.value)}
          placeholder="OMS notes..."
        />
      </div>

      <nav>
        <Link to={`/orders/${orderId}/shipments`}>View Shipments (Nested Dynamic)</Link>
        <button onClick={() => navigate(-1)}>Back</button>
      </nav>
    </div>
  );
};

export default OrderDetail;

src/inventory/InventoryList.js (Lazy Chunk 3)

import React from 'react';
import { Link } from 'react-router-dom';
// Reuses reusable components from [designing reusable systems](https://thedevlearnings.com/designing-reusable-component-systems-2/)

const categories = ['electronics', 'clothing', 'books'];

const InventoryList = () => (
  <div>
    <h1>Inventory Categories</h1>
    <ul>
      {categories.map((cat) => (
        <li key={cat}>
          <Link to={`/inventory/${cat}`}>Browse {cat.charAt(0).toUpperCase() + cat.slice(1)}</Link>
        </li>
      ))}
    </ul>
  </div>
);

export default InventoryList;

src/inventory/InventoryCategory.js (Lazy Chunk 4: Advanced with Custom Hook)

import React from 'react';
import { useParams } from 'react-router-dom';
import { useFormHandler } from '../hooks/useFormHandler'; // Custom hook from previous

// Mock data
const inventoryData = {
  electronics: [{ id: 1, name: 'Laptop', stock: 10 }, { id: 2, name: 'Phone', stock: 20 }],
  clothing: [{ id: 3, name: 'Shirt', stock: 50 }],
  books: [{ id: 4, name: 'React Guide', stock: 5 }]
};

const InventoryCategory = () => {
  const { category } = useParams();
  const items = inventoryData[category] || [];
  const { formData, handleChange, handleSubmit } = useFormHandler({ reorderThreshold: '' });

  const handleReorder = (e) => {
    e.preventDefault();
    console.log('Reorder below threshold:', formData.reorderThreshold, 'for', category);
  };

  return (
    <div>
      <h1>{category.charAt(0).toUpperCase() + category.slice(1)} Inventory</h1>
      <ul>
        {items.map((item) => (
          <li key={item.id}>
            {item.name} - Stock: {item.stock}
          </li>
        ))}
      </ul>
      <form onSubmit={handleReorder}>
        <label>Reorder if stock &lt;</label>
        <input
          name="reorderThreshold"
          value={formData.reorderThreshold}
          onChange={handleChange}
          type="number"
        />
        <button type="submit">Check OMS Reorder</button>
      </form>
    </div>
  );
};

export default InventoryCategory;

src/hooks/useFormHandler.js (Custom Hook, Builds on Form Handling)

import { useState } from 'react';
// From [building custom hook](https://thedevlearnings.com/building-your-first-custom-hook-form-handling/), [controlled inputs](https://thedevlearnings.com/controlled-vs-uncontrolled-inputs-in-react/)

export const useFormHandler = (initialData) => {
  const [formData, setFormData] = useState(initialData);

  const handleChange = (e) => {
    setFormData({
      ...formData,
      [e.target.name]: e.target.value
    });
  };

  const handleSubmit = (callback) => callback;

  return { formData, handleChange, handleSubmit };
};

Run npm start. Navigate: Dashboard → Orders → /orders/OMS-123 (lazy loads OrderDetail). Check Network tab: separate chunks for orders/, inventory/ load on-demand.​


Performance in Order Management

In OMS-like systems, dynamic routing handles thousands of order IDs without route explosion. Code splitting ensures detail views (with charts, FedEx integrations) don't bloat home bundles.

Measure: Initial bundle ~20KB vs 200KB unsplit. TTI drops 2-3s on slow networks. Combines with server vs client state—fetch order data in loaders post-split.​

Pitfalls: Ensure unique keys in lists; use React.memo next (teased below). For global state, lazy-load Redux slices via code-split reducers.[ link placeholder]

SEO and Guards Tie-In

Dynamic routes work with React Router guards from prior article—protect /orders/:id with auth loaders pre-split.

For SSR/SEO, use generateStaticParams in Next.js, but client-side: meta via React Helmet post-load.

Testing Your Setup

  1. Open DevTools Network: Navigate to /inventory/electronics—see inventory chunk load.
  2. Throttle to Slow 3G: Feel split benefits.
  3. Invalid /orders/999: Handles gracefully.

Next article: [React.memo & useCallback for smoother apps].