{"id":23923593,"url":"https://github.com/yusukebe/hono-react-router-adapter","last_synced_at":"2025-05-15T13:02:55.608Z","repository":{"id":255828224,"uuid":"853685084","full_name":"yusukebe/hono-react-router-adapter","owner":"yusukebe","description":"Hono \u003c-\u003e React Router Adapter","archived":false,"fork":false,"pushed_at":"2025-03-25T23:33:49.000Z","size":480,"stargazers_count":255,"open_issues_count":4,"forks_count":15,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-07T23:01:46.669Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/hono-react-router-adapter","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yusukebe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2024-09-07T08:35:52.000Z","updated_at":"2025-04-06T08:11:38.000Z","dependencies_parsed_at":"2024-09-07T10:44:27.274Z","dependency_job_id":"5c58c65d-c5b6-40eb-8396-4a3b43fb4069","html_url":"https://github.com/yusukebe/hono-react-router-adapter","commit_stats":{"total_commits":56,"total_committers":4,"mean_commits":14.0,"dds":0.0892857142857143,"last_synced_commit":"cddd29ebd8bea5c3cc50e149a8ed9bc578b8d93a"},"previous_names":["yusukebe/hono-remix-adapter","yusukebe/hono-react-router-adapter"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fhono-react-router-adapter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fhono-react-router-adapter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fhono-react-router-adapter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yusukebe%2Fhono-react-router-adapter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yusukebe","download_url":"https://codeload.github.com/yusukebe/hono-react-router-adapter/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249003941,"owners_count":21196794,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2025-01-05T18:05:04.882Z","updated_at":"2025-04-15T03:46:57.174Z","avatar_url":"https://github.com/yusukebe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# hono-react-router-adapter\n\n`hono-react-router-adapter` is a set of tools for adapting between Hono and React Router. It is composed of a Vite plugin and handlers that enable it to support platforms like Cloudflare Workers and Node.js. You just create Hono app, and it will be applied to your React Router app.\n\n```ts\n// server/index.ts\nimport { Hono } from 'hono'\n\nconst app = new Hono()\n\napp.use(async (c, next) =\u003e {\n  await next()\n  c.header('X-Powered-By', 'React Router and Hono')\n})\n\napp.get('/api', (c) =\u003e {\n  return c.json({\n    message: 'Hello',\n  })\n})\n\nexport default app\n```\n\nThis means you can create API routes with Hono's syntax and use a lot of Hono's built-in middleware and third-party middleware.\n\n\u003e [!WARNING]\n\u003e\n\u003e `hono-react-router-adapter` is currently unstable. The API may be changed without announcement in the future.\n\n## Install\n\n```bash\nnpm i hono-react-router-adapter hono\n```\n\n## How to use\n\nEdit your `vite.config.ts`:\n\n```ts\n// vite.config.ts\nimport serverAdapter from 'hono-react-router-adapter/vite'\n\nexport default defineConfig({\n  plugins: [\n    // ...\n    reactRouter(),\n    serverAdapter({\n      entry: 'server/index.ts',\n    }),\n  ],\n})\n```\n\nWrite your Hono app:\n\n```ts\n// server/index.ts\nimport { Hono } from 'hono'\n\nconst app = new Hono()\n\n//...\n\nexport default app\n```\n\n## Cloudflare Workers\n\nTo support Cloudflare Workers and Cloudflare Pages, add the adapter in `@hono/vite-dev-server` for development.\n\n```ts\n// vite.config.ts\nimport adapter from '@hono/vite-dev-server/cloudflare'\nimport serverAdapter from 'hono-react-router-adapter/vite'\n\nexport default defineConfig({\n  plugins: [\n    // ...\n    reactRouter(),\n    serverAdapter({\n      adapter, // Add Cloudflare adapter\n      entry: 'server/index.ts',\n    }),\n  ],\n})\n```\n\nTo deploy your app to Cloudflare Workers, you can write the following handler on `worker.ts`:\n\n```ts\n// worker.ts\nimport handle from 'hono-react-router-adapter/cloudflare-workers'\nimport * as build from './build/server'\nimport server from './server'\n\nexport default handle(build, server)\n```\n\nSpecify `worker.ts` in your `wrangler.toml`:\n\n```toml\nname = \"example-cloudflare-workers\"\ncompatibility_date = \"2024-11-06\"\nmain = \"./worker.ts\"\nassets = { directory = \"./build/client\" }\n```\n\n## Cloudflare Pages\n\nTo deploy your app to Cloudflare Pages, you can write the following handler on `functions/[[path]].ts`:\n\n```ts\n// functions/[[path]].ts\nimport handle from 'hono-react-router-adapter/cloudflare-pages'\nimport * as build from '../build/server'\nimport server from '../server'\n\nexport const onRequest = handle(build, server)\n```\n\n## Node.js\n\nIf you want to run your app on Node.js, you can use `hono-react-router-adapter/node`. Write `main.ts`:\n\n```ts\n// main.ts\nimport { serve } from '@hono/node-server'\nimport { serveStatic } from '@hono/node-server/serve-static'\nimport handle from 'hono-react-router-adapter/node'\nimport * as build from './build/server'\nimport { getLoadContext } from './load-context'\nimport server from './server'\n\nserver.use(\n  serveStatic({\n    root: './build/client',\n  })\n)\n\nconst handler = handle(build, server, { getLoadContext })\n\nserve({ fetch: handler.fetch, port: 3010 })\n```\n\nRun `main.ts` with [`tsx`](https://github.com/privatenumber/tsx):\n\n```bash\ntsx main.ts\n```\n\nOr you can compile to a pure JavaScript file with `esbuild` with the command below:\n\n```bash\nesbuild main.ts --bundle --outfile=main.mjs --platform=node --target=node16.8 --format=esm --banner:js='import { createRequire as topLevelCreateRequire } from \"module\"; const require = topLevelCreateRequire(import.meta.url);'\n```\n\n## `getLoadContext`\n\nIf you want to add extra context values when you use React Router routes, like in the following use case:\n\n```ts\n// app/routes/_index.tsx\nimport type { Route } from './+types/home'\n\nexport const loader = (args: Route.LoaderArgs) =\u003e {\n  return { extra: args.context.extra }\n}\n\nexport default function Home({ loaderData }: Route.ComponentProps) {\n  const { extra } = loaderData\n  return \u003ch1\u003eExtra is {extra}\u003c/h1\u003e\n}\n```\n\nFirst, create the `getLoadContext` function and export it:\n\n```ts\n// load-context.ts\nimport type { AppLoadContext } from 'react-router'\nimport type { PlatformProxy } from 'wrangler'\n\ntype Cloudflare = Omit\u003cPlatformProxy, 'dispose'\u003e\n\ndeclare module 'react-router' {\n  interface AppLoadContext {\n    cloudflare: Cloudflare\n    extra: string\n  }\n}\n\ntype GetLoadContext = (args: {\n  request: Request\n  context: { cloudflare: Cloudflare }\n}) =\u003e AppLoadContext\n\nexport const getLoadContext: GetLoadContext = ({ context }) =\u003e {\n  return {\n    ...context,\n    extra: 'stuff',\n  }\n}\n```\n\nThen import the `getLoadContext` and add it to the `serverAdapter` as an argument in your `vite.config.ts`:\n\n```ts\n// vite.config.ts\nimport adapter from '@hono/vite-dev-server/cloudflare'\nimport { reactRouter } from '@react-router/dev'\nimport serverAdapter from 'hono-react-router-adapter/vite'\nimport { defineConfig } from 'vite'\nimport { getLoadContext } from './load-context'\n\nexport default defineConfig({\n  plugins: [\n    // ...\n    reactRouter(),\n    serverAdapter({\n      adapter,\n      getLoadContext,\n      entry: 'server/index.ts',\n    }),\n  ],\n})\n```\n\nFor Cloudflare Workers, you can add it to the `handler` function:\n\n```ts\n// worker.ts\nimport handle from 'hono-react-router-adapter/cloudflare-workers'\nimport * as build from './build/server'\nimport { getLoadContext } from './load-context'\nimport app from './server'\n\nexport default handle(build, app, { getLoadContext })\n```\n\nYou can also add it for Cloudflare Pages:\n\n```ts\n// functions/[[path]].ts\nimport handle from 'hono-react-router-adapter/cloudflare-pages'\nimport { getLoadContext } from 'load-context'\nimport * as build from '../build/server'\nimport server from '../server'\n\nexport const onRequest = handle(build, server, { getLoadContext })\n```\n\nThis way is almost the same as [Remix](https://remix.run/docs/en/main/guides/vite#augmenting-load-context).\n\n### Getting Hono context\n\nYou can get the Hono context in React Router routes. For example, you can pass the value with `c.set()` from your Hono instance in the `server/index.ts`:\n\n```ts\n// server/index.ts\nimport { Hono } from 'hono'\n\nconst app = new Hono\u003c{\n  Variables: {\n    message: string\n  }\n}\u003e()\n\napp.use(async (c, next) =\u003e {\n  c.set('message', 'Hi from Hono')\n  await next()\n})\n\nexport default app\n```\n\nIn the React Router route, you can get the context from `args.context.hono.context`:\n\n```ts\n// app/routes/home.tsx\nimport type { Route } from './+types/home'\n\nexport const loader = (args: Route.LoaderArgs) =\u003e {\n  const message = args.context.hono.context.get('message')\n  return { message }\n}\n\nexport default function Home({ loaderData }: Route.ComponentProps) {\n  const { message } = loaderData\n  return \u003ch1\u003eMessage is {message}\u003c/h1\u003e\n}\n```\n\nTo enable type inference, config the `load-context.ts` like follows:\n\n```ts\n// load-context.ts\nimport type { AppLoadContext } from 'react-router'\nimport type { Context } from 'hono'\nimport type { PlatformProxy } from 'wrangler'\n\ntype Env = {\n  Variables: {\n    message: string\n  }\n}\n\ntype Cloudflare = Omit\u003cPlatformProxy, 'dispose'\u003e\n\ndeclare module 'react-router' {\n  interface AppLoadContext {\n    cloudflare: Cloudflare\n    hono: {\n      context: Context\u003cEnv\u003e\n    }\n    extra: string\n  }\n}\n\ntype GetLoadContext = (args: {\n  request: Request\n  context: {\n    cloudflare: Cloudflare\n    hono: { context: Context\u003cEnv\u003e }\n  }\n}) =\u003e AppLoadContext\n\nexport const getLoadContext: GetLoadContext = ({ context }) =\u003e {\n  return {\n    ...context,\n    extra: 'stuff',\n  }\n}\n```\n\n## AsyncLocalStorage\n\nYou can use AsyncLocalStorage, which is supported by Node.js, Cloudflare Workers, etc.\nYou can easily store context using Hono's Context Storage Middleware.\n\n```ts\n// server/index.ts\nimport { Hono } from 'hono'\nimport { contextStorage } from 'hono/context-storage'\n\nexport interface Env {\n  Variables: {\n    message: string\n    // db: DatabaseConnection // It's also a good idea to store database connections, etc.\n  }\n}\n\nconst app = new Hono\u003cEnv\u003e()\n\napp.use(contextStorage())\n\napp.use(async (c, next) =\u003e {\n  c.set('message', 'Hello!')\n\n  await next()\n})\n\nexport default app\n```\n\nYou can retrieve and process the context saved in Hono from React Router as follows:\n\n```ts\n// app/routes/home.tsx\nimport type { Env } from 'server'\nimport { getContext } from 'hono/context-storage' // It can be called anywhere for server-side processing.\n\nexport const loader = () =\u003e {\n  const message = getContext\u003cEnv\u003e().var.message\n  ...\n}\n```\n\n\u003e [!NOTE]\n\u003e To use AsyncLocalStorage on Cloudflare Workers, enable [the Node.js compatibility flag](https://developers.cloudflare.com/workers/runtime-apis/nodejs).\n\n## Auth middleware for React Router routes\n\nIf you want to add Auth Middleware, e.g. Basic Auth middleware, please be careful that users can access the protected pages with SPA tradition. To prevent this, add a `loader` to the page:\n\n```ts\n// app/routes/admin\nexport const loader = async () =\u003e {\n  return { props: {} }\n}\n```\n\n## Related works\n\n- https://github.com/sergiodxa/remix-hono\n- https://github.com/yusukebe/hono-and-remix-on-vite\n\n## Author\n\nYusuke Wada \u003chttps://github.com/yusukebe\u003e\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyusukebe%2Fhono-react-router-adapter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyusukebe%2Fhono-react-router-adapter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyusukebe%2Fhono-react-router-adapter/lists"}