Quick start
Found is a router for React applications with a focus on fetching performance and extensibility. Found enables "Render-as-You-Fetch" patterns for efficient code splitting and data fetching in your React applications with or without Suspense. Giving you unmatched control, and your user's the best experiences.
Found is designed to be extremely customizable. Almost all behavior and pieces can be changed or fully replaced. Including the path matching algorithm and route UI construction. This allows extensions such as Found Relay to provide first-class support for different use cases.
Installation and setup
Include the packages in your product via your favorite package manager:
npm i found
# or yarn
yarn add found
Basic usage
Found provides rich client-side route, for your Single Page Application. It allows you to define your web application in terms of the URL just like traditional multi-page browser navigation, while also efficiently updating the your page as the URL changes, only loading and changing the parts of your application that need to. This enables unmatched data fetching efficiency and code code splitting, while providing users with a fast, ultra-responsive browsering experience.
To get started, define the "routes", or urls your application supports and the UI they require.
import { createBrowserRouter, Link } from "found"; const routeConfig = [ { path: "/", Component: () => ( <div> <h1>Hello World</h1> <Link to="/about">About Us</Link> </div> ), }, { path: "/about", Component: () => ( <div> <h1>About</h1> <p>Welcome to found</p> </div> ), }, ]; const BrowserRouter = createBrowserRouter({ routeConfig }); export default function App() { return <BrowserRouter />; }
Routes, can also be specified as JSX if you prefer, using Route
, and makeRouteConfig
:
import { createRoot } from "react-dom/client";
import { createBrowserRouter, Route } from "found";
const BrowserRouter = createBrowserRouter({
routeConfig: makeRouteConfig(
<>
<Route path="/" Component={HomePage} />
<Route path="/about" Component={AboutPage} />
</>
),
});
Nested routes
Best described by found's original inspiration react-router
(who also borrowed the idea from Ember):
Nested Routing is the general idea of coupling segments of the URL to component hierarchy and data.
Nested routing is similar to "layouts" in server-side templating languages, allowings reuse of components, as well as connecting components with their data requirements.
import { createBrowserRouter, makeRouteConfig, Route, Link, } from "found"; function AppPage({ children }) { return ( <div> <nav> <Link to="/dashboard">Dashboard</Link>{" "} <Link to="/about">About</Link> </nav> <main>{children}</main> </div> ); } const Router = createBrowserRouter({ routeConfig: makeRouteConfig( <Route path="/" Component={AppPage}> <Route path="dashboard" Component={() => <h1>Dashboard page</h1>} /> <Route path="about" Component={() => <h1>About page</h1>} /> </Route> ), }); export default function App() { return <Router />; }
Here the route passes children
to AppPage
which will be the resolved nested routes component
for the current location. In otherwords, when on /about
the "About page" header will render and on
/dashboard
"Dashboard page". Because both routes start with /
they are children of AppPage
.
You can use spa-routing to manage your links in an organized and well-typed way
Index routes
A URL will 404 if there is no exact match for it in the route config.
<Route path="/">
<Route path="customers" Component={CustomersPage}>
<Route path=":customerId" Component={CustomerPage} />
</Route>
</Route>
For the above config only /customers/1/
will render, while '/customers'
will 404
. However if want
to render a customers list page, we can add an "index route", which is a route with no path
.
<Route path="/">
<Route path="customers">
<Route Component={CustomersPage} />
<Route path=":customerId" Component={CustomerPage} />
</Route>
</Route>
If you need a parent route to function as a parent while also rendering
as the index route when there is no customer mark the parent route with allowAsIndex
.
<Route path="/">
<Route path="customers" allowAsIndex Component={CustomersPage}>
<Route path=":customerId" Component={CustomerPage} />
</Route>
</Route>
Dynamic route parameters
Routes, can also be parameterized, allowing you to represent state through URLs. Route components
are passed match
as a prop which contains params
(amoung other things). Any component can access
the current route props using useParams
as well.
import { createBrowserRouter, makeRouteConfig, Route, Link, } from "found"; import useParams from "found/useParams"; function CustomerPage({ children, match }) { const { customerId } = match.params; return ( <div> <h1>Customer #{customerId}</h1> <h2>Orders</h2> <nav> <Link to={`/customers/${customerId}/orders/1`}>Order 1</Link>{" "} <Link to={`/customers/${customerId}/orders/2`}>Order 2</Link> </nav> <main>{children}</main> </div> ); } function OrderPage() { const { orderId } = useParams(); return <div>Order #{orderId}</div>; } const Router = createBrowserRouter({ routeConfig: makeRouteConfig( <Route path="customers/:customerId" Component={CustomerPage}> <Route path="orders/:orderId" Component={OrderPage} /> </Route> ), }); export default function App() { return <Router />; }
All params are accessible to every component and routes not just the Route
that
declares it.
Route params are flexible and allow "splats" as well as regular expressions, see path for all the details.
Loading data
Found has out-of-the-box support for efficient data fetching. Add a getData
function to any route and it's return value
will be passed to the Route as a data
prop. see Data fetching for more details about how found
avoids waterfalls and overfetching.
import "./styles.css"; import { createBrowserRouter, HttpError } from "found"; import PostsPage from "./PostsPage"; import PostPage from "./PostPage"; async function fetchFromApi(path: string) { const resp = await fetch(`https://dummyjson.com${path}`); if (!resp.ok) throw new HttpError(404); return resp.json(); } const Router = createBrowserRouter({ routeConfig: [ { path: "/", getData: () => fetchFromApi("/posts"), Component: PostsPage, }, { path: "posts/:postId", getData: async ({ params }) => fetchFromApi(`/posts/${params.postId}/`), Component: PostPage, }, ], }); export default function App() { return <Router />; }
IE 11
found
depends on async iterators, which requires a polyfill of
Symbol.asyncIterator
for IE11. Core-js provides one if needed, import before
importing found.
import "core-js/es/symbol/async-iterator";