React Router A11Y
title: "React Router Accessibility — Focus on Navigate" weight: 17 description: "Learn how to manage focus during route transitions in React Router: focus on route change, announce page changes to screen readers, skip links for navigation, and handle focus in nested routes." date: 2026-06-28 lastmod: 2026-06-28 tags: [accessibility, react]
React Router does not automatically manage focus when routes change, requiring a focus management strategy that moves focus to the main content area on navigation and announces the new page to screen readers using live regions.
## What You'll Learn
You will implement focus management on route change, announce page transitions to screen readers, use skip links with React Router, and handle focus in nested route layouts.
## Why It Matters
When a user navigates to a new page in a single-page app, the focus remains on the link they clicked. Screen reader users do not know the page changed. The content may be scrolled off-screen. Focus must be moved intentionally.
## Real-World Use
A user clicks a product link in a SPA. The URL changes and new content renders, but focus stays on the link. The screen reader continues reading the old page. The user scrolls down and finds the new content, but does not know when the navigation completed.
## Router Focus Flow
```mermaid
flowchart TD
A[Link Clicked] --> B[Route Changes]
B --> C[New Content Renders]
C --> D[Focus Main Content]
D --> E[Announce New Page]
E --> F[Continue Browsing]
Implementing Route Focus
Create a component that manages focus on route changes.
import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
function FocusOnNavigate({ children }) {
const location = useLocation();
const mainRef = useRef(null);
useEffect(() => {
if (mainRef.current) {
mainRef.current.focus();
}
// Announce page change to screen readers
const announcer = document.getElementById('route-announcer');
if (announcer) {
announcer.textContent = `Navigated to ${document.title}`;
}
window.scrollTo(0, 0);
}, [location.pathname]);
return (
<main
ref={mainRef}
tabIndex={-1}
id="main-content"
>
{children}
</main>
);
}
// Route announcer component
function RouteAnnouncer() {
return (
<div
id="route-announcer"
aria-live="polite"
aria-atomic="true"
style={{
position: 'absolute',
width: '1px',
height: '1px',
overflow: 'hidden',
clip: 'rect(0, 0, 0, 0)'
}}
/>
);
}
// Skip link for React Router
function SkipLink() {
return (
<a
href="#main-content"
className="skip-link"
onClick={(e) => {
e.preventDefault();
document.getElementById('main-content')?.focus();
}}
>
Skip to main content
</a>
);
}
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #0056b3;
color: white;
padding: 8px 16px;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
// App layout with route accessibility
function App() {
const location = useLocation();
useEffect(() => {
document.title = getPageTitle(location.pathname);
}, [location]);
return (
<>
<RouteAnnouncer />
<SkipLink />
<Header />
<FocusOnNavigate>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</FocusOnNavigate>
</>
);
}
Common Mistakes
- Not managing focus on route changes
- Moving focus to the wrong element (header instead of main content)
- Not announcing page changes to screen readers
- Forgetting scroll position reset on navigation
- Breaking focus when routes share layouts
- Not handling back/forward browser navigation
- Making the main content area focusable without visual indication
Practice and Challenge
Practice 1: Add focus-on-navigate to a React Router app. Practice 2: Implement a route announcer for screen readers. Practice 3: Add a skip link that works with React Router. Practice 4: Test focus behavior with browser forward/back buttons. Practice 5: Handle focus in nested route layouts.
Challenge: Build a React Router app with 5 pages and a focus management system that: moves focus to the main content on every navigation, announces the page title to screen readers, resets scroll position, handles nested routes correctly, and includes a skip link that focuses the main content.
FAQ
Mini Project
Create a React Router accessibility wrapper library with: FocusOnNavigate component for automatic focus management, RouteAnnouncer for screen reader announcements, SkipLink component, and ScrollReset behavior. The library should be a drop-in addition to any React Router app.
What's Next
Dynamic Content and Live Regions covers live regions for dynamic React content.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro