Implementing Accessible Client-Side Routing with Reach Router
Learn how to implement accessible client-side routing using Reach Router to solve focus-loss issues in SPAs and manage dynamic URL parameters.
06 Dec 2025, 17:42 UTC

Solving the Focus-Loss Problem in Single-Page Applications
In standard Single-Page Applications (SPAs), navigating between views often leaves the browser focus on the link that was just clicked. For users relying on screen readers or keyboard navigation, this creates a "dead end" where the focus remains on a disappearing element, forcing the user to tab through the entire page again to find the new content. Reach Router solves this by automatically managing focus during route transitions, ensuring the browser focus shifts to the new page wrapper or a designated heading.
Prerequisites and Versioning
This guide assumes a React project environment. While Reach Router's core logic was merged into React Router v5 and v6, this implementation focuses on the declarative, ranking-based approach introduced by Reach. To use the standalone library, you will need @reach/router installed via npm or yarn.
Implementing the Router Structure
Unlike traditional routers that match routes in the order they are defined, Reach Router uses a ranking system. This means you do not need to worry about placing your "catch-all" or 404 routes at the very bottom of the list; the router calculates the most specific match automatically.
Run the following command in your project terminal to install the dependency:
npm install @reach/router
Create a main application wrapper to handle the routing logic. Use the Router component to wrap your page components, where each component's path prop defines the URL segment it handles.
import { Router, Link } from "@reach/router";
const Home = () => <div>Home Page Content</div>;
const UserProfile = ({ name }) => <div>Profile for {name}</div>;
const NotFound = () => <div>404 - Page Not Found</div>;
const App = () => (
<div className="app-container">
Home
Jane's Profile
{/* The Router handles focus management automatically on transition */}
</div>
);
Handling Dynamic Parameters
Reach Router passes URL parameters directly as props to the component. In the example above, the :name segment in the path /user/:name is extracted and passed to the UserProfile component as a prop named name. This eliminates the need for a separate hook or higher-order component to access route data.
Verification and Testing
To ensure the routing is functioning correctly and maintaining accessibility standards, perform these three checks:
- URL Synchronization: Click a
Linkcomponent. The browser address bar should update immediately without the browser tab showing a loading spinner (indicating no full page refresh). - Focus Shift: Open your browser's developer tools and monitor the focused element. Upon navigation, the focus should move from the clicked link to the new page content or the page wrapper.
- Default Routing: Manually enter a non-existent URL (e.g.,
/random-page). TheNotFoundcomponent (marked with thedefaultprop) should render.
Limitations and Integration Risks
Reach Router is effectively deprecated as a standalone package, as its features were integrated into React Router v6. If you are starting a new enterprise project, it is recommended to use React Router v6+, which incorporates these accessibility patterns. Avoid mixing Reach Router components with legacy React Router v4 patterns, as this can cause conflicting history listeners and erratic navigation behavior.
Rollback Procedure
If the routing implementation causes application crashes or conflicts with existing state management, remove the package and revert to standard React state-based conditional rendering:
- Run
npm uninstall @reach/router. - Remove the
RouterandLinkcomponents from your JSX. - Replace
Linktags with standard<a>tags or a custom state-switching function.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.