{"id":15723271,"url":"https://github.com/rphlmr/react-router-hono-server","last_synced_at":"2025-05-15T07:07:26.059Z","repository":{"id":251615556,"uuid":"837635091","full_name":"rphlmr/react-router-hono-server","owner":"rphlmr","description":"Remix with Hono in less than 10 seconds","archived":false,"fork":false,"pushed_at":"2025-04-30T19:20:48.000Z","size":1162,"stargazers_count":340,"open_issues_count":8,"forks_count":14,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-11T09:46:37.013Z","etag":null,"topics":["honojs","react-router","react-router-v7","remix","remix-run"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/react-router-hono-server","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/rphlmr.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.MD","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["rphlmr"]}},"created_at":"2024-08-03T14:57:50.000Z","updated_at":"2025-05-11T09:44:14.000Z","dependencies_parsed_at":"2024-08-22T09:56:03.420Z","dependency_job_id":"c67cea11-8309-4c13-a485-521453bd6ed9","html_url":"https://github.com/rphlmr/react-router-hono-server","commit_stats":{"total_commits":20,"total_committers":3,"mean_commits":6.666666666666667,"dds":"0.44999999999999996","last_synced_commit":"0ee11d2d6d3b79c79fdd07156da4dfc6164c054c"},"previous_names":["rphlmr/react-router-hono-server"],"tags_count":37,"template":false,"template_full_name":"forge-42/open-source-stack","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rphlmr%2Freact-router-hono-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rphlmr%2Freact-router-hono-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rphlmr%2Freact-router-hono-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rphlmr%2Freact-router-hono-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rphlmr","download_url":"https://codeload.github.com/rphlmr/react-router-hono-server/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254292042,"owners_count":22046426,"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":["honojs","react-router","react-router-v7","remix","remix-run"],"created_at":"2024-10-03T22:10:52.984Z","updated_at":"2025-05-15T07:07:21.051Z","avatar_url":"https://github.com/rphlmr.png","language":"TypeScript","funding_links":["https://github.com/sponsors/rphlmr"],"categories":[],"sub_categories":[],"readme":"# React Router v7 Hono Server\n\n\u003c!-- ![GitHub Repo stars](https://img.shields.io/github/stars/rphlmr/react-router-hono-server?style=social)\n![npm](https://img.shields.io/npm/v/open-source-stack?style=plastic)\n![GitHub](https://img.shields.io/github/license/rphlmr/react-router-hono-server?style=plastic)\n![npm](https://img.shields.io/npm/dy/open-source-stack?style=plastic)\n![npm](https://img.shields.io/npm/dw/open-source-stack?style=plastic)\n![GitHub top language](https://img.shields.io/github/languages/top/rphlmr/react-router-hono-server?style=plastic) --\u003e\n\n## Psst!\n\u003e [!IMPORTANT]\n\u003e This package is only compatible with React Router v7\n\u003e\n\u003e You can still use the v1 with @remix-run. [Previous docs](https://github.com/rphlmr/react-router-hono-server/tree/v1.2.0)\n\u003e\n\u003e Migration guide from v1 [here](#migrate-from-v1)\n\n## TLDR\nThis package contains a helper function `createHonoServer` that enables you to create a Hono\nserver bound to your React Router v7 app.\n\nSince the Hono server is built along with the rest of your app, you may import app modules as needed.\n\nIt also supports Vite HMR via the `react-router-hono-server/dev` plugin (which is required\nfor this to function).\n\nIt presets a default Hono server config that you can [customize](#options)\n\n\n\u003e [!IMPORTANT]\n\u003e Only works with React Router v7 in **ESM mode**\n\u003e\n\u003e Only works with **Vite**\n\u003e\n\u003e Only **Node**, **Bun**, **Cloudflare Workers** and **AWS Lambda** (experimental) are supported\n\n\u003e [!TIP]\n\u003e 👨‍🏫 There is some examples in the [examples](./examples) folder. I hope they will help you.\n\u003e\n\u003e You can use [remix-hono](https://github.com/sergiodxa/remix-hono) to add cool middleware like [`session`](https://github.com/sergiodxa/remix-hono?tab=readme-ov-file#session-management)\n\n## Installation\n\nInstall the following npm package.\n\n\u003e [!NOTE]\n\u003e This is not a dev dependency, as it creates the Hono server used in production.\n\n```bash\nnpm install react-router-hono-server\n\n# For Cloudflare Workers, add the following\nnpm install -D miniflare wrangler\n```\n\n\u003e [!TIP]\n\u003e You don't need to install `hono` as it is included in this package.\n\u003e\n\u003e If you use `pnpm`, and want to use some imports from `hono`, you may need to install `hono` manually or create a `.npmrc` file in your project with the following content:\n\u003e\n\u003e ```\n\u003e public-hoist-pattern[]=hono\n\u003e ```\n\n## Easy mode\nIn your `vite.config.ts`, add the `reactRouterHonoServer` plugin.\n\n```ts\nimport { reactRouter } from \"@react-router/dev/vite\";\nimport { reactRouterHonoServer } from \"react-router-hono-server/dev\"; // add this\nimport { defineConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\nexport default defineConfig({\n  plugins: [\n    reactRouterHonoServer(), // add this\n    reactRouter(),\n    tsconfigPaths()\n  ],\n});\n```\n**That's all!**\n\nWait, what?\n\nFor really simple apps, that's all you need to do. Behind the hood, it will create a virtual module with a default Hono server.\nWhen building for production, it will create the server file at `build/server/index.js` and import your React Router app from `virtual:react-router/server-build` module (replacing it with the real file located in `build/server/assets/server-build-[hash].js`).\n\n## Configuration\nOk, by default it works, but you may want to customize the server and use some middleware.\n\n\u003e [!IMPORTANT]\n\u003e Until you define your own `serverEntryPoint`, the file name `${appDirectory}/server.ts` and the folder name `${appDirectory}/server` are reserved words.\n\u003e\n\u003e `reactRouterHonoServer` plugin is looking for them to find your server file.\n\n\n### Add the Vite plugin\n\u003e [!NOTE]\n\u003e It uses the `reactRouter` plugin to build your app and will automatically load its config.\n\n#### Node\n\u003e [!TIP]\n\u003e Check this [example](./examples/node/simple/) to see how to use it.\n\n```ts\n// vite.config.ts\nimport { reactRouter } from \"@react-router/dev/vite\";\nimport { reactRouterHonoServer } from \"react-router-hono-server/dev\"; // add this\nimport { defineConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\nexport default defineConfig({\n  plugins: [\n    reactRouterHonoServer(), // add this\n    reactRouter(),\n    tsconfigPaths()\n  ],\n});\n```\n\n#### Bun\n\u003e [!TIP]\n\u003e Check this [example](./examples/bun/simple/) to see how to use it.\n\n\u003e [!IMPORTANT]\n\u003e React 19 and `bun --bun` flag require a special `entry.server.tsx` file. Check [Using `bun --bun` flag?](#using-bun---bun-flag)\n\u003e Check this [example](./examples/bun/simple-bun-runtime/) to see how to use it.\n\n```ts\n// vite.config.ts\nimport { reactRouter } from \"@react-router/dev/vite\";\nimport { reactRouterHonoServer } from \"react-router-hono-server/dev\"; // add this\nimport { defineConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\nexport default defineConfig({\n  plugins: [\n    reactRouterHonoServer({ runtime: \"bun\"} ), // add this\n    reactRouter(),\n    tsconfigPaths()\n  ],\n});\n```\n\n##### Using `bun --bun` flag?\n\u003e [!TIP]\n\u003e Check this [example](./examples/bun/simple-bun-runtime/) to see how to use it.\n\nIf you are using the [`bun --bun` flag](https://bun.sh/docs/cli/run#bun), you need to have a proper `entry.server.tsx` file.\n\nWhen building for production, at least for React 19, it uses a special `react-dom-server.bun` import with only the `renderToReadableStream` function available.\n\n\n```ts\nimport { isbot } from \"isbot\";\nimport { renderToReadableStream } from \"react-dom/server.bun\";\nimport type { AppLoadContext, EntryContext } from \"react-router\";\nimport { ServerRouter } from \"react-router\";\n\nexport default async function handleRequest(\n  request: Request,\n  responseStatusCode: number,\n  responseHeaders: Headers,\n  routerContext: EntryContext,\n  _loadContext: AppLoadContext\n) {\n  let shellRendered = false;\n  const userAgent = request.headers.get(\"user-agent\");\n\n  const body = await renderToReadableStream(\u003cServerRouter context={routerContext} url={request.url} /\u003e, {\n    onError(error: unknown) {\n      responseStatusCode = 500;\n      if (shellRendered) {\n        console.error(error);\n      }\n    },\n  });\n  shellRendered = true;\n\n  if ((userAgent \u0026\u0026 isbot(userAgent)) || routerContext.isSpaMode) {\n    await body.allReady;\n  }\n\n  responseHeaders.set(\"Content-Type\", \"text/html\");\n  return new Response(body, {\n    headers: responseHeaders,\n    status: responseStatusCode,\n  });\n}\n```\n\n#### Cloudflare Workers\n\u003e [!TIP]\n\u003e Check this [example](./examples/cloudflare/simple/) to see how to use it.\n\n\u003e [!IMPORTANT]\n\u003e You need to add the `cloudflareDevProxy` plugin to use the Cloudflare Workers runtime on dev.\n\n\n```ts\n// vite.config.ts\nimport { reactRouter } from \"@react-router/dev/vite\";\nimport { cloudflareDevProxy } from \"@react-router/dev/vite/cloudflare\"; // add this\nimport { reactRouterHonoServer } from \"react-router-hono-server/dev\"; // add this\nimport { defineConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\nexport default defineConfig({\n  plugins: [\n    cloudflareDevProxy(),\n    reactRouterHonoServer({ runtime: \"cloudflare\"} ), // add this\n    reactRouter(),\n    tsconfigPaths()\n  ],\n});\n```\n\n##### React 19\n\u003e [!IMPORTANT]\n\u003e Check this [example](./examples/cloudflare/simple-19/) to see how to use it.\n\n\u003e [!TIP]\n\u003e You may need to add the `force_react_19` flag to the `reactRouterHonoServer` plugin config.\n\n```ts\n// vite.config.ts\nreactRouterHonoServer({ runtime: \"cloudflare\", flag: { force_react_19: true } }), // add this\n```\n\n#### AWS Lambda\n\u003e [!TIP]\n\u003e AWS shares the same runtime as Node.\n\u003c!-- \u003e Check this [example](./examples/node/simple/) to see how to use it. --\u003e\n\n```ts\n// vite.config.ts\nimport { reactRouter } from \"@react-router/dev/vite\";\nimport { reactRouterHonoServer } from \"react-router-hono-server/dev\"; // add this\nimport { defineConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\nexport default defineConfig({\n  plugins: [\n    reactRouterHonoServer({ runtime: \"aws\", dev: { export: \"handler\" } }), // add this\n    reactRouter(),\n    tsconfigPaths()\n  ],\n});\n```\n\n### Create the server\n\u003e [!TIP]\n\u003e You can use the CLI to create the server file for you.\n\u003e\n\u003e ```bash\n\u003e npx react-router-hono-server reveal file\n\u003e ```\n\nIn your `app` folder, create a file named `server.ts` and export **as default** the server created by `createHonoServer`.\n\n```bash\ntouch app/server.ts\n```\n\n```ts\n// app/server.ts\nimport { createHonoServer } from \"react-router-hono-server/node\";\n\nexport default await createHonoServer({/* options */});\n```\n\n#### Alternative\nYou can define your server in `app/server/index.ts`.\n\n\u003e [!TIP]\n\u003e You can use the CLI to create the server file for you.\n\u003e\n\u003e ```bash\n\u003e npx react-router-hono-server reveal folder\n\u003e ```\n\nIt is useful if you have many middleware and want to keep your server file clean.\n\n```ts\n// app/server/index.ts\n\nimport { createHonoServer } from \"react-router-hono-server/node\";\n\nexport default await createHonoServer({/* options */});\n```\n\n#### I don't like this default\nNo problem, you can define your files wherever you want.\n\nUse the `serverEntryPoint` option of the Vite plugin `reactRouterHonoServer` to point to your server file.\n\n\n### Update your package.json scripts\n### Node\nIt is not an error, you can keep the React Router defaults for `build` and `dev`!\n```json\n  \"scripts\": {\n    \"build\": \"react-router build\",\n    \"dev\": \"react-router dev\",\n    \"start\": \"node ./build/server/index.js\",\n  },\n```\n### Bun\nIt is not an error, you can keep the React Router defaults for `build`!\n```json\n  \"scripts\": {\n    \"build\": \"react-router build\",\n    \"dev\": \"bunx --bun vite\",\n    \"start\": \"bun ./build/server/index.js\",\n  },\n```\n\n### Cloudflare\nIt is not an error, you can keep the React Router defaults for `build` and `dev`!\n```json\n  \"scripts\": {\n    \"build\": \"react-router build\",\n    \"dev\": \"react-router dev\",\n    \"start\": \"wrangler dev\",\n  },\n```\n\n#### Wrangler\nAdd a file named `wrangler.toml` at the root of your project (close to `package.json`).\n\nAdapt the `main` and `assets` fields based on your build output, if you changed them from the defaults.\n```toml\nworkers_dev = true\nname = \"my-worker\"\ncompatibility_date = \"2024-11-18\"\nmain = \"./build/server/index.js\"\nassets = { directory = \"./build/client/\" }\n```\n\n##### Custom assets serving\nYou can set Cloudflare `experimental_serve_directly` and delegate assets serving to Hono, like for Node and Bun.\n\n\u003e [!TIP]\n\u003e Check https://developers.cloudflare.com/workers/static-assets/binding/#experimental_serve_directly\n\n\u003e [!TIP]\n\u003e Check this [example](./examples/cloudflare/simple/) to see how to use it.\n\n```toml\n[assets]\ndirectory = \"./build/client/\"\nbinding = \"ASSETS\"\nexperimental_serve_directly = false\n```\n\n### AWS Lambda\nIt is not an error, you can keep the React Router defaults for `build` and `dev`!\n```json\n  \"scripts\": {\n    \"build\": \"react-router build\",\n    \"dev\": \"react-router dev\",\n    \"start\": \"I don't know how to start it in production mode 😅\",\n  },\n```\n\n## How it works\n\nThis helper works differently depending on the environment.\n\nIn development, it uses [@hono/vite-dev-server](https://github.com/honojs/vite-plugins/tree/main/packages/dev-server) and loads your server and React Router app with `import('virtual:react-router/server-build')`.\nIt can be configured in `vite.config.ts`.\n\nIn `production`, it will create a standard node HTTP server listening at `HOST:PORT`.\nYou can customize the production server port using the `port` option of `createHonoServer`.\n\nWhen building for production, the Hono server is compiled as `build/server/index.js` and imports your React Router app from `assets/server-build-[hash].js`.\n\nTo run the server in production, use `node ./build/server/index.js`.\n\nThat's all!\n\n### Options\n\n#### `reactRouterHonoServer` (Vite Plugin)\n```ts\ntype Runtime = \"node\" | \"bun\" | \"cloudflare\" | \"aws\";\n\ntype ReactRouterHonoServerPluginOptions = {\n  /**\n   * The runtime to use for the server.\n   *\n   * Defaults to `node`.\n   */\n  runtime?: Runtime;\n  /**\n   * The path to the server file, relative to `vite.config.ts`.\n   *\n   * If it is a folder (`app/server`), it will look for an `index.ts` file.\n   *\n   * Defaults to `${appDirectory}/server[.ts | /index.ts]` if present.\n   *\n   * Fallback to a virtual module `virtual:react-router-hono-server/server`.\n   */\n  serverEntryPoint?: string;\n  /**\n   * The paths that are not served by the dev-server.\n   *\n   * Defaults include `appDirectory` content.\n   */\n  dev?: {\n    /**\n     * The paths that are not served by the dev-server.\n     *\n     * Defaults include `appDirectory` content.\n     */\n    exclude?: (string | RegExp)[];\n    /**\n     * The name of the export to use for the server.\n     *\n     * Defaults to `default`.\n     */\n    export?: string;\n  };\n};\n```\n\n#### `createHonoServer`\n##### All adapters\n```ts\nexport type HonoServerOptions\u003cE extends Env = BlankEnv\u003e = {\n  /**\n   * The base Hono app to use\n   *\n   * It will be used to mount the React Router server on the `basename` path\n   * defined in the [React Router config](https://api.reactrouter.com/v7/types/_react_router_dev.config.Config.html)\n   *\n   * {@link Hono}\n   */\n  app?: Hono\u003cE\u003e;\n  /**\n   * Enable the default logger\n   *\n   * Defaults to `true`\n   */\n  defaultLogger?: boolean;\n  /**\n   * The port to start the server on\n   *\n   * Defaults to `process.env.PORT || 3000`\n   */\n  port?: number;\n  /**\n   * Customize the Hono server, for example, adding middleware\n   *\n   * It is applied after the default middleware and before the React Router middleware\n   */\n  configure?: \u003cE extends Env = BlankEnv\u003e(server: Hono\u003cE\u003e) =\u003e Promise\u003cvoid\u003e | void;\n  /**\n   * Augment the React Router AppLoadContext\n   *\n   * Don't forget to declare the AppLoadContext in your app, next to where you create the Hono server\n   *\n   * ```ts\n   * declare module \"react-router\" {\n   *   interface AppLoadContext {\n   *     // Add your custom context here\n   *     whatever: string;\n   *   }\n   * }\n   * ```\n   */\n  getLoadContext?: (\n    c: Context,\n    options: {\n      build: ServerBuild;\n      mode: \"development\" | \"production\" | \"test\";\n    }\n  ) =\u003e Promise\u003cAppLoadContext\u003e | AppLoadContext;\n  /**\n   * Hook to add middleware that runs before any built-in middleware, including assets serving.\n   *\n   * You can use it to add protection middleware, for example.\n   */\n  beforeAll?: (app: Hono\u003cE\u003e) =\u003e Promise\u003cvoid\u003e | void;\n};\n```\n\nYou can add additional Hono middleware with the `configure` function. If you do not provide a function, it will create a default Hono server.\n\nThe `configure` function can be async. So, make sure to `await createHonoServer()`.\n\nIf you want to set up the React Router `AppLoadContext`, pass in a function to `getLoadContext`.\n\nModify the `AppLoadContext` interface used in your app.\n\nSince the Hono server is compiled in the same bundle as the rest of your React Router app, you can import app modules just like you normally would.\n\n###### Example\n\n```ts\n// app/server.ts\n\nimport { createHonoServer } from \"react-router-hono-server/node\";\n\n/**\n * Declare our loaders and actions context type\n */\ndeclare module \"react-router\" {\n  interface AppLoadContext {\n    /**\n     * The app version from the build assets\n     */\n    readonly appVersion: string;\n  }\n}\n\nexport default await createHonoServer({\n  getLoadContext(_, { build, mode }) {\n    const isProductionMode = mode === \"production\";\n    return {\n      appVersion: isProductionMode ? build.assets.version : \"dev\",\n    };\n  },\n});\n```\n\n```ts\n// app/routes/test.tsx\nimport type { Route } from \"./+types/test\";\nexport async function loader({ context }: Route.LoaderArgs) {\n  // get the context provided from `getLoadContext`\n  return { appVersion: context.appVersion }\n}\n```\n\n\u003e [!TIP]\n\u003e If you declare your `getLoadContext` function in a separate file, you can use the helper `createGetLoadContext` from `react-router-hono-server/{adapter}`\n\u003e ```ts\n\u003e import { createGetLoadContext } from \"react-router-hono-server/node\";\n\u003e\n\u003e export const getLoadContext = createGetLoadContext((c, { mode, build }) =\u003e {\n\u003e   const isProductionMode = mode === \"production\";\n\u003e   return {\n\u003e     appVersion: isProductionMode ? build.assets.version : \"dev\",\n\u003e   };\n\u003e });\n\u003e ```\n\n##### Node\n```ts\nexport interface HonoServerOptions\u003cE extends Env = BlankEnv\u003e extends HonoServerOptionsBase\u003cE\u003e {\n  /**\n   * Listening listener (production mode only)\n   *\n   * It is called when the server is listening\n   *\n   * Defaults log the port\n   */\n  listeningListener?: (info: AddressInfo) =\u003e void;\n  /**\n   * Customize the node server (ex: using http2)\n   *\n   * {@link https://hono.dev/docs/getting-started/nodejs#http2}\n   */\n  customNodeServer?: CreateNodeServerOptions;\n  /**\n   * Callback executed just after `serve` from `@hono/node-server`\n   *\n   * **Only applied to production mode**\n   *\n   * For example, you can use this to bind `@hono/node-ws`'s `injectWebSocket`\n   */\n  onServe?: (server: ServerType) =\u003e void;\n  /**\n   * The Node.js Adapter rewrites the global Request/Response and uses a lightweight Request/Response to improve performance.\n   *\n   * If you want this behavior, set it to `true`\n   *\n   * 🚨 Setting this to `true` can break `request.clone()` if you later check `instanceof Request`.\n   *\n   * {@link https://github.com/honojs/node-server?tab=readme-ov-file#overrideglobalobjects}\n   *\n   * @default false\n   */\n  overrideGlobalObjects?: boolean;\n  /**\n   * Customize the hostname of the node server\n   */\n  hostname?: string;\n  /**\n   * Customize the serve static options\n   */\n  serveStaticOptions?: {\n    /**\n     * Customize the public assets (what's in your `public` directory) serve static options.\n     *\n     */\n    publicAssets?: Omit\u003cServeStaticOptions\u003cE\u003e, \"root\"\u003e;\n    /**\n     * Customize the client assets (what's in your `build/client/assets` directory - React Router) serve static options.\n     *\n     */\n    clientAssets?: Omit\u003cServeStaticOptions\u003cE\u003e, \"root\"\u003e;\n  };\n}\n```\n\n##### Bun\n```ts\nexport interface HonoServerOptions\u003cE extends Env = BlankEnv\u003e extends HonoServerOptionsBase\u003cE\u003e {\n  /**\n   * Customize the bun server\n   *\n   * {@link https://bun.sh/docs/api/http#start-a-server-bun-serve}\n   */\n  customBunServer?: Serve \u0026 ServeOptions;\n  /**\n   * Customize the serve static options\n   */\n  serveStaticOptions?: {\n    /**\n     * Customize the public assets (what's in your `public` directory) serve static options.\n     *\n     */\n    publicAssets?: Omit\u003cServeStaticOptions\u003cE\u003e, \"root\"\u003e;\n    /**\n     * Customize the client assets (what's in your `build/client/assets` directory - React Router) serve static options.\n     *\n     */\n    clientAssets?: Omit\u003cServeStaticOptions\u003cE\u003e, \"root\"\u003e;\n  };\n}\n```\n\n##### Cloudflare Workers\n```ts\nexport interface HonoServerOptions\u003cE extends Env = BlankEnv\u003e extends Omit\u003cHonoServerOptionsBase\u003cE\u003e, \"port\"\u003e {}\n```\n\n##### AWS Lambda\n```ts\ntype InvokeMode = \"default\" | \"stream\";\n\ninterface HonoAWSServerOptions\u003cE extends Env = BlankEnv\u003e extends Omit\u003cHonoServerOptionsBase\u003cE\u003e, \"port\"\u003e {\n  /**\n   * The invoke mode to use\n   *\n   * Defaults to `default`\n   *\n   * {@link https://hono.dev/docs/getting-started/aws-lambda#lambda-response-streaming}\n   */\n  invokeMode: InvokeMode;\n}\n```\n\n## Middleware\n\n🚨 Redirecting from a middleware\n\u003e [!IMPORTANT]\n\u003e TLDR: If you encounter a `Error: Unable to decode turbo-stream response` after a redirect from your middleware, try to use `redirect` instead of `c.redirect`.\n\u003e\n\u003e `redirect` will use `c.redirect` for \"normal\" requests and a single-fetch-like response for React Router `.data` requests.\n\u003e\n\u003e If the next handler is a Hono middleware (ex: https://github.com/rphlmr/react-router-hono-server/discussions/56), you can use `c.redirect` as usual.\n\u003e\n\u003e You **have to** use the `redirect` helper to redirect from a middleware if the next handler that will receive this redirect is a React Router route.\n\u003e\n\u003e It returns a single-fetch-like response.\n\u003e\n\u003e If you use `c.redirect`, it will not work as expected and you will get a `Unable to decode turbo-stream response` error.\n\u003e\n\u003e```ts\n\u003e import { redirect } from \"react-router-hono-server/http\";\n\u003e```\n\u003e\n\u003e I'm sorry for this inconvenience, I hope it can be \"fixed\" in future React Router versions.\n\nMiddleware are functions that are called before React Router calls your loader/action.\n\nHono is the perfect tool for this, as it supports middleware out of the box.\n\nSee the [Hono docs](https://hono.dev/docs/guides/middleware) for more information.\n\nYou can imagine many use cases for middleware, such as authentication, protecting routes, caching, logging, etc.\n\nSee how [Shelf.nu](https://github.com/Shelf-nu/shelf.nu/blob/main/server/middleware.ts) uses them!\n\n\u003e [!TIP]\n\u003e This lib exports one middleware `cache` (`react-router-hono-server/middleware`) that you can use to cache your responses.\n\n### `beforeAll`\nYou can use the `beforeAll` option to add middleware that runs before any built-in middleware, including assets serving.\n\nYou can use it to add protection middleware, for example.\n\n\u003e [!TIP]\n\u003e When you check the path to protect, don't forget to use `c.req.path.includes(\"\")` to handle `.data` requests (`loader`)!\n\n```ts\nimport { reactRouterRedirect } from \"react-router-hono-server/http\";\nimport { createHonoServer } from \"react-router-hono-server/node\";\n\nexport default await createHonoServer({\n  beforeAll(app) {\n    app.use(async (c, next) =\u003e {\n      if (c.req.path.includes(\"/protected\") \u0026\u0026 !c.req.header(\"Authorization\")) {\n        return reactRouterRedirect(\"/login\");\n      }\n\n      return next();\n    });\n  },\n});\n```\n\n### Using remix-hono middleware\n\nIt is easy to use [remix-hono](https://github.com/sergiodxa/remix-hono) middleware with this package.\n\n```ts\nimport { createCookieSessionStorage } from \"react-router\";\nimport { createHonoServer } from \"react-router-hono-server/node\";\nimport { session } from \"remix-hono/session\";\n\nexport default await createHonoServer({\n  configure: (server) =\u003e {\n    server.use(\n      session({\n        autoCommit: true,\n        createSessionStorage() {\n          const sessionStorage = createCookieSessionStorage({\n            cookie: {\n              name: \"session\",\n              httpOnly: true,\n              path: \"/\",\n              sameSite: \"lax\",\n              secrets: [process.env.SESSION_SECRET],\n              secure: process.env.NODE_ENV === \"production\",\n            },\n          });\n\n          return {\n            ...sessionStorage,\n            // If a user doesn't come back to the app within 30 days, their session will be deleted.\n            async commitSession(session) {\n              return sessionStorage.commitSession(session, {\n                maxAge: 60 * 60 * 24 * 30, // 30 days\n              });\n            },\n          };\n        },\n      })\n    );\n  },\n});\n```\n\n### Creating custom Hono Middleware\n\nYou can create middleware using the [`createMiddleware`](https://hono.dev/docs/helpers/factory#createmiddleware) or [`createFactory`](https://hono.dev/docs/helpers/factory#createfactory) functions from `hono/factory`.\n\nThen, use them with the `configure` function of `createHonoServer`.\n\n```ts\nimport { createMiddleware } from \"hono/factory\";\nimport { createHonoServer } from \"react-router-hono-server/node\";\n\nexport default await createHonoServer({\n  configure: (server) =\u003e {\n    server.use(\n      createMiddleware(async (c, next) =\u003e {\n        console.log(\"middleware\");\n        return next();\n      })\n    );\n  },\n});\n```\n\n### Using React Router Middleware (unstable future)\nYou can use React Router middleware with this package.\n\n\u003e [!TIP]\n\u003e Check this [example](./examples/node/simple-future-middleware/) to see how to use it.\n\n\u003e [!IMPORTANT]\n\u003e Please note that this is an unstable feature and could change in the future.\n\u003e\n\u003e If you already have a custom `getLoadContext` function, you now have to return a `Map` from it. Check the example for more information.\n\n\n### Using WebSockets\n#### Node\nThis package has a built-in helper to use `@hono/node-ws`\n\n\u003e [!TIP]\n\u003e Check this [example](./examples/node/websocket/) to see how to use it.\n\n```ts\nimport type { WSContext } from \"hono/ws\";\nimport { createHonoServer } from \"react-router-hono-server/node\";\n\n// Store connected clients\nconst clients = new Set\u003cWSContext\u003e();\n\nexport default await createHonoServer({\n  useWebSocket: true,\n  // 👆 Unlock this 👇 from @hono/node-ws\n  configure: (app, { upgradeWebSocket }) =\u003e {\n    app.get(\n      \"/ws\",\n      upgradeWebSocket((c) =\u003e ({\n        // https://hono.dev/helpers/websocket\n        onOpen(_, ws) {\n          console.log(\"New connection ⬆️\");\n          clients.add(ws);\n        },\n        onMessage(event, ws) {\n          console.log(\"Context\", c.req.header(\"Cookie\"));\n          console.log(\"Event\", event);\n          console.log(`Message from client: ${event.data}`);\n          // Broadcast to all clients except sender\n          clients.forEach((client) =\u003e {\n            if (client.readyState === 1) {\n              client.send(`${event.data}`);\n            }\n          });\n        },\n        onClose(_, ws) {\n          console.log(\"Connection closed\");\n          clients.delete(ws);\n        },\n      }))\n    );\n  },\n});\n```\n\n#### Bun\nThis package has a built-in helper to use `hono/bun` in prod and `@hono/node-ws` in dev\n\n\u003e [!TIP]\n\u003e Check this [example](./examples/bun/websocket/) to see how to use it.\n\n```ts\nimport { WSContext } from \"hono/ws\";\nimport { createHonoServer } from \"react-router-hono-server/bun\";\n\n// Store connected clients\nconst clients = new Set\u003cWSContext\u003e();\n\nexport default await createHonoServer({\n  useWebSocket: true,\n  // 👆 Unlock this 👇 from @hono/node-ws in dev, hono/bun in prod\n  configure(app, { upgradeWebSocket }) {\n    app.get(\n      \"/ws\",\n      upgradeWebSocket((c) =\u003e ({\n        // https://hono.dev/helpers/websocket\n        onOpen(_, ws) {\n          console.log(\"New connection 🔥\");\n          clients.add(ws);\n        },\n        onMessage(event, ws) {\n          console.log(\"Context\", c.req.header(\"Cookie\"));\n          console.log(\"Event\", event);\n          console.log(`Message from client: ${event.data}`);\n          // Broadcast to all clients except sender\n          clients.forEach((client) =\u003e {\n            if (client.readyState === 1) {\n              client.send(`${event.data}`);\n            }\n          });\n        },\n        onClose(_, ws) {\n          console.log(\"Connection closed\");\n          clients.delete(ws);\n        },\n      }))\n    );\n  },\n});\n```\n\n#### Cloudflare Workers\nCloudflare requires a different approach to WebSockets, based on Durable Objects.\n\n\u003e [!TIP]\n\u003e Check this [example](./examples/cloudflare/websocket/) to see how to use it.\n\n\u003e [!IMPORTANT]\n\u003e For now, HMR is not supported in Cloudflare Workers. Will try to come back to it later.\n\u003e\n\u003e Work in progress on Cloudflare team: https://github.com/flarelabs-net/vite-plugin-cloudflare\n\n#### AWS\nNot supported.\n\n## Basename and Hono sub apps\n\n\u003e [!NOTE]\n\u003e By default, the React Router app is mounted at `/` (default `basename` value).\n\u003e\n\u003e You may not need to use this option. It's for advanced use cases.\n\n\u003e [!TIP]\n\u003e Check this [example](./examples/node/custom-mount/) to see how to use it.\n\nYou can use the `basename` option in your React Router config (`react-router.config.ts`) to mount your React Router app on a subpath.\n\nIt will automatically mount the app on the subpath.\n\n```ts\n// react-router.config.ts\nimport type { Config } from \"@react-router/dev/config\";\n\nexport default {\n  basename: \"/app\", // Now the React Router app will be mounted on /app\n} satisfies Config;\n```\n\nThen, you can use the `app` option in `createHonoServer` to pass your \"root\" Hono app. This will be used to mount the React Router app on the `basename` path.\n\n```ts\nimport { Hono } from \"hono\";\nimport { createHonoServer } from \"react-router-hono-server/node\";\nimport { API_BASENAME, api } from \"./api\";\nimport { getLoadContext } from \"./context\";\n\n// Create a root Hono app\nconst app = new Hono();\n\n// Mount the API app at /api\napp.route(API_BASENAME, api);\n\nexport default await createHonoServer({\n  // Pass the root Hono app to the server.\n  // It will be used to mount the React Router app on the `basename` defined in react-router.config.ts\n  app,\n  getLoadContext,\n});\n```\n\u003e [!NOTE]\n\u003e You now have two entry points!\n\u003e - `/api` - for your API\n\u003e - `/app` - for your React Router app\n\n## Pre-rendering\nYou should be able to use pre-rendering with this package.\n\n### Node and Bun\n\u003e [!TIP]\n\u003e Check this [example](./examples/node/with-pre-render/) to see how to use it.\n\n\u003e [!IMPORTANT]\n\u003e You need to add the `serverBuildFile` option to your `react-router.config.ts` file.\n\u003e\n\u003e The file path is fixed to `assets/server-build.js`.\n\nAdd the pre-render option to your `react-router.config.ts`\n\n```ts\nimport type { Config } from \"@react-router/dev/config\";\n\nexport default {\n  prerender: [\"/\"],\n} satisfies Config;\n```\n\n### Cloudflare\n\u003e [!TIP]\n\u003e Check this [example](./examples/cloudflare/with-pre-render/) to see how to use it.\n\nAdd the pre-render option to your `react-router.config.ts`\n\n```ts\nimport type { Config } from \"@react-router/dev/config\";\n\nexport default {\n  prerender: [\"/\"],\n} satisfies Config;\n```\n\n\n## Migrate from v1\n_You should not expect any breaking changes._\n\n### Install the latest version\n\n```bash\nnpm install react-router-hono-server@latest\n```\n\n### Create the server file\n#### Option 1 - You previously had all your server code in `app/entry.server.tsx`\n```bash\ntouch app/server.ts\n```\nor\n```bash\nnpx react-router-hono-server reveal file\n```\n\n#### Option 2 - You previously had your server code in a `server` folder\n```bash\nmkdir app/server\ntouch app/server/index.ts\n```\nor\n```bash\nnpx react-router-hono-server reveal folder\n```\n\n### Move your server code\nMove your previous server code to the new file you created in the previous step.\n\n\u003e [!NOTE]\n\u003e You can remove the import from `react-router-hono-server/node` in your `entry.server.tsx` file and any other server code.\n\nMany options are gone, `serverBuildFile` `assetsDir` and `buildDirectory`.\n\nWe now use the Vite virtual import `virtual:react-router/server-build` to load the server build and we read the Vite config thanks to the `reactRouterHonoServer` plugin.\n\n\u003e [!IMPORTANT]\n\u003e You now need to export the server created by `createHonoServer` as **default**.\n\u003e\n\u003e ```ts\n\u003e import { createHonoServer } from \"react-router-hono-server/node\";\n\u003e\n\u003e export default await createHonoServer({/* other options */});\n\u003e ```\n\n### Update your `vite.config.ts`\n\n\u003e [!IMPORTANT]\n\u003e `devServer` is now `reactRouterHonoServer`.\n\nMany options are gone or have changed.\n\n`exportName` (`reactRouterHonoServer` expects a default export from your server file), `entry` is now `serverEntryPoint`. `appDirectory` is removed (read from `vite.config.ts`), and `exclude` has been moved under `dev`.\n\n\n##### You used `buildEnd` from `remix()` plugin or a custom `buildDirectory` option\nYou may know that it has been moved to `react-router.config.ts` (see [here](https://reactrouter.com/upgrading/remix#5-add-a-react-router-config) for more information).\n\nIf you used this hook for Sentry, check this [example](./examples/node/with-sentry/react-router.config.ts) to see how to migrate.\n\nIf you used a custom `buildDirectory` option, check this [example](./examples/node/custom-build/react-router.config.ts) to see how to migrate.\n\n### Update your package.json scripts\n```json\n  \"scripts\": {\n    \"build\": \"react-router build\",\n    \"dev\": \"react-router dev\",\n    \"start\": \"node ./build/server/index.js\",\n  },\n```\n\n## Special Thanks\n\nInspired by [remix-express-vite-plugin](https://github.com/kiliman/remix-express-vite-plugin) from [@kiliman](https://github.com/kiliman)\n\n`remix` handler was forked from [remix-hono](https://github.com/sergiodxa/remix-hono) by [@sergiodxa](https://github.com/sergiodxa) as it is a small and simple core dependency of this library.\n\nI will still help maintain it.\n\n## Contributors ✨\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frphlmr%2Freact-router-hono-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frphlmr%2Freact-router-hono-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frphlmr%2Freact-router-hono-server/lists"}