The JavaScript ecosystem is always in a constant state of flux. Despite the changes, the JavaScript community strives to resolve all issues as they crop up. If you have worked with React before, you will notice that the go-to routing solution is the React Router library.
It’s often wrongly portrayed as the official routing solution from Facebook. In reality, though, most of the projects at Facebook do not even utilize it.
The majority of the web app projects and use cases can do pretty well with a small and straightforward routing library.
One such routing solution is Wouter. In this guide, you will get an overview of Wouter and see how it can be implemented in place of React Router.
How does client side routing work?
Client side routing (CSR) is similar to server side routing (SSR), except CSR is handled by the browser. In a typical web app, some several pages or routes map into different URLs. Each page has some logic and a template that is then rendered once the route is called.
CSR is handled entirely by the browser’s JavaScript engine. Client side frameworks prefetch and load the JavaScript bundle. This enables the app to switch between pages stored in the bundle without losing the state or app data.
Frontend routing or CSR works by matching the URL with the login from the JavaScript file and then fetching the necessary data from the server using an API.
To learn more about client side routing, you can refer to this guide where CSR is explained in more detail.
Wouter features
Below you will find all the features or advantages that Wouter has to offer over React Router.
- It has zero dependencies — only 1308 bytes gzipped compared to React Router’s 17KB.
- Along with React, Wouter also supports Preact, which is a tiny alternative to React with the same API.
- The
<Router />
component is entirely optional in Wouter. - Wouter is a Hook-based API, so you have more control over routing.
Getting started with Wouter
Take a look at this basic code for setting up routing using Wouter. All you need to do is import the Link
and Route
components from wouter
, and you are good to go. This setup is very minimal compared to all the boilerplate required by react-router
.
import { Link, Route } from "wouter"; const App = () => ( <div> <Link href="/users/John"> <a className="link">My Account</a> </Link> <Route path="/inbox" component={InboxPage} /> <Route path="/settings">Settings</Route> <Route path="/users/:name"> {(params) => <div>Hello, {params.name}!</div>} </Route> </div> ); export default App;
Wouter components
<Route />
The <Route />
component represents a part of the app that is rendered conditionally based on a specific pattern. The pattern is a string that can contain special characters to match dynamic segments.
Wouter provides different ways to declare a route.
import { Route } from "wouter"; // basic usage <Route path="/home"><Home /></Route> // render-prop style usage <Route path="/profile/:id"> {params => <ProfilePage id={params.id} />} </Route> // prop based usage <Route path="/orders/:status" component={Orders} />
<Link />
The <Link />
component renders an anchor <a />
element that on click navigates to the route specified in the href
prop. It can also be customized by providing a custom component as its children.
import { Link } from "wouter"; <Link href="/users/all" className="active"> List Users </Link>; <Link href="/users/all"> <CustomLink> List Users </CustomLink> </Link>;
<Switch />
There may be cases where you would want exclusive routing. That is, only one route needs to be rendered on the page at all times, even if the routes have a pattern that overlaps with each other.
You can achieve exclusive routing using the <Switch />
component. It only renders the first matching route.
import { Route, Switch } from "wouter"; <Switch> <Route path="/users/all" component={AllUsers} /> <Route path="/users/:userId" component={User} /> </Switch>;
Redirect
The <Redirect />
component is used to redirect the user to another route provided in the to
prop. It uses the useLocation
Hook internally to trigger the navigation inside of the useEffect
callback block.
import { Redirect } from "wouter"; const User = ({ userId }) => { if (!userId) return <Redirect to="/login" />; return <div> Current User is {userId} </div>; };
To programmatically navigate the user, you can directly access the setLocation
method using the useLocation
Hook.
import { useLocation } from "wouter"; const App = () => { const [location, setLocation] = useLocation(); const navigate = () => { setLocation("/login"); }; return ( <div> <button onClick={() => navigate()}> Logout </button> </div> ); };
How to make the current link active
Determining if a link is currently active or not, for example, in the navigation menu, is not supported by Wouter out-of-the-box. However, this functionality can be easily implemented by creating a wrapper component and detecting if the current path is active using the useRoute
Hook.
More great articles from LogRocket:
- Don't miss a moment with The Replay, a curated newsletter from LogRocket
- Learn how LogRocket's Galileo cuts through the noise to proactively resolve issues in your app
- Use React's useEffect to optimize your application's performance
- Switch between multiple versions of Node
- Discover how to animate your React app with AnimXYZ
- Explore Tauri, a new framework for building binaries
- Advisory boards aren’t just for executives. Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
import { useRoute, Link } from "wouter"; const CustomLink = (props) => { const [isActive] = useRoute(props.href); return ( <Link {...props}> <a className={isActive ? "active" : ""}>{props.children}</a> </Link> ); };
Using Wouter for server side rendering (SSR)
To use Wouter in a server side context, you will need to instruct the router that the current location needs to come from the HTTP request rather than the browser history.
You can use staticLocationHook
instead of the default useLocation
Hook to tell the router to handle incoming HTTP requests.
import { renderToString } from "react-dom/server"; import { Router } from "wouter"; import staticLocationHook from "wouter/static-location"; import App from "./App"; const handleRequest = (req, res) => { const prerendered = renderToString( <Router hook={staticLocationHook(req.path)}> <App /> </Router> ); // return with prerendered html };
If you need to detect redirects, you can also provide the record option to the staticLocationHook
. The location.history
property is an array matching the user’s history that would be captured after loading the page.
import { renderToString } from "react-dom/server"; import { Router } from "wouter"; import staticLocationHook from "wouter/static-location"; import App from "./App"; const handleRequest = (req, res) => { const location = staticLocationHook(req.path, { record: true }); const prerendered = renderToString( <Router hook={location}> <App /> </Router> ); const finalPage = location.history.slice(-1)[0]; if (finalPage !== req.path) { // perform redirect here } };
Minimize the bundle size further
If you are a bundle size minimalist and want to keep your app’s bundle size as small as possible, you can have all the routes handled using the useLocation
Hook.
The useLocation
Hook is a low-level navigation API that powers the core Wouter components and routing. It is essentially a wrapper around the native browser location
object.
import { useLocation } from "wouter"; const CurrentLocation = () => { const [location, setLocation] = useLocation(); return ( <div> {`The active page is: ${location}`} <button onClick={() => setLocation("/users")}> Click to update the route </buttom> </div> ); };
The useLocation
Hook can also be accessed from a separate file, which will decrease the bundle size further down if you are only going to use the useLocation
Hook and match the routes manually.
import useLocation from "wouter/use-location"; const UsersRoute = () => { const [location] = useLocation(); if (location !== "/users") return null; // render the route };
Obsolete platform support
The Wouter library uses the latest EcmaScript (ES) 6 features like destructuring assignment and const/let variable declarations, and does not come with ES5 transpiled source code. If you want to support legacy browsers like Internet Explorer 11 and below, make sure you run Babel over the node_modules
folder.
Pros and cons compared to React Router
A few of the Pros and Cons of Wouter compared to the React Router library are listed below.
Pros:
- Wouter is tiny and has no dependencies on other libraries
- Wouter is excellent for side projects, hackathon projects, and small apps with around ten routes
- Wouter can be a good inspiration for developers who want to learn more about React Hooks by reading source code
Cons:
- Wouter only implements a limited set of functionalities (redirects, switches, support for hash-based history, extended pattern syntax (like
/app/:foo(\d+)
) are not yet developed) - Not a good option for complex and enterprise-level apps
- The community behind Wouter is much smaller compared to React Router
Conclusion
Wouter is undoubtedly not a replacement for React Router, and nor it is recommended switching over to Wouter in your project. Migration from React Router to Wouter would not work in most use cases because Wouter only mimics and doesn’t fully implement the features of the React Router API.
But the main idea behind Wouter is that most developers tend to choose React Router and then use only 30% of functionality or features. React Router can be overkill for a basic side project where only simple routing is required.
Wouter can also be used as a reference to how React Hooks can be used to create excellent libraries that can be open-sourced.
So that’s it for this guide. Until next time, keep exploring more routing solutions and libraries.
References
Get setup with LogRocket's modern React error tracking in minutes:
- Visit https://logrocket.com/signup/ to get an app ID.
- Install LogRocket via NPM or script tag.
LogRocket.init()
must be called client-side, not server-side. - (Optional) Install plugins for deeper integrations with your stack:
- Redux middleware
- ngrx middleware
- Vuex plugin
$ npm i --save logrocket
// Code:
import LogRocket from 'logrocket';
LogRocket.init('app/id');
Add to your HTML:
<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Well this will be a confusing package for me.
Great article! But nothing in it supports the conclusion that:
“Wouter is excellent for […] small apps with around ten routes” / “Not a good option for complex and enterprise-level apps”