{"id":37227616,"url":"https://github.com/pheleine/react-router-dataflow","last_synced_at":"2026-01-15T03:22:53.726Z","repository":{"id":331677209,"uuid":"1129230567","full_name":"pheleine/react-router-dataflow","owner":"pheleine","description":"A tiny, fully type-safe composer for React Router loaders, actions, and route guards","archived":false,"fork":false,"pushed_at":"2026-01-10T23:33:38.000Z","size":50,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-11T05:29:06.151Z","etag":null,"topics":["action","context","data-router","loader","middleware","middleware-pipeline","react","react-router","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pheleine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-01-06T19:47:55.000Z","updated_at":"2026-01-10T23:23:33.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/pheleine/react-router-dataflow","commit_stats":null,"previous_names":["pheleine/react-router-dataflow"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/pheleine/react-router-dataflow","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pheleine%2Freact-router-dataflow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pheleine%2Freact-router-dataflow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pheleine%2Freact-router-dataflow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pheleine%2Freact-router-dataflow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pheleine","download_url":"https://codeload.github.com/pheleine/react-router-dataflow/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pheleine%2Freact-router-dataflow/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28442269,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-15T00:55:22.719Z","status":"online","status_checked_at":"2026-01-15T02:00:08.019Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["action","context","data-router","loader","middleware","middleware-pipeline","react","react-router","typescript"],"created_at":"2026-01-15T03:22:53.122Z","updated_at":"2026-01-15T03:22:53.719Z","avatar_url":"https://github.com/pheleine.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003eReact Router Dataflow\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003eA tiny, fully type-safe composer for React Router loaders, actions, and route guards.\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/pheleine/react-router-dataflow/actions/workflows/ci.yaml\"\u003e\u003cimg src=\"https://github.com/pheleine/react-router-dataflow/actions/workflows/ci.yaml/badge.svg?branch=master\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/pheleine/react-router-dataflow\"\u003e\u003cimg src=\"https://codecov.io/gh/pheleine/react-router-dataflow/graph/badge.svg?token=6MXCXQ7YJH\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.typescriptlang.org/\"\u003e\u003cimg src=\"https://img.shields.io/badge/TypeScript-Strict-blue\"/\u003e\u003c/a\u003e\n  \u003ca href=\"./LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-MIT-brightgreen.svg\"/\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## What is React Router Dataflow?\n\nReact Router loaders and actions scale poorly once you start sharing logic.\nContext becomes implicit, middleware composition is manual, and type safety quickly degrades.\n\nReact 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.\n\n```typescript\nimport { Loader } from \"react-router-dataflow\";\n\nconst loader = Loader\n  .with(parseParams)\n  .with(fetchData)\n  .build(async (_, { params, data }) =\u003e {\n    // params and data are guaranteed to be available here\n    return data;\n  });\n\n// =\u003e loader can be used as a standard React Router loader\n```\n\n\n## What problems does it solve?\n\nReact 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\nthe need for defensive runtime checks.\n\n## Installation\n\n```sh\nnpm install react-router-dataflow\n```\n```sh\nyarn add react-router-dataflow\n```\n```sh\npnpm add react-router-dataflow\n```\n\n## Basic usage\n\nCompose loaders and actions as pipelines with a progressively typed context.\n\n### Define reusable middlewares\n\n```ts\nimport { LoaderMiddleware, Loader } from \"react-router-dataflow\";\n\n// Explicitly typed middleware (optional, but recommended)\nconst mw: LoaderMiddleware\u003c{ data: string }\u003e = async (args) =\u003e {\n  /* do stuff with args */\n  return { data: \"OK\" };\n};\n\nLoader.with(mw);\n```\n\n\u003e Middlewares steps (for loader/action context pipelines) can also be typed action-only (`ActionMiddleware`) or universal (`Middleware`).\n\n### Declare middlewares inline\n\n```ts\nimport { Loader } from \"react-router-dataflow\";\n\nLoader\n  .with(async (args) =\u003e {\n    /* do stuff with args */\n    return { data: \"OK\" };\n  })\n  .with(async (_, { data }) =\u003e {\n    // data is guaranteed here\n    return null; // no context enrichment\n  });\n```\n\n### Build a loader or an action\n\n```ts\nimport { Loader } from \"react-router-dataflow\";\n\n// When loader should send data\nLoader\n  .with(mw)\n  .with(async () =\u003e ({ additional: 1 }))\n  .build(async (_, { data, additional }) =\u003e {\n    // data and additional are guaranteed here\n    return { data, additional };\n  });\n\n// When it should not\nLoader.with(mw).build();\n```\n\n```ts\nimport { Action } from \"react-router-dataflow\";\n\n// throws 405 response for any method not handled (ignoring case)\nAction.build({\n  POST: async (args) =\u003e ({ data: \"post\" }),\n  DELETE: async (args) =\u003e ({ deleted: true })\n});\n\n// Runs middlewares then throws 405 response for any method not handled (ignoring case)\nAction\n  .with(mw)\n  .with(async () =\u003e ({ additional: 1 }))\n  .build({\n    POST: async (_, { data, additional }) =\u003e {\n      /* data and additional are guaranteed here */\n      return { data, additional };\n    },\n    DELETE: async (_, { data, additional }) =\u003e {\n      /* data and additional are guaranteed here */\n      return { deleted: true };\n    }\n  });\n```\n\n\u003e For more advanced patterns such as context enforcement, parameterized middlewares and middleware factorization, see the [advanced middleware documentation](https://github.com/pheleine/react-router-dataflow/blob/master/docs/advanced_middlewares.md).\n\n## RouteMiddleware - Guarding routes with React Router\n\nReact 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.\n`RouteMiddleware` is a builder that produces a **single React Router middleware** suitable for guarding routes and short-circuiting navigation.\n\n\u003e `RouteMiddleware` is not related to middlewares types used by `Loader` and `Action` builders which are composing functions of loaders and actions.\n\n`RouteMiddleware` focuses on composing route guards by letting you compose a sequence of typed steps called **route requirements**.\nEach requirement runs before the route is entered, can enrich internal context, and can throw a `Response` to block navigation.\nRoute requirements can be predefined in the same way as [loaders and actions middlewares](#define-reusable-middlewares) using `RouteRequirement` type.\n\n\u003e `RouteMiddleware` does not support post-processing or response interception.\n\u003e If you need a pre/post middleware, write a React Router middleware directly and compose it alongside `RouteMiddleware`.\n\n\u003e *Terminology*\n\u003e - **Middleware**: a loader/action pipeline step\n\u003e - **RouteMiddleware**: a builder that produces a React Router middleware\n\u003e - **RouteRequirement**: a typed guard step executed inside a RouteMiddleware\n\n```typescript\nimport { RouteMiddleware } from \"react-router-dataflow\";\nimport { requireAuth } from \"~/guards/require-auth\";\n\n// Ensures user is authenticated and has \"admin\" role\nRouteMiddleware\n  .with(requireAuth) // =\u003e { user }\n  .build(async (_, { user }) =\u003e {\n    if (user.role !== \"admin\") {\n      throw new Response(\"Unauthorized\", { status: 401 });\n    }\n\n    return null;\n  });\n```\n\n## Integration in React Router\n\nLoaders, 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.\nThey can be used in:\n- framework mode (route files)\n- data routers (`createBrowserRouter`)\n- declarative routes (`\u003cRoute loader/\u003e`)\n\n### Framework mode example\n\n```tsx\n// app/routes/data.$id.tsx\n\nimport { Loader, Action } from \"react-router-dataflow\";\nimport { requireAuth } from \"~/guards/require-auth\";\nimport { dataFromParams } from \"~/middlewares/data-from-params\";\n\n// Auth is enforced on the route\nexport const middleware = [\n  RouteMiddleware\n    .with(requireAuth)\n    .build()\n];\n\n// =\u003e reached only if authenticated, get data from route params\nexport const loader = Loader\n  .with(dataFromParams) // =\u003e { data }\n  .build(async (_, { data }) =\u003e data);\n\n// =\u003e reached only if authenticated, only PATCH/patch requests are handled\nexport const action = Action\n  .with(dataFromParams) // =\u003e { data }\n  .build({\n    PATCH: async ({ request }, { data }) =\u003e {\n      const updatedData = /* data update using request */;\n      return updatedData;\n    }\n  });\n\nconst ExamplePage = () =\u003e \u003ch1\u003eExample page\u003c/h1\u003e;\n\nexport default ExamplePage;\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpheleine%2Freact-router-dataflow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpheleine%2Freact-router-dataflow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpheleine%2Freact-router-dataflow/lists"}