README
⚛️ React-Location (Beta)
Enterprise Routing for React
Enjoy this library? Try the entire TanStack! React Query, React Table, React Form, React Charts
Features
- Familiar API inspired by React Router & Next.js
- JSON Search Param API
- Search Param compression
- Full ⌘/Ctrl-click Support
- Transactionally Safe Location Updates
- Relative Routing + Links
Table of Contents
Getting Started
- Install
react-location@next
npm install react-location@next --save
# or
yarn add react-location@next
- Import and use
react-location
import { React } from 'react';
import { ReactLocation, Route, Routes, Link, useParams } from 'react-location';
export function App() {
return (
<ReactLocation>
<nav>
<Link to="/">Home</Link>
<Link to="dashboard">Dashboard</Link>
<Link to="invoices">Invoices</Link>
<Link to="https://github.com/tannerlinsley/react-location">
Github - React Location
</Link>
</nav>
<div>
<Route path="/" element={<div>This is Home</div>} />
<Route path="dashboard" element={<div>This is the Dashboard</div>} />
<Route path="invoices" element={<Invoices />}></Route>
</div>
</ReactLocation>
);
}
function Invoices() {
return (
<Routes>
<Route path="/">
<div>Invoices</div>
<ul>
<li>
<Link to="new">New Invoice</Link>
</li>
<li>
<Link to="1">Invoice 1</Link>
</li>
<li>
<Link to="2">Invoice 2</Link>
</li>
<li>
<Link to="3">Invoice 3</Link>
</li>
</ul>
</Route>
<Route
path="new"
element={
<>
<Link to="..">Back</Link>
<div>This is a new invoice!</div>
</>
}
/>
<Route path=":invoiceID" element={<Invoice />} />
</Routes>
);
}
function Invoice() {
const params = useParams();
const search = useSearch();
const navigate = useNavigate();
const isPreviewOpen = search.previewState?.isOpen;
const togglePreview = () =>
navigate({
search: old => ({
...old,
previewState: {
...(old.previewState ?? {}),
isOpen: !old.previewState.isOpen,
},
}),
});
return (
<div>
<Link to="..">Back</Link>
<div>This is invoice #{params.invoiceID}</div>
<div>
<button onClick={togglePreview}>
{isPreviewOpen ? 'Hide' : 'Show'} Preview
</button>
</div>
{isPreviewOpen ? <div>This is a preview!</div> : null}
</div>
);
}
Documentation
ReactLocation
Required: true
The ReactLocation
component is the root Provider component for react-location
in your app. Render it at least once in the highest sensible location within your application. You can also use this component to preserve multiple location instances in the react tree at the same, which is useful for things like route animations or location mocking.
Prop | Required | Description |
---|---|---|
history | false | The history object to be used internally by react-location |
basepath | false | The basepath prefix for all URLs (not-supported for memory source histories) |
children | true | The children to pass the location context to |
Example: Basic
import { ReactLocation } from 'react-location';
return (
<ReactLocation>
<div>Your Application</div>
</ReactLocation>
);
Example: Memory History
import { createMemoryHistory, ReactLocation } from 'react-location';
const history = createMemoryHistory();
return (
<ReactLocation history={history}>
<div>...</div>
</ReactLocation>
);
Route
The Route
component is used to render content when its path routees the current history's location. It is generally used for routing purposes. It also provides the new relative routeing path to child Route
components, allowing for clean nested route definition.
Prop | Required | Description |
---|---|---|
path | true | The path to route (relative to the nearest parent Route component or root basepath) |
element | The content to be rendered when the path routees the current location |
Example
<Route path="about" element="About Me" />
Routes
The Routes
component is used to selectively render the first child component that is av valid route and/or provide fallbacks. This is useful for:
- Nesting and Relative Routes
- Matching index routes and hard-coded routes before dynamic ones
- Fallback/Wildcard routes
Example - Route Params
render(
<Route path="invoices">
<Routes>
<Route
path="/"
element="This route would route and display at `/invoices/`"
/>
<Route
path="new"
element="This route would route and display at `/invoices/new`"
/>
<Route path=":invoiceID" element={<Invoice />} />
</Routes>
</Route>
);
function Invoice() {
const params = useParams();
return (
<div>
<Link to="..">Back</Link>
<div>This is invoice #{params.invoiceID}</div>
</div>
);
}
Example - Default / Fallback Route
render(
<Routes>
<Route path="/" element="his route would route and display at `/`" />
<Route
path="about"
element="This route would route and display at `/about`"
/>
<Route
path="*"
element="This element would be rendered as the fallback when no matches are found"
/>
</Routes>
);
Example - Default / Fallback Route with redirect
render(
<Routes>
<Route path="/" element="This route would route and display at `/`" />
<Route
path="about"
element="This route would route and display at `/about`"
/>
{/* Redirect all other routes to `/` */}
<Route path="*" element={<Navigate to="/" />} />
</Routes>
);
Link
The Link
component allows you to generate links for internal navigation, capable of updating the:
- Pathname
- Search Parameters
- Location State
- Hash
- Push vs. Replace
The links generated by it are designed to work perfectly with Open in new Tab
+ ctrl + left-click
and Open in new window...
. They are also capable of receiving "active" props (depending on the activeOptions
passed) to decorate the link when the link is currently active relative to the current location.
Prop | Description |
---|---|
...NavigateProps | All properties for the |
activeOptions?: { exact?: boolean, includeHash?: boolean} | Defaults to { exact: false, includeHash: false } |
getActiveProps: () => PropsObject | A function that is passed the Location API and returns additional props for the active state of this link. These props override other props passed to the link (style 's are merged, className 's are concatenated) |
Example: The basics
render(
<div>
<Link to="/home">I will navigate to `/home`</Link>
<Link to="todos">
I will navigate to `./todos`, relative to the current location
</Link>
<Link to="..">I will navigate up one level in the location hierarchy.</Link>
<Link to="." hash="somehash">
I will update the hash to `somehash` at the current location
</Link>
<Link to="/search" search={{ q: 'yes' }}>
I will navigate to `/search?q=yes`
</Link>
<Link
to="."
search={{
someParams: true,
otherParams: 'gogogo',
object: { nested: { list: [1, 2, 3], hello: 'world' } },
}}
>
I will navigate to the current location +
`?someParams=true&otherParams=gogogo&object=~(nested~(list~(~1~2~3)~hello~%27world))`
</Link>
<Link
search={({ removeThis, ...rest }) => ({
...rest,
addThis: 'This is new!',
})}
>
I will add `addThis='This is new!' and also remove the `removeThis` param
to/from the search params on the current page
</Link>
</div>
);
Example: Using getActiveProps
The following link will be green with /about
as the current location.
<Link
to="/about"
getActiveProps={location => ({
style: { color: 'green' },
})}
>
About
</Link>
useLocation
The useLocation
hook returns the current React Location instance from context when used.
Example
import { useLocation } from 'react-location';
export function MyComponent() {
const location = useLocation();
// use location...
}
Navigate
When renderd, the Navigate
component will declaratively and relatively navigate to any route.
Type
type NavigateProps = {
to?: string | null;
pathname?: string | null;
search?: Updater<SearchObj>;
state?: Updater<StateObj>;
hash?: Updater<string>;
replace?: boolean;
};
Example
function App () {
return <Navigate to='./about'>
}
useNavigate
The useNavigate
hook allows you to programmatically navigate your application.
Usage
function MyComponent() {
const navigate = useNavigate();
const onClick = () => {
navigate('./about', { replace: true });
};
return <button onClick={onClick}>About</button>;
}
useMatch
The useMatch
hook allows you to programmatically test a path for a route within the closest relative route. If a path is match, it will return an object of route params detected, even if this is an empty object. If a path doesn't match, it will return false
.
Usage
function App() {
const match = useMatch();
// If the path is '/'
match('/'); // {}
match(':teamId'); // false
// If the path is `/team-1'
match('/'); // {}
match('/', { exact: true }); // false
match(':teamId'); // { teamId: 'team-1 }
return (
<Routes>
<Route path="/" element="Hello!" />
<Route path=":teamId" element="Hello!" />
</Routes>
);
}
SSR
Server-side rendering is easy with react-location. Use createMemoryHistory
and ReactLocation
to mock your app into a specific state for SSR:
import {
createBowserHistory
createMemoryHistory,
ReactLocation,
} from 'react-location';
let history;
if (typeof document !== 'undefined') {
history = createBowserHistory();
} else {
history = createMemoryHistory(['/blog/post/2]);
}
return (
<ReactLocation history={history}>
<div>...</div>
</ReactLocation>
);
Inspiration
All of these libraries offered a lot of guidance and good patterns for writing this library: