Found: a React router built around static route configuration
Extensible route-based routing for React applications
At a glance
- What is it?
- Found is an extensible, route-based router for React that leans on Redux for state and Farce for browser navigation, with static route configs at its core. Strong for nested data fetching and code splitting, heavier than a plain React router and shipping an older stable npm line than its source tree suggests.
- Who is it for?
- Found fits teams that already run Redux, want nested routes with explicit loading states, and are willing to adopt a routing model whose matcher and element resolution can be replaced. It does not fit a team that wants a zero-dependency router, a current stable npm release, or a project without Redux in the stack.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A route tree declared up front instead of at render time
Found is a router for React applications and its central idea is that route configuration is static. The README is explicit about this: Found uses static route configurations, and that choice is what makes efficient code splitting and data fetching with nested routes possible. The configuration describes the shape of the URL space before any component renders, so the router can reason about what a path means, which bundle it needs, and which element should be resolved, rather than discovering that as clicks happen.
That design targets a specific kind of application. A product with genuinely nested screens, where a child route only makes sense inside a parent that has already loaded, is the case the model was built for. Found also says it is designed to be extremely customizable, which is the second half of its identity: this is not a minimal router that you configure and stop touching.
The cost of that ambition is a dependency list with opinions. Found uses Redux for state management and Farce for controlling browser navigation, and it can integrate with an existing store and connected components. Integration, not replacement, is the goal. If your application does not run Redux, adopting Found means adopting Redux or writing an adapter for it.
Pending states for bundles that have not been downloaded
The most distinctive claim in the README concerns loading states. Found offers extensive control over indicating those loading states, even for routes with code bundles that have not yet been downloaded. In practice that means a route can render a pending indicator while its JavaScript is still in flight, not merely while a promise is unresolved. The distinction matters for split-bundle applications, where the gap between navigation and executable code is real and visible.
The repository backs this up with dedicated examples. The examples/ directory contains global-pending, modal-routes, navigation-listener, basic, basic-jsx, redux, universal and universal-redux, each isolating one behaviour of the router rather than showing a toy app. A global pending state and a modal route are both navigation models that many routers force you to rebuild by hand on top of a basic switch statement.
The third-party extension story follows the same shape. Found Relay provides first-class support for a Relay-based data layer, and it works that way because the path matching algorithm and the route element resolution inside Found can be fully replaced. When a loading state must be driven by a GraphQL request, that is a case the extension layer handles without a fork of the router.
Redux holds the state, Farce holds the browser
The runtime picture is a split of responsibilities. Redux, listed as a dependency at version 4.2.0, carries state management, and Farce, listed at version 0.4.5, controls browser navigation. Other dependencies are small and specific: dequal for deep equality, is-promise to detect thenables, tiny-warning for console warnings, tiny-set-immediate for scheduling, and @restart/hooks. Nothing here resembles a full framework on top of a framework; the weight sits in Redux and in the extension mechanism.
The consequence for an adopter is that Found is not a drop-in swap if your navigation state already lives somewhere else. The README's phrasing is that Found can integrate with your existing store and connected components, which means the Redux contract is part of the public surface rather than an implementation detail you can ignore. Reading the redux and universal-redux examples will show you how much wiring the integration actually requires.
Farce is worth separating out, because it is the piece you can adopt independently. It handles browser navigation, so a codebase that only needs history control and none of Found's route matching can use Farce on its own. That is also the cleanest alternative to Found within the same ecosystem, and the difference is that Farce gives you navigation events without a route tree, element resolution or code splitting.
Swapping the matcher is a supported move, not a fork
Most pieces of Found can be fully replaced, and the README names two of them: the path matching algorithm and the route element resolution. Everything else in the router, from the connection to Redux through to the pending indicators, stays in place while those two are replaced.
This is the mechanism behind the extension ecosystem. An extension supplies its own matcher when the default one cannot express the routing rules of an application, and its own element resolution when the default cannot decide which component should render for a matched route. Found Relay is the example given. Nothing in the tree suggests a plugin API with a registration hook, so the README's framing of extensions as a separate project is the accurate picture: you depend on an extension package rather than registering something at runtime.
The trade-off is worth stating plainly. A replaceable core is what makes Found extensible, and it is also what makes it possible for you to build a router that behaves the way you want while quietly keeping a large part of Found's machinery in the path. Extensions built this way inherit Found's release cadence, which matters given the version situation described later.
Working on Found: scripts, examples and a docs site
The repository is a Yarn project with the scripts that matter listed in package.json. Installing dependencies is done with the pinned toolchain, since the tree carries .nvmrc for the Node version and .yarnrc.yml together with a .yarn/ directory for the Yarn release itself. Running the checks and the documentation site are both driven by named scripts:
"scripts": {
"tsc": "tsc",
"build": "./build.sh",
"format": "prettier --write .",
"lint": "eslint src test",
"prepublishOnly": "yarn build",
"publishLocal": "yarn prepublishOnly && yarn file-butler prepare-publish-dir lib && yalc push lib",
"release": "rollout --conventional-commits",
"tdd": "vitest watch",
"test": "yarn lint && yarn testonly",
"testonly": "vitest run --coverage",
"docs": "yarn --cwd www start"
}So the quickest genuine first use is the test suite and the docs site, which run with:
yarn lint && yarn testonly
yarn --cwd www startThe first command lints src and test and then runs the Vitest suite with coverage, and the second starts the www documentation app. If you want to change Found itself rather than consume it, examples/ is the place to look, and vitest.config.ts governs how the tests are wired.
lib, cjs and a wildcard export map for deep imports
The published package is shaped by its files and exports fields. The files array lists lib and cjs, and the entry points split by module system: main points at lib/cjs/index.js, module at lib/lib/index.js, and types at lib/index.d.ts. The exports map is the more interesting part, because it exposes a wildcard subpath alongside the root entry:
"./*": {
"require": {
"types": "./cjs/*.d.ts",
"default": "./cjs/*.js"
},
"import": {
"types": "./lib/*.d.ts",
"default": "./lib/*.js"
}
}That wildcard means consumers can import internal modules by path, and both require and import resolve to their own build with matching type declarations. It is a deliberate openness about internals rather than a minimal public surface, which fits a router whose parts are meant to be swapped.
Building is a two-step process that keeps both formats in sync. The build script runs ./build.sh, prepublishOnly triggers that build, and the local publish path uses yarn file-butler prepare-publish-dir lib followed by yalc push lib, so a colleague can test a change against a real package layout without publishing. The release script uses rollout --conventional-commits, which means commit messages, not version numbers, drive the release.
The stable npm line is far older than the source tree
Here is the fact that should drive an adoption decision. The version in package.json is 2.0.0-rc.7, a release candidate. The most recent listed GitHub release is v0.5.9 dated 2020-08-10, with v0.5.8 and v0.5.7 both from 2020-08-02. The repository is not archived and the last push was on 2026-10-16, so the code continues to move, but the published stable line that most installs would resolve to is the 0.5.x series from six years ago. The 2.0 line is still a candidate rather than a tagged release.
The licence is MIT, so the code can be used, modified and redistributed in a commercial product, with the notice and licence text carried along. That is a statement about the licence terms rather than legal advice.
Practical consequences follow. If you install Found from npm without pinning, you will not get the 2.0 code that the repository has been shaping, and the features described in the README, such as the pending state handling, may not match what you get. There is also no changelog entry to consult, though a CHANGELOG.md file is present in the tree for that purpose. Read the version in the installed package before you write a routing layer on top of it.
When Found is the wrong shape for the job
Found is a poor fit for a small application with a handful of screens and no nested data requirements. The static configuration and the Redux and Farce dependencies cost more than a plain router would, and the extension story only pays off if you actually need to replace the matcher or the element resolution. A single-screen or two-screen React app gains nothing from the pending state machinery.
It is also a poor fit when Redux is off the table. Redux is a hard dependency rather than an optional peer, and the README describes integration with an existing store and connected components as the intended path. Fighting that constraint means maintaining your own adaptation layer around the router, which is exactly the work the extension mechanism is meant to make unnecessary.
For navigation only, Farce is the lighter option and the difference is concrete: Farce gives you browser navigation control, while Found adds a static route tree, element resolution, code splitting and the pending state model on top of it. If a large part of what you need is history and back button behaviour, take Farce. If you need nested routes with data fetching and want the loading behaviour to be first class, Found is the one that has it.
Editorial conclusion
Found fits teams that already run Redux, want nested routes with explicit loading states, and are willing to adopt a routing model whose matcher and element resolution can be replaced. It does not fit a team that wants a zero-dependency router, a current stable npm release, or a project without Redux in the stack. Before adopting, check the version you actually install from npm, because the repository declares 2.0.0-rc.7 while the most recent listed release is v0.5.9 from 2020-08-10, and read the contents of examples/basic to see whether a static route tree matches how your app is organised.
Frequently asked questions
What does Found require for state management and navigation?
Found uses Redux for state management and Farce for controlling browser navigation, and it can integrate with an existing store and connected components. Both appear as dependencies in package.json, with redux at version 4.2.0 and farce at 0.4.5.
How do I run the tests in the Found repository?
The test script runs yarn lint && yarn testonly, where lint covers src and test and testonly runs vitest run --coverage. The tdd script starts Vitest in watch mode for local development.
Which version of Found is current?
The package.json in the repository declares version 2.0.0-rc.7, while the most recent listed GitHub release is v0.5.9 from 2020-08-10. The 2.0 line is still marked as a release candidate.
Does Found handle code splitting and nested data fetching?
The README states that Found uses static route configurations, which enables efficient code splitting and data fetching with nested routes. It also describes extensive control over loading states, including for routes whose code bundles have not yet been downloaded.
Can I change how Found matches paths?
Yes. The README says most pieces of Found, including the path matching algorithm and the route element resolution, can be fully replaced. Found Relay is given as an extension that uses this to support Relay-based data fetching.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/4catalyzer-found)Was this useful?