Next generation React routing library with accessible navigation and focus management.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Modern React hooks for accessing routing state and navigation functions in functional components.
Hook to access the current location object containing path, search, hash, and state information.
/**
* Hook to access current location object
* @returns Current location with pathname, search, hash, state, etc.
* @throws Error if used outside LocationProvider
*/
function useLocation(): Location;
interface Location {
pathname: string; // URI encoded/decoded
search: string;
hash: string;
href?: string; // Optional in non-browser environments
origin?: string; // Optional in non-browser environments
protocol?: string; // Optional in non-browser environments
host?: string; // Optional in non-browser environments
hostname?: string; // Optional in non-browser environments
port?: string; // Optional in non-browser environments
state: any;
key: string; // Defaults to "initial"
}Usage Examples:
import React from "react";
import { useLocation } from "@reach/router";
// Basic location access
const LocationDisplay = () => {
const location = useLocation();
return (
<div>
<p>Current path: {location.pathname}</p>
<p>Query string: {location.search}</p>
<p>Hash: {location.hash}</p>
</div>
);
};
// Conditional rendering based on location
const Header = () => {
const location = useLocation();
const isHomePage = location.pathname === "/";
return (
<header className={isHomePage ? "home-header" : "page-header"}>
{isHomePage ? <HeroSection /> : <PageTitle />}
</header>
);
};
// Accessing navigation state
const WelcomeMessage = () => {
const location = useLocation();
const { justLoggedIn } = location.state || {};
return (
<div>
{justLoggedIn && <p>Welcome back! You've successfully logged in.</p>}
</div>
);
};
// URL parameter parsing
const SearchResults = () => {
const location = useLocation();
const queryParams = new URLSearchParams(location.search);
const query = queryParams.get("q");
const page = parseInt(queryParams.get("page") || "1");
return (
<div>
<h1>Search Results for: {query}</h1>
<p>Page {page}</p>
</div>
);
};Hook to access the navigation function for programmatic routing within components.
/**
* Hook to access navigation function
* @returns Navigation function for programmatic routing
* @throws Error if used outside LocationProvider
*/
function useNavigate(): NavigateFunction;
interface NavigateFunction {
(to: string | number, options?: NavigateOptions): Promise<void>;
}
interface NavigateOptions {
state?: any;
replace?: boolean;
}Usage Examples:
import React from "react";
import { useNavigate } from "@reach/router";
// Basic programmatic navigation
const LoginForm = () => {
const navigate = useNavigate();
const handleSubmit = async (formData) => {
const success = await login(formData);
if (success) {
navigate("/dashboard");
}
};
return <form onSubmit={handleSubmit}>{/* form content */}</form>;
};
// Navigation with state
const ProductCreator = () => {
const navigate = useNavigate();
const handleCreate = async (productData) => {
const newProduct = await createProduct(productData);
navigate(`/products/${newProduct.id}`, {
state: { justCreated: true, product: newProduct }
});
};
return <ProductForm onSubmit={handleCreate} />;
};
// History navigation
const NavigationControls = () => {
const navigate = useNavigate();
return (
<div>
<button onClick={() => navigate(-1)}>Back</button>
<button onClick={() => navigate(1)}>Forward</button>
<button onClick={() => navigate("/")}>Home</button>
</div>
);
};
// Conditional navigation with replace
const RedirectHandler = ({ shouldRedirect, redirectTo }) => {
const navigate = useNavigate();
React.useEffect(() => {
if (shouldRedirect) {
navigate(redirectTo, { replace: true });
}
}, [shouldRedirect, redirectTo, navigate]);
return null;
};Hook to access route parameters from the current matched route.
/**
* Hook to access route parameters from current matched route
* Matches parameters against the current base path context
* @returns Object with route parameters or null if no match
* @throws Error if used outside Router
*/
function useParams(): Record<string, string | string[]> | null;Usage Examples:
import React from "react";
import { useParams } from "@reach/router";
// Basic parameter access
const UserProfile = () => {
const params = useParams();
const { userId } = params || {};
if (!userId) {
return <div>No user specified</div>;
}
return <div>User ID: {userId}</div>;
};
// Multiple parameters
const BlogPost = () => {
const params = useParams();
const { category, postId } = params || {};
return (
<article>
<p>Category: {category}</p>
<p>Post ID: {postId}</p>
</article>
);
};
// Parameter validation and effects
const ProductDetails = () => {
const params = useParams();
const { productId } = params || {};
const [product, setProduct] = React.useState(null);
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
if (productId) {
setLoading(true);
fetchProduct(productId)
.then(setProduct)
.finally(() => setLoading(false));
}
}, [productId]);
if (!productId) {
return <div>Product not found</div>;
}
if (loading) {
return <div>Loading...</div>;
}
return <div>Product: {product?.name}</div>;
};
// Using with wildcard parameters
const FileViewer = () => {
const params = useParams();
const filepath = params["*"]; // or params.filepath if named wildcard
return (
<div>
<h1>File Viewer</h1>
<p>File path: {filepath}</p>
</div>
);
};Hook to check if a specific path matches the current location and get match details.
/**
* Hook to check if a path matches current location
* @param path - Path pattern to match against
* @returns Match result with params and uri, or null if no match
* @throws Error if used outside Router or if path is not provided
*/
function useMatch(path: string): MatchResult | null;
interface MatchResult {
uri: string; // Always normalized with leading slash
path: string;
[paramName: string]: string | string[]; // Can contain arrays for splat routes
}Usage Examples:
import React from "react";
import { useMatch } from "@reach/router";
// Basic path matching
const ConditionalComponent = () => {
const match = useMatch("/admin/*");
if (match) {
return <AdminToolbar />;
}
return null;
};
// Match with parameters
const UserContextProvider = ({ children }) => {
const userMatch = useMatch("/users/:userId/*");
const userId = userMatch?.userId;
return (
<UserContext.Provider value={{ userId }}>
{children}
</UserContext.Provider>
);
};
// Multiple matches for complex logic
const PageLayout = ({ children }) => {
const dashboardMatch = useMatch("/dashboard/*");
const adminMatch = useMatch("/admin/*");
const publicMatch = useMatch("/public/*");
let layoutClass = "default-layout";
if (dashboardMatch) layoutClass = "dashboard-layout";
else if (adminMatch) layoutClass = "admin-layout";
else if (publicMatch) layoutClass = "public-layout";
return (
<div className={layoutClass}>
{dashboardMatch && <DashboardSidebar />}
{adminMatch && <AdminHeader />}
<main>{children}</main>
</div>
);
};
// Dynamic styling based on match
const NavigationItem = ({ path, children }) => {
const match = useMatch(path);
return (
<li className={match ? "nav-item active" : "nav-item"}>
{children}
</li>
);
};
// Accessing match details
const BreadcrumbTrail = () => {
const productMatch = useMatch("/products/:productId");
const categoryMatch = useMatch("/categories/:categoryId");
const breadcrumbs = [];
if (categoryMatch) {
breadcrumbs.push({
label: `Category ${categoryMatch.categoryId}`,
path: `/categories/${categoryMatch.categoryId}`
});
}
if (productMatch) {
breadcrumbs.push({
label: `Product ${productMatch.productId}`,
path: `/products/${productMatch.productId}`
});
}
return (
<nav>
{breadcrumbs.map((crumb, index) => (
<span key={index}>
<Link to={crumb.path}>{crumb.label}</Link>
{index < breadcrumbs.length - 1 && " > "}
</span>
))}
</nav>
);
};All hooks must be used within the appropriate context providers.
/**
* Hook usage requirements:
* - useLocation and useNavigate require LocationProvider or Router
* - useParams and useMatch require Router
* - All hooks follow standard React hooks rules
*/Usage Examples:
// Correct usage - hooks inside providers
const App = () => (
<Router>
<UserProfile path="/users/:userId" />
</Router>
);
const UserProfile = () => {
const params = useParams(); // ✓ Inside Router
const location = useLocation(); // ✓ Inside Router (which provides LocationProvider)
const navigate = useNavigate(); // ✓ Inside Router
const match = useMatch("/users/:userId/settings"); // ✓ Inside Router
return <div>User: {params.userId}</div>;
};
// Error handling for missing context
const SafeComponent = () => {
try {
const location = useLocation();
return <div>Path: {location.pathname}</div>;
} catch (error) {
return <div>Component must be used within a Router</div>;
}
};
// Custom hook combining multiple routing hooks
const useRouteInfo = () => {
const location = useLocation();
const params = useParams();
const navigate = useNavigate();
return {
location,
params,
navigate,
isHomePage: location.pathname === "/",
queryParams: new URLSearchParams(location.search)
};
};Install with Tessl CLI
npx tessl i tessl/npm-reach--router