Navigation Patterns — Scroll, Modals and 404s
AdvancedThe details that separate a single-page app from a website that feels right: where the page scrolls to, what the back button does to a modal, and what an unknown URL renders.
Overview
A browser gives you a set of behaviours for free — scrolling to the top on navigation, restoring position on back, a 404 for a URL that does not exist — and a single-page app breaks all of them by default. Nothing here is difficult, but each one is invisible until a user complains that the back button "did nothing" or that a long list dumped them at the bottom of a new page. These are also exactly the details an interviewer probes to find out whether someone has shipped a real application.
Scroll Behaviour
Scroll to top on a new page, restore position on back, and respect anchors.
// Without this, navigating from halfway down a list leaves the new
// page scrolled halfway down.
function ScrollToTop() {
const { pathname, hash } = useLocation()
const navigationType = useNavigationType()
useEffect(() => {
if (navigationType === 'POP') return // back/forward: let the
// browser restore position
if (hash) {
document.querySelector(hash)?.scrollIntoView()
return
}
window.scrollTo(0, 0)
}, [pathname, hash, navigationType])
return null
}
// React Router's <ScrollRestoration /> does this, including saving
// and restoring positions per history entry.
// Focus matters as much as scroll: after navigation, move focus to
// the main heading, or a screen-reader user stays where they were.
useEffect(() => { headingRef.current?.focus() }, [pathname])Modals and the Back Button
Two kinds of overlay, and picking the wrong one is a common complaint.
// Ephemeral overlay -> local state. Back should LEAVE the page.
const [confirmOpen, setConfirmOpen] = useState(false)
// Content the user might share or return to -> a route or a search
// param, so back closes it and the URL is copyable.
const [params, setParams] = useSearchParams()
const previewSlug = params.get('preview')
{previewSlug && (
<Modal onClose={() => setParams(p => { p.delete('preview'); return p })}>
<ProblemPreview slug={previewSlug} />
</Modal>
)}
// The test: if the user pressed back expecting the modal to close,
// which is what phones have taught everyone, then it belongs in
// the URL.
// Route-based modals keep the list rendered underneath while the
// URL points at the item — the pattern behind image lightboxes and
// detail overlays in most modern apps.404s and Redirects
An unknown URL, a moved page, and the server config people forget.
// A catch-all route, always
{ path: '*', element: <NotFound /> }
// Two different 404s, and users need to be able to tell them apart:
// the ROUTE does not exist -> NotFound page
// the route exists, the DATA does -> "This problem was removed",
// with the surrounding nav intact
// Redirect old URLs rather than 404ing them
{ path: 'old-problems', element: <Navigate to="/dsa/problems" replace /> }
// Permanent moves ideally happen at the CDN or server so search
// engines see a real 301.
// The refresh 404 — every SPA hits this once. /problems/two-sum has
// no file on disk, so the server must return index.html for unknown
// paths. Vercel and Netlify do it by default; a bare nginx does not:
// try_files $uri /index.html;
// And set the page title per route, or every page reads the same in
// history, bookmarks and screen-reader announcements.Key Points to Remember
- 1Scroll to top on a push navigation but let the browser restore position on back and forward
- 2Move focus to the main heading after navigation, or screen-reader users are left behind
- 3An overlay users might share or expect back to close belongs in the URL; an ephemeral one belongs in state
- 4Distinguish a missing route from missing data — they need different pages and different words
- 5A client-side route 404s on refresh unless the server returns index.html for unknown paths
Interview Questions
Sign in to ask AriaHow do you handle scroll position when navigating in a single-page app?
When should a modal be part of the URL?
Why does refreshing a client-side route sometimes 404, and how is it fixed?
Ask Aria about Navigation Patterns — Scroll, Modals and 404s
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.