Top Related Projects
Declarative routing for React
🥢 A minimalist-friendly ~2.1KB routing for React and Preact
🧭 Declarative, asynchronous routing for React.
Manage session history with JavaScript
Quick Overview
Redux-First Router is a routing solution for React applications that seamlessly integrates with Redux. It allows you to manage your routing state within your Redux store, providing a unified approach to state management and navigation in your app.
Pros
- Seamless integration with Redux, allowing for centralized state management including routing
- Supports code splitting and dynamic route loading for improved performance
- Provides a declarative API for defining routes and handling navigation
- Offers powerful features like route matching, redirects, and thunks for complex routing scenarios
Cons
- Steeper learning curve compared to traditional React routing solutions
- Requires a good understanding of Redux and its concepts
- May introduce additional complexity for simpler applications
- Less community support and resources compared to more popular routing libraries
Code Examples
- Defining routes:
const routesMap = {
HOME: '/',
USER: '/user/:id',
ABOUT: '/about',
};
const options = {
initialDispatch: false
};
const enhancer = createEnhancer(routesMap, options);
const store = createStore(rootReducer, compose(enhancer));
- Navigating programmatically:
import { push } from 'redux-first-router';
// In a component or action creator
dispatch(push('/user/123'));
- Handling route changes:
const routeReducer = (state = 'HOME', action = {}) => {
switch (action.type) {
case 'USER':
return 'USER_PAGE';
case 'ABOUT':
return 'ABOUT_PAGE';
default:
return state;
}
};
Getting Started
-
Install the package:
npm install redux-first-router
-
Define your routes and create the store enhancer:
import { createEnhancer } from 'redux-first-router'; const routesMap = { HOME: '/', USER: '/user/:id', ABOUT: '/about', }; const enhancer = createEnhancer(routesMap); const store = createStore(rootReducer, compose(enhancer));
-
Use the
Link
component for navigation:import { Link } from 'redux-first-router-link'; const Navigation = () => ( <nav> <Link to="/">Home</Link> <Link to="/about">About</Link> </nav> );
-
Handle route changes in your reducer:
const routeReducer = (state = 'HOME', action = {}) => { switch (action.type) { case 'USER': return 'USER_PAGE'; case 'ABOUT': return 'ABOUT_PAGE'; default: return state; } };
Competitor Comparisons
Declarative routing for React
Pros of React Router
- More widely adopted and maintained, with a larger community and ecosystem
- Offers a more traditional, declarative routing approach familiar to many React developers
- Provides built-in components for common routing patterns (e.g.,
<Link>
,<NavLink>
)
Cons of React Router
- Can be more verbose, requiring explicit route definitions
- Less integrated with state management solutions like Redux
- May require additional configuration for advanced routing scenarios
Code Comparison
React Router:
import { BrowserRouter, Route, Switch } from 'react-router-dom';
<BrowserRouter>
<Switch>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
</Switch>
</BrowserRouter>
Redux First Router:
import { connectRoutes } from 'redux-first-router';
const routesMap = {
HOME: '/home',
ABOUT: '/about'
};
const { reducer, middleware, enhancer } = connectRoutes(routesMap);
Redux First Router takes a more centralized approach to routing, integrating closely with Redux and allowing for easier management of route-related state. It uses a routes map object to define routes, which can be more concise for complex applications. React Router, on the other hand, offers a more familiar and declarative syntax for defining routes directly in the component tree, which can be easier to understand for developers new to the ecosystem.
Pros of Reach Router
- Simpler API and easier to learn, especially for React beginners
- Built-in accessibility features and focus management
- Lightweight and performant, with a smaller bundle size
Cons of Reach Router
- Less flexibility for complex routing scenarios
- Limited integration with state management libraries like Redux
- Fewer advanced features compared to Redux First Router
Code Comparison
Reach Router:
import { Router, Link } from "@reach/router"
const App = () => (
<Router>
<Home path="/" />
<Dashboard path="dashboard" />
</Router>
)
Redux First Router:
import { connectRoutes } from 'redux-first-router'
import { createStore, applyMiddleware, compose } from 'redux'
const routesMap = { HOME: '/', DASHBOARD: '/dashboard' }
const { reducer, middleware, enhancer } = connectRoutes(routesMap)
const store = createStore(reducer, compose(enhancer, applyMiddleware(middleware)))
Redux First Router offers deeper integration with Redux and more control over routing behavior, while Reach Router provides a simpler, more declarative approach to routing in React applications. The choice between the two depends on the specific needs of your project, such as the complexity of routing requirements and the level of Redux integration desired.
🥢 A minimalist-friendly ~2.1KB routing for React and Preact
Pros of wouter
- Lightweight and minimalistic, with a smaller bundle size
- Simple API that's easy to understand and use
- No external dependencies, making it more flexible and easier to integrate
Cons of wouter
- Less feature-rich compared to redux-first-router
- Lacks built-in support for complex state management
- May require additional libraries for advanced routing scenarios
Code Comparison
wouter:
import { Route, Switch } from "wouter";
<Switch>
<Route path="/users/:id" component={UserProfile} />
<Route path="/about" component={About} />
</Switch>
redux-first-router:
import { connectRoutes } from 'redux-first-router'
const routesMap = {
USER: '/users/:id',
ABOUT: '/about'
}
connectRoutes(routesMap)
wouter offers a more familiar React-like syntax for defining routes, while redux-first-router integrates closely with Redux and uses a different approach to route definition. wouter's simplicity makes it easier to get started with, but redux-first-router provides more powerful features for complex applications, especially those already using Redux for state management.
🧭 Declarative, asynchronous routing for React.
Pros of Navi
- Simpler API and easier to learn, especially for developers new to React routing
- Built-in support for code splitting and asynchronous data loading
- More flexible and adaptable to different project structures
Cons of Navi
- Less integration with Redux ecosystem compared to Redux First Router
- May require additional setup for complex state management scenarios
- Smaller community and fewer third-party extensions
Code Comparison
Navi:
import { createBrowserNavigation } from 'navi';
import { Router, View } from 'react-navi';
const routes = {
'/': () => <HomePage />,
'/about': () => <AboutPage />,
};
const navigation = createBrowserNavigation({ routes });
Redux First Router:
import { connectRoutes } from 'redux-first-router';
const routesMap = {
HOME: '/',
ABOUT: '/about',
};
const { reducer, middleware, enhancer } = connectRoutes(routesMap);
Both libraries offer declarative routing solutions, but Navi's approach is more straightforward and requires less boilerplate. Redux First Router integrates tightly with Redux, making it a better choice for applications heavily reliant on Redux state management. Navi, on the other hand, provides a more flexible and lightweight solution that can easily adapt to various project structures and state management approaches.
Manage session history with JavaScript
Pros of history
- Lightweight and focused solely on managing browser history
- Widely adopted and well-maintained by the React Router team
- Flexible and can be used independently of any specific routing library
Cons of history
- Requires additional setup for integration with Redux
- Less opinionated, which may lead to more boilerplate code
- Doesn't provide built-in support for code splitting or data fetching
Code Comparison
history:
import { createBrowserHistory } from 'history';
const history = createBrowserHistory();
history.push('/home');
history.listen(({ location, action }) => {
// Handle location changes
});
redux-first-router:
import { connectRoutes } from 'redux-first-router';
const routesMap = {
HOME: '/home',
USER: '/user/:id'
};
const { reducer, middleware, enhancer } = connectRoutes(routesMap);
// Use in Redux store configuration
Key Differences
- redux-first-router is specifically designed for Redux integration, while history is more generic
- history provides lower-level control over browser history, while redux-first-router offers a higher-level abstraction for routing in Redux applications
- redux-first-router includes features like automatic code splitting and data fetching, which are not present in history
Use Cases
- Choose history for more control over browser history management or when working with routing libraries other than Redux
- Opt for redux-first-router when building Redux-based applications with complex routing requirements and desire for seamless integration
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual CopilotREADME
Redux-First Router
Think of your app in terms of states, not routes or components. Connect your components and just dispatch Flux Standard Actions!
Motivation
To be able to use Redux as is while keeping the address bar in sync. To define paths as actions, and handle path params and query strings as action payloads.
The address bar and Redux actions should be bi-directionally mapped, including via the browser's back/forward buttons. Dispatch an action and the address bar updates. Change the address, and an action is dispatched.
In addition, here are some obstacles Redux-First Router seeks to avoid:
- Rendering from state that doesn't come from Redux
- Dealing with the added complexity from having state outside of Redux
- Cluttering components with route-related code
- Large API surface areas of frameworks like
react-router
andnext.js
- Routing frameworks getting in the way of optimizing animations (such as when animations coincide with component updates).
- Having to do route changes differently in order to support server-side rendering.
Usage
Install
yarn add redux-first-router
(A minimal <Link>
component exists in the separate package redux-first-router-link
.)
Quickstart
// configureStore.js
import { applyMiddleware, combineReducers, compose, createStore } from 'redux'
import { connectRoutes } from 'redux-first-router'
import page from './pageReducer'
const routesMap = {
HOME: '/',
USER: '/user/:id'
}
export default function configureStore(preloadedState) {
const { reducer, middleware, enhancer } = connectRoutes(routesMap)
const rootReducer = combineReducers({ page, location: reducer })
const middlewares = applyMiddleware(middleware)
const enhancers = compose(enhancer, middlewares)
const store = createStore(rootReducer, preloadedState, enhancers)
return { store }
}
// pageReducer.js
import { NOT_FOUND } from 'redux-first-router'
const components = {
HOME: 'Home',
USER: 'User',
[NOT_FOUND]: 'NotFound'
}
export default (state = 'HOME', action = {}) => components[action.type] || state
// App.js
import React from 'react'
import { connect } from 'react-redux'
// Contains 'Home', 'User' and 'NotFound'
import * as components from './components';
const App = ({ page }) => {
const Component = components[page]
return <Component />
}
const mapStateToProps = ({ page }) => ({ page })
export default connect(mapStateToProps)(App)
// components.js
import React from 'react'
import { connect } from 'react-redux'
const Home = () => <h3>Home</h3>
const User = ({ userId }) => <h3>{`User ${userId}`}</h3>
const mapStateToProps = ({ location }) => ({
userId: location.payload.id
})
const ConnectedUser = connect(mapStateToProps)(User)
const NotFound = () => <h3>404</h3>
export { Home, ConnectedUser as User, NotFound }
Documentation
Basics
Flow Chart
connectRoutes
connectRoutes is the primary "work" you will do to get Redux First
Router going. It's all about creating and maintaining a pairing of
action types and dynamic express style route paths. If you use our <Link />
component and pass an action as its href prop, you can change the URLs you use here any time without having to change your application code.
URL parsing
Besides the simple option of matching a literal path, all matching capabilities of the path-to-regexp package we use are now supported, except unnamed parameters.
Flux Standard Actions
One of the goals of Redux First Router is to NOT alter your actions and be 100% flux standard action-compliant. That allows for automatic support for packages such as redux-actions.
Location Reducer
The location reducer primarily maintains the state of the current pathname and action dispatched (type + payload). That's its core mission.
Link Component
A minimal link component exists in the separate package redux-first-router-link.
Query Strings
Queries can be dispatched by assigning a query object containing key/vals to an action, its payload object or its meta object.
React Native
Redux First Router has been thought up from the ground up with React Native (and server environments) in mind. They both make use of the history package's createMemoryHistory. In coordination, we are able to present you with a first-rate developer experience when it comes to URL-handling on native. We hope you come away feeling: "this is what I've been waiting for."
Advanced
addRoutes
Sometimes you may want to dynamically add routes to routesMap, for example so that you can codesplit routesMap. You can do this using the addRoutes function.
Blocking navigation
Sometimes you may want to block navigation away from the current route, for example to prompt the user to save their changes.
Scroll Restoration
Complete Scroll restoration and hash #links handling is addressed primarily by one of our companion packages: redux-first-router-restore-scroll (we like to save you the bytes sent to clients if you don't need it).
Server Side Rendering
Ok, this is the biggest example here, but given what it does, we think it's extremely concise and sensible.
Client-Only API
The following are features you should avoid unless you have a reason that makes sense to use them. These features revolve around the history package's API. They make the most sense in React Native--for things like back button handling.
Low-level API
Below are some additional methods we export. The target user is package authors. Application developers will rarely need this.
Version 2 Migration Steps
In earlier versions history was a peerDependency, this is no longer the case since version 2 has its own history management tool. This means that the arguments passed to connectRoutes(documentation) need to be changed.
Usage with redux-persist
You might run into a situation where you want to trigger a redirect as soon as possible in case some particular piece of state is or is not set. A possible use case could be persisting checkout state, e.g. checkoutSteps.step1Completed.
Prior Art
These packages attempt in similar ways to reconcile the browser history with redux actions and state.
Recipes
- Dispatching thunks & pathless routes
- SEO-friendly styled links
- Automatically changing page
<title>
- Use Redux Devtools to debug route changes
Help add more recipes for these use cases. PR's welcome!
Topics for things you can do with redux-first-router but need examples written:
- Performing redirects bases on
state
andpayload
. - Use hash-based routes/history (see the migration instructions)
- Restoring scroll position
- Handling optional URL fragments and query strings
- Route change pre- & post-processing
- Code-splitting
- Server-side rendering
- Usage together with
react-universal-component
,babel-plugin-universal-import
,webpack-flush-chunks
.
Where is new feature development occuring?
Feature development efforts are occuring in the Respond framework Rudy repository.
Contributing
We use commitizen, run npm run cm
to make commits. A command-line form will appear, requiring you answer a few questions to automatically produce a nicely formatted commit. Releases, semantic version numbers, tags, changelogs and publishing will automatically be handled based on these commits thanks to [semantic-release](https:/
/github.com/semantic-release/semantic-release).
Community And Related Projects
-
react-universal-component. Made to work perfectly with Redux-First Router.
-
webpack-flush-chunks. The foundation of our
Universal
product line.
Top Related Projects
Declarative routing for React
🥢 A minimalist-friendly ~2.1KB routing for React and Preact
🧭 Declarative, asynchronous routing for React.
Manage session history with JavaScript
Convert designs to code with AI
Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.
Try Visual Copilot