React - Routing (Advanced)
Overview
Estimated time: 25–35 minutes
Extend the basic hash router with dynamic parameters, nested routes, and a not-found route.
Try it: Dynamic params and nested routes
View source
// Mini matcher: patterns like /users/:id and /users/:id/settings
function compile(pattern){
  const parts = pattern.split('/').filter(Boolean);
  const keys = [];
  const rx = new RegExp('^/' + parts.map(p => {
    if (p.startsWith(':')) { keys.push(p.slice(1)); return '([^/]+)'; }
    return p.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  }).join('/') + '$');
  return { rx, keys };
}
function matchRoute(path, routes){
  for (const r of routes){
    const { rx, keys } = r._compiled || (r._compiled = compile(r.path));
    const m = path.match(rx);
    if (m){ const params = {}; keys.forEach((k,i)=> params[k]=decodeURIComponent(m[i+1])); return { ...r, params }; }
  }
  return null;
}
function useHash(){
  const [hash, setHash] = React.useState(() => window.location.hash || '#/');
  React.useEffect(() => { const on=()=>setHash(window.location.hash||'#/'); window.addEventListener('hashchange',on); return ()=>window.removeEventListener('hashchange',on); }, []);
  return hash.replace('#','') || '/';
}
function Link({to, children}){ return {children}; }
function UsersLayout({children}){ return (Users
{children}); }
function UsersIndex(){ return Pick a user.
; }
function User({params}){ return User ID: {params.id}
; }
function UserSettings({params}){ return Settings for user {params.id}
; }
function NotFound(){ return Not found.
; }
function App(){
  const path = useHash();
  const routes = [
    { path: '/', element: Home
 },
    { path: '/users', element: Syntax primer
- Compile patterns into regex once and reuse to match path.
- Route entries can either provide elementor anelementFactory(params).
- Nested UI is composed by rendering children inside a layout component.
Vocabulary
- Route param: a dynamic segment like :idcaptured from the URL.
- Nested route: a route rendered within a parent layout.
- 404: a Not Found view when no route matches.
Common pitfalls
- Route ordering matters—put specific routes before general ones.
- Remember to decodeURIComponentparam values.
- Hash routing doesn’t hit the network, but full URL routing would require server support.
Exercises
- Add /users/:id/posts/:postIdand show both params.
- Implement a catch-all route that matches anything under /docs/*.
- Add a breadcrumb that reflects the current nested route.