npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-router-dataflow

v1.1.2

Published

A tiny, fully type-safe composer for React Router loaders, actions, and route guards

Readme

What is React Router Dataflow?

React Router loaders and actions scale poorly once you start sharing logic. Context becomes implicit, middleware composition is manual, and type safety quickly degrades.

React Router Dataflow fixes this by letting you compose loaders, actions, and route guards as typed pipelines, producing a fully type-safe execution context per route.

import { Loader } from "react-router-dataflow";

const loader = Loader
  .with(parseParams)
  .with(fetchData)
  .build(async (_, { params, data }) => {
    // params and data are guaranteed to be available here
    return data;
  });

// => loader can be used as a standard React Router loader

What problems does it solve?

React Router Dataflow is useful as soon as your routes have different contexts or you start sharing logic between loaders or actions. It makes execution order explicit, context predictable, and removes the need for defensive runtime checks.

Installation

npm install react-router-dataflow
yarn add react-router-dataflow
pnpm add react-router-dataflow

Basic usage

Compose loaders and actions as pipelines with a progressively typed context.

Define reusable middlewares

import { LoaderMiddleware, Loader } from "react-router-dataflow";

// Explicitly typed middleware (optional, but recommended)
const mw: LoaderMiddleware<{ data: string }> = async (args) => {
  /* do stuff with args */
  return { data: "OK" };
};

Loader.with(mw);

Middlewares steps (for loader/action context pipelines) can also be typed action-only (ActionMiddleware) or universal (Middleware).

Declare middlewares inline

import { Loader } from "react-router-dataflow";

Loader
  .with(async (args) => {
    /* do stuff with args */
    return { data: "OK" };
  })
  .with(async (_, { data }) => {
    // data is guaranteed here
    return null; // no context enrichment
  });

Build a loader or an action

import { Loader } from "react-router-dataflow";

// When loader should send data
Loader
  .with(mw)
  .with(async () => ({ additional: 1 }))
  .build(async (_, { data, additional }) => {
    // data and additional are guaranteed here
    return { data, additional };
  });

// When it should not
Loader.with(mw).build();
import { Action } from "react-router-dataflow";

// throws 405 response for any method not handled (ignoring case)
Action.build({
  POST: async (args) => ({ data: "post" }),
  DELETE: async (args) => ({ deleted: true })
});

// Runs middlewares then throws 405 response for any method not handled (ignoring case)
Action
  .with(mw)
  .with(async () => ({ additional: 1 }))
  .build({
    POST: async (_, { data, additional }) => {
      /* data and additional are guaranteed here */
      return { data, additional };
    },
    DELETE: async (_, { data, additional }) => {
      /* data and additional are guaranteed here */
      return { deleted: true };
    }
  });

For more advanced patterns such as context enforcement, parameterized middlewares and middleware factorization, see the advanced middleware documentation.

RouteMiddleware - Guarding routes with React Router

React Router Dataflow includes support for building React Router middlewares with a fully typed internal pipeline, using the same with/build builder API as loaders and actions. RouteMiddleware is a builder that produces a single React Router middleware suitable for guarding routes and short-circuiting navigation.

RouteMiddleware is not related to middlewares types used by Loader and Action builders which are composing functions of loaders and actions.

RouteMiddleware focuses on composing route guards by letting you compose a sequence of typed steps called route requirements. Each requirement runs before the route is entered, can enrich internal context, and can throw a Response to block navigation. Route requirements can be predefined in the same way as loaders and actions middlewares using RouteRequirement type.

RouteMiddleware does not support post-processing or response interception. If you need a pre/post middleware, write a React Router middleware directly and compose it alongside RouteMiddleware.

Terminology

  • Middleware: a loader/action pipeline step
  • RouteMiddleware: a builder that produces a React Router middleware
  • RouteRequirement: a typed guard step executed inside a RouteMiddleware
import { RouteMiddleware } from "react-router-dataflow";
import { requireAuth } from "~/guards/require-auth";

// Ensures user is authenticated and has "admin" role
RouteMiddleware
  .with(requireAuth) // => { user }
  .build(async (_, { user }) => {
    if (user.role !== "admin") {
      throw new Response("Unauthorized", { status: 401 });
    }

    return null;
  });

Integration in React Router

Loaders, actions, and route middlewares created with React Router Dataflow are standard React Router primitives and behave the same way on the client and on the server. They can be used in:

  • framework mode (route files)
  • data routers (createBrowserRouter)
  • declarative routes (<Route loader/>)

Framework mode example

// app/routes/data.$id.tsx

import { Loader, Action } from "react-router-dataflow";
import { requireAuth } from "~/guards/require-auth";
import { dataFromParams } from "~/middlewares/data-from-params";

// Auth is enforced on the route
export const middleware = [
  RouteMiddleware
    .with(requireAuth)
    .build()
];

// => reached only if authenticated, get data from route params
export const loader = Loader
  .with(dataFromParams) // => { data }
  .build(async (_, { data }) => data);

// => reached only if authenticated, only PATCH/patch requests are handled
export const action = Action
  .with(dataFromParams) // => { data }
  .build({
    PATCH: async ({ request }, { data }) => {
      const updatedData = /* data update using request */;
      return updatedData;
    }
  });

const ExamplePage = () => <h1>Example page</h1>;

export default ExamplePage;