react-router
Use nested routes for shared layouts; Use loaders for data fetching (v6.4+); Implement proper error boundaries
Works with
Agent Skills format with YAML frontmatter. Claude Code reads it as-is.
---
name: "react-router"
description: "Use nested routes for shared layouts; Use loaders for data fetching (v6.4+); Implement proper error boundaries"
license: "MIT"
---
# React Router
> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `react` topic: `router` for comprehensive documentation on React Router v6+ patterns and data loading.
> **Full Reference**: See [advanced.md](advanced.md) for Data Loading (v6.4+), Actions, Code Splitting, Route Configuration, Error Handling, Scroll Restoration, Modal Routes, and TypeScript Integration.
## Basic Setup
```tsx
import { BrowserRouter, Routes, Route } from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/users" element={<Users />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
}
```
---
## Navigation
### Link Component
```tsx
import { Link, NavLink } from 'react-router-dom';
function Navigation() {
return (
<nav>
{/* Basic link */}
<Link to="/">Home</Link>
{/* NavLink with active styling */}
<NavLink
to="/about"
className={({ isActive, isPending }) =>
isActive ? 'active' : isPending ? 'pending' : ''
}
>
About
</NavLink>
{/* With state */}
<Link to="/dashboard" state={{ from: 'home' }}>
Dashboard
</Link>
{/* Replace instead of push */}
<Link to="/login" replace>
Login
</Link>
</nav>
);
}
```
### Programmatic Navigation
```tsx
import { useNavigate, useLocation } from 'react-router-dom';
function LoginButton() {
const navigate = useNavigate();
const location = useLocation();
const handleLogin = async () => {
await login();
// Navigate with state
navigate('/dashboard', {
state: { from: location },
replace: true,
});
};
const handleBack = () => {
navigate(-1); // Go back
};
return (
<div>
<button onClick={handleLogin}>Login</button>
<button onClick={handleBack}>Back</button>
</div>
);
}
```
---
## Route Parameters
### Dynamic Segments
```tsx
<Routes>
<Route path="/users/:userId" element={<UserProfile />} />
<Route path="/posts/:postId/comments/:commentId" element={<Comment />} />
</Routes>
// Component
import { useParams } from 'react-router-dom';
function UserProfile() {
const { userId } = useParams<{ userId: string }>();
return <div>User ID: {userId}</div>;
}
function Comment() {
const { postId, commentId } = useParams();
return (
<div>
Post: {postId}, Comment: {commentId}
</div>
);
}
```
### Optional Segments
```tsx
<Routes>
{/* Optional lang parameter */}
<Route path="/:lang?/products" element={<Products />} />
{/* Catch-all (splat) */}
<Route path="/files/*" element={<FileExplorer />} />
</Routes>
function FileExplorer() {
const { '*': filePath } = useParams();
// /files/documents/report.pdf → filePath = "documents/report.pdf"
return <div>Path: {filePath}</div>;
}
```
---
## Query Parameters
```tsx
import { useSearchParams } from 'react-router-dom';
function ProductList() {
const [searchParams, setSearchParams] = useSearchParams();
const category = searchParams.get('category') || 'all';
const page = parseInt(searchParams.get('page') || '1');
const sort = searchParams.get('sort') || 'name';
const updateFilters = (newCategory: string) => {
setSearchParams({
category: newCategory,
page: '1', // Reset page
sort,
});
};
const nextPage = () => {
setSearchParams(prev => {
prev.set('page', String(page + 1));
return prev;
});
};
return (
<div>
<select
value={category}
onChange={(e) => updateFilters(e.target.value)}
>
<option value="all">All</option>
<option value="electronics">Electronics</option>
</select>
<ProductGrid category={category} page={page} sort={sort} />
<button onClick={nextPage}>Next Page</button>
</div>
);
}
```
---
## Nested Routes
```tsx
// Route configuration
<Routes>
<Route path="/dashboard" element={<DashboardLayout />}>
<Route index element={<DashboardHome />} />
<Route path="analytics" element={<Analytics />} />
<Route path="settings" element={<Settings />} />
<Route path="users">
<Route index element={<UserList />} />
<Route path=":userId" element={<UserDetail />} />
<Route path="new" element={<NewUser />} />
</Route>
</Route>
</Routes>
// Parent layout with Outlet
import { Outlet, Link } from 'react-router-dom';
function DashboardLayout() {
return (
<div className="dashboard">
<nav className="sidebar">
<Link to="/dashboard">Home</Link>
<Link to="/dashboard/analytics">Analytics</Link>
<Link to="/dashboard/settings">Settings</Link>
<Link to="/dashboard/users">Users</Link>
</nav>
<main className="content">
{/* Child routes render here */}
<Outlet />
</main>
</div>
);
}
```
### Relative Links
```tsx
function UserDetail() {
const { userId } = useParams();
return (
<div>
<h1>User {userId}</h1>
{/* Relative to current route */}
<Link to="edit">Edit User</Link> {/* → /dashboard/users/:userId/edit */}
<Link to="../">Back to List</Link> {/* → /dashboard/users */}
<Link to="../../">Dashboard</Link> {/* → /dashboard */}
</div>
);
}
```
---
## Protected Routes
```tsx
import { Navigate, Outlet, useLocation } from 'react-router-dom';
function ProtectedRoute({ children }: { children?: ReactNode }) {
const { user, isLoading } = useAuth();
const location = useLocation();
if (isLoading) {
return <LoadingSpinner />;
}
if (!user) {
// Redirect to login, preserving intended destination
return <Navigate to="/login" state={{ from: location }} replace />;
}
return children ?? <Outlet />;
}
// Usage
<Routes>
<Route path="/login" element={<Login />} />
{/* Protected routes */}
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/profile" element={<Profile />} />
</Route>
</Routes>
// Role-based protection
function AdminRoute({ children }: { children?: ReactNode }) {
const { user } = useAuth();
if (user?.role !== 'admin') {
return <Navigate to="/unauthorized" replace />;
}
return children ?? <Outlet />;
}
<Routes>
<Route element={<ProtectedRoute />}>
<Route path="/dashboard" element={<Dashboard />} />
{/* Admin only */}
<Route element={<AdminRoute />}>
<Route path="/admin" element={<AdminPanel />} />
<Route path="/users/manage" element={<UserManagement />} />
</Route>
</Route>
</Routes>
```
---
## Best Practices
- Use nested routes for shared layouts
- Use loaders for data fetching (v6.4+)
- Implement proper error boundaries
- Use relative links in nested routes
- Lazy load route components
- Preload on user intent (hover)
- Don't fetch data in useEffect when loaders available
- Don't hardcode paths - use relative navigation
- Don't forget to handle loading states
## When NOT to Use This Skill
- **Next.js applications** - Use Next.js App Router or Pages Router instead
- **Server-side routing** - Use framework-specific routing (Express, etc.)
- **Simple conditional rendering** - Use `react` skill for basic show/hide logic
- **Static sites** - Consider if routing library is needed
## Anti-Patterns
| Anti-Pattern | Problem | Solution |
|--------------|---------|----------|
| Fetching in useEffect with loaders available | Waterfall loading, slower UX | Use loader functions |
| Hardcoding absolute paths | Hard to refactor, breaks nested routes | Use relative paths |
| Not handling loading states | Poor UX | Use useNavigation or Suspense |
| Missing error boundaries | App crashes on route errors | Add errorElement to routes |
| Not lazy loading routes | Large initial bundle | Use React.lazy() for routes |
| Using index as route key | Incorrect behavior | Don't use keys for routes |
| Forgetting to handle 404 | Blank page or crash | Add catch-all route with path="*" |
## Quick Troubleshooting
| Issue | Likely Cause | Fix |
|-------|--------------|-----|
| Route not matching | Wrong path syntax | Check path definition, use exact paths |
| Link not working | Wrong to prop | Verify path starts with / or is relative |
| Params undefined | Using wrong hook | Use useParams() for route params |
| Navigation not working | Wrong hook | Use useNavigate() for programmatic navigation |
| Nested route not showing | Missing Outlet | Add <Outlet /> in parent component |
| Loader not called | Using Routes instead of RouterProvider | Use createBrowserRouter with loaders |
| Protected route not working | Wrong redirect logic | Check authentication state and Navigate component |
## Reference Documentation
- [React Router](https://reactrouter.com/)
- [Upgrading to v6](https://reactrouter.com/en/main/upgrading/v5)
- [Advanced Patterns](advanced.md)
- MCP: `mcp__documentation__fetch_docs` → technology: `react`, topic: `router`More Frontend Frameworks skills
frontend-design
anthropics/skills
Guidance for distinctive, intentional visual design when building new UI or reshaping an existing one. Helps with aesthetic direction, typography, and making choices that don't read as templated defaults.
design-taste-frontend
leonxlnx/taste-skill
Anti-slop frontend skill for landing pages, portfolios, and redesigns. The agent reads the brief, infers the right design direction, and ships interfaces that do not look templated. Real design systems when applicable, audit-first on redesigns, strict pre-flight check.
hyperframes-creative
heygen-com/hyperframes
Non-animation creative direction for HyperFrames videos. Use for design spec (frame.md / design.md) handling, palettes, typography, narration, beat planning, audio-reactive visuals, composition patterns, and brand / style decisions. For atomic motion patterns and scene blueprints, use hyperframes-animation.

