Frontend Routing: Hash, History, and Modern App Routers

Routing is everywhere in frontend, yet many engineers know only one library and one pattern. Senior interviews probe whether you understand the underlying mechanisms (history vs hash), the modern frameworks (Next.js App Router, React Router, TanStack Router), and the architectural tradeoffs.

Hash routing

URL: example.com/#/dashboard

Pros:

  • No server config needed (anything after # is client-side)
  • Works on static hosts
  • Pre-2014 standard

Cons:

  • SEO unfriendly (Google ignored hash for years)
  • URLs look unprofessional
  • No real history API

Modern: avoid except for legacy or very static contexts.

History API routing

URL: example.com/dashboard

The History API (pushState, replaceState, popstate) lets the URL change without a page reload.

history.pushState({ key: 'value' }, '', '/new-path');
window.addEventListener('popstate', handler);

Pros: clean URLs, server can render full HTML for SEO, modern.

Cons: requires server config (all routes serve the SPA shell).

Server config for SPA

For nginx:

location / {
  try_files $uri $uri/ /index.html;
}

For Cloudflare Pages, Netlify, Vercel: configured automatically. For Apache: rewrite rules.

React Router

The dominant React routing library. v7+ unifies the SPA and SSR experience.

Patterns:

  • Declarative <Routes> + <Route>
  • Hooks: useNavigate, useParams, useSearchParams
  • Loaders for data fetching
  • Actions for mutations

Next.js App Router

The 2024+ paradigm:

  • File-system-based routing (app/dashboard/page.tsx)
  • Server Components by default
  • Route handlers (formerly API routes)
  • Server actions for mutations
  • Streaming and Suspense built in

TanStack Router

Type-safe routing for React. Newer; growing in popularity. Strong for apps with complex search-param state.

Search params as state

Increasingly recognized: many app states belong in URL query params:

  • Filters (?status=active)
  • Pagination (?page=2)
  • Selected items (?selectedId=123)

Benefit: shareable, browser-back-friendly, indexable.

Library: nuqs for typed query-param management.

Nested routing

Modern apps have layouts that persist across routes:

  • App shell at /
  • Sidebar at /dashboard/*
  • Specific page at /dashboard/profile

React Router and Next.js both support layout nesting elegantly.

Programmatic navigation

const navigate = useNavigate();
navigate('/dashboard');
navigate(-1); // back
navigate({ pathname: '/list', search: '?q=foo' });

Authentication and protected routes

Common pattern:

function ProtectedRoute({ children }) {
  const { user } = useAuth();
  if (!user) return <Navigate to="/login" />;
  return children;
}

Or via loaders that throw redirect on missing auth.

Scroll restoration

By default, navigation does not preserve scroll. Modern routers offer scroll restoration:

  • Restore scroll on Back navigation
  • Reset to top on forward navigation

React Router has <ScrollRestoration />.

Pre-rendering / static generation

For content sites, pre-render HTML at build time:

  • Next.js generateStaticParams
  • Astro: static-first by default
  • Gatsby: pure static

Common mistakes

  • Hash routing in modern apps (legacy unless required)
  • No server config for SPA (refresh on /dashboard 404s)
  • State in URL params encoded incorrectly
  • Browser back not working as users expect
  • Heavy navigation (full re-render) instead of partial updates

Frequently Asked Questions

React Router or Next.js App Router?

Next.js App Router for full-stack apps with SSR / SSG needs. React Router for SPA-only apps or non-Next contexts. Both ship in production.

What about Remix?

Remix merged with React Router in 2024 — many of its concepts now in React Router 7. Same primitives.

Should the URL be the source of truth for app state?

For shareable / bookmarkable state: yes. For ephemeral UI state (modal open?): no — local state is fine.

Scroll to Top