Solid Router provides a powerful, type-safe routing solution for SolidJS applications. With features like automatic code splitting, parallel data loading, and seamless integration with Solid's reactivity, it offers everything needed for modern single-page applications.
Installation and Setup
Install Solid Router:
npm install @solidjs/routerBasic Routing Setup
Setting Up Routes
// App.tsx
import { Router } from '@solidjs/router';
import Home from './pages/Home';
import About from './pages/About';
import NotFound from './pages/NotFound';
function App() {
return (
<Router>
<Routes>
<Route path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="*404" component={NotFound} />
</Routes>
</Router>
);
}Route Configuration
import { render } from 'solid-js/web';
import { Router, Routes, Route } from '@solidjs/router';
import Home from './pages/Home';
import Products from './pages/Products';
import ProductDetail from './pages/ProductDetail';
import Login from './pages/Login';
import Dashboard from './pages/Dashboard';
render(() => (
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/login" element={<Login />} />
<Route path="/dashboard" element={<Dashboard />} />
</Routes>
</Router>
), document.getElementById('root'));Navigation
Link Component
Use the Link component for declarative navigation:
import { Link } from '@solidjs/router';
function Navigation() {
return (
<nav>
<Link href="/">Home</Link>
<Link href="/products">Products</Link>
<Link href="/about">About</Link>
</nav>
);
}NavLink for Active States
import { NavLink } from '@solidjs/router';
function Navigation() {
return (
<nav>
<NavLink href="/" activeClass="active">
Home
</NavLink>
<NavLink href="/products" activeClass="active">
Products
</NavLink>
<NavLink href="/about" activeClass="active">
About
</NavLink>
</nav>
);
}Programmatic Navigation
Use useNavigate for programmatic navigation:
import { useNavigate } from '@solidjs/router';
function LoginForm() {
const navigate = useNavigate();
const handleLogin = async (credentials: Credentials) => {
const success = await login(credentials);
if (success) {
navigate('/dashboard');
} else {
navigate('/login', { replace: true });
}
};
return (
<form onSubmit={(e) => {
e.preventDefault();
handleLogin(/* credentials */);
}}>
{/* form fields */}
</form>
);
}Navigation Options
const navigate = useNavigate();
// Navigate with options
navigate('/dashboard', {
replace: true, // Replace current history entry
scroll: true, // Scroll to top
state: { fromLogin: true }, // Pass state
});
// Navigate back/forward
navigate(-1); // Back
navigate(1); // Forward
navigate(-2); // Back two pagesRoute Parameters
Path Parameters
// Route definition
<Route path="/users/:id" component={UserDetail} />
// Component
function UserDetail() {
const params = useParams<{ id: string }>();
return (
<div>
<h1>User ID: {params.id}</h1>
{/* Fetch user data using params.id */}
</div>
);
}Multiple Parameters
// Route: /users/:userId/posts/:postId
function PostDetail() {
const params = useParams();
return (
<div>
<p>User: {params.userId}</p>
<p>Post: {params.postId}</p>
</div>
);
}Query Parameters
import { useSearchParams } from '@solidjs/router';
function ProductList() {
const [searchParams, setSearchParams] = useSearchParams();
const page = () => searchParams.page || '1';
const category = () => searchParams.category || 'all';
const setCategory = (category: string) => {
setSearchParams({ category, page: '1' });
};
const nextPage = () => {
setSearchParams({
category: category(),
page: String(parseInt(page()) + 1),
});
};
return (
<div>
<select onInput={(e) => setCategory(e.currentTarget.value)}>
<option value="all">All Categories</option>
<option value="electronics">Electronics</option>
<option value="clothing">Clothing</option>
</select>
<button onClick={nextPage}>Next Page</button>
<div>
Showing page {page()} of {category()} products
</div>
</div>
);
}Nested Routes
Nested Route Structure
function App() {
return (
<Router>
<Routes>
<Route path="/" component={Layout}>
<Route path="/" element={<Home />} />
<Route path="/products" element={<Products />} />
<Route path="/products/:id" element={<ProductDetail />} />
<Route path="/dashboard" element={<DashboardLayout />}>
<Route path="/dashboard" element={<DashboardHome />} />
<Route path="/dashboard/settings" element={<Settings />} />
<Route path="/dashboard/profile" element={<Profile />} />
</Route>
</Route>
</Routes>
</Router>
);
}
function Layout(props: { children: any }) {
return (
<div>
<Navigation />
<main>{props.children}</main>
<Footer />
</div>
);
}
function DashboardLayout(props: { children: any }) {
return (
<div class="dashboard">
<Sidebar />
<main>{props.children}</main>
</div>
);
}Nested Route Parameters
function App() {
return (
<Router>
<Routes>
<Route path="/users/:userId" component={UserLayout}>
<Route path="/users/:userId" element={<UserOverview />} />
<Route path="/users/:userId/posts" element={<UserPosts />} />
<Route path="/users/:userId/posts/:postId" element={<PostDetail />} />
</Route>
</Routes>
</Router>
);
}
function UserLayout() {
const params = useParams<{ userId: string }>();
return (
<div>
<h1>User: {params.userId}</h1>
<Outlet />
</div>
);
}Data Loading
Route Data Loaders
import { RouteSectionProps, useRouteData } from '@solidjs/router';
// Data loader function
async function loader(props: RouteSectionProps) {
const userId = props.params.userId;
const response = await fetch(`/api/users/${userId}`);
const user = await response.json();
return { user };
}
// Route with loader
<Route
path="/users/:userId"
component={UserDetail}
data={loader}
/>
// Component using loaded data
function UserDetail() {
const data = useRouteData();
return (
<Show when={data()}>
{(data) => (
<div>
<h1>{data().user.name}</h1>
<p>{data().user.email}</p>
</div>
)}
</Show>
);
}Parallel Data Loading
async function loadDashboardData() {
const [user, notifications, stats] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/notifications').then(r => r.json()),
fetch('/api/stats').then(r => r.json()),
]);
return { user, notifications, stats };
}
<Route
path="/dashboard"
component={Dashboard}
data={loadDashboardData}
/>Reactive Data Loading
function ProductList() {
const params = useParams();
const [products] = createResource(() => params.category, async (category) => {
const response = await fetch(`/api/products?category=${category}`);
return response.json();
});
return (
<Show when={products()}>
{(products) => (
<div>
<For each={products()}>
{(product) => (
<div>{product.name}</div>
)}
</For>
</div>
)}
</Show>
);
}Route Guards
Authentication Guard
function ProtectedRoute(props: { component: any }) {
const [isAuthenticated] = useAuthenticated();
const navigate = useNavigate();
createEffect(() => {
if (!isAuthenticated()) {
navigate('/login', { replace: true });
}
});
return <Show when={isAuthenticated()}>
{props.component}
</Show>;
}
// Usage
<Route
path="/dashboard"
element={
<ProtectedRoute component={<Dashboard />} />
}
/>Role-Based Access
function RoleGuard(props: {
roles: string[];
component: any;
}) {
const [user] = useUser();
const navigate = useNavigate();
const hasAccess = createMemo(() => {
return props.roles.some(role => user()?.roles?.includes(role));
});
createEffect(() => {
if (!hasAccess()) {
navigate('/unauthorized', { replace: true });
}
});
return <Show when={hasAccess()}>
{props.component}
</Show>;
}
// Usage
<Route
path="/admin"
element={
<RoleGuard roles={['admin']} component={<AdminPanel />} />
}
/>Redirects
Programmatic Redirect
function OldRoute() {
const navigate = useNavigate();
onMount(() => {
navigate('/new-route', { replace: true });
});
return <p>Redirecting...</p>;
}Conditional Redirect
function LoginPage() {
const [isAuthenticated] = useAuthenticated();
const navigate = useNavigate();
createEffect(() => {
if (isAuthenticated()) {
navigate('/dashboard', { replace: true });
}
});
return <LoginForm />;
}Route Transitions
Transition Effect
import { useTransition } from '@solidjs/router';
function PageTransition() {
const location = useLocation();
const [transitioning] = useTransition();
return (
<div classList={{ transitioning: transitioning() }}>
<Outlet />
</div>
);
}Custom Transition Animation
function AnimatedRoute() {
const [exiting, setExiting] = createSignal(false);
return (
<div
class="page"
classList={{ exiting: exiting() }}
onAnimationEnd={() => setExiting(false)}
>
<Outlet />
</div>
);
}Error Handling
Error Boundary
import { ErrorBoundary } from 'solid-js';
function App() {
return (
<Router>
<ErrorBoundary fallback={(error) => <ErrorPage error={error} />}>
<Routes>
{/* routes */}
</Routes>
</ErrorBoundary>
</Router>
);
}
function ErrorPage(props: { error: Error }) {
return (
<div class="error-page">
<h1>Oops! Something went wrong</h1>
<p>{props.error.message}</p>
<Link href="/">Go Home</Link>
</div>
);
}Route-Level Error Handling
<Route
path="/users/:userId"
element={<UserDetail />}
data={async (props) => {
try {
const user = await fetchUser(props.params.userId);
return { user };
} catch (error) {
throw redirect('/404');
}
}}
/>Best Practices
- 1Use Type Safety: Leverage TypeScript for route params
- 2Optimize Data Loading: Use parallel loading when possible
- 3Implement Guards: Protect routes properly
- 4Handle Errors: Provide error boundaries
- 5Lazy Load Routes: Code split for performance
- 6Use Proper Navigation: Prefer Link over manual navigation
Conclusion
Solid Router provides a comprehensive routing solution for SolidJS applications with features like type-safe navigation, nested routes, data loading, and route guards. By mastering these concepts, you can build complex, navigable applications with confidence.
The combination of SolidJS's reactivity and Solid Router's powerful features makes building single-page applications both efficient and enjoyable. Use these patterns to create scalable, maintainable routing solutions in your SolidJS projects.