The Vite plugin you need to create a Hono server for your React Router app in less than 10 seconds.
npm install react-router-hono-servercreateHonoServer that enables you to create a HonoSince the Hono server is built along with the rest of your app, you may import app modules as needed.
It also supports Vite HMR via the react-router-hono-server/dev plugin (which is required
for this to function).
It presets a default Hono server config that you can customize
> [!IMPORTANT]
> Only works with React Router v7 in ESM mode
>
> Only works with Vite
>
> Only Node, Bun, Cloudflare Workers and AWS Lambda (experimental) are supported
> [!TIP]
> šØāš« There is some examples in the examples folder. I hope they will help you.
>
> You can use remix-hono to add cool middleware like session
Install the following npm package.
> [!NOTE]
> This is not a dev dependency, as it creates the Hono server used in production.
``bash
npm install react-router-hono-server hono @hono/node-server
> [!TIP]
> 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:
>
> `
> public-hoist-pattern[]=hono
> `Easy mode
In your vite.config.ts, add the reactRouterHonoServer plugin.`ts
import { reactRouter } from "@react-router/dev/vite";
import { reactRouterHonoServer } from "react-router-hono-server/dev"; // add this
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";export default defineConfig({
plugins: [
reactRouterHonoServer(), // add this
reactRouter(),
tsconfigPaths()
],
});
`
That's all!Wait, what?
For really simple apps, that's all you need to do. Behind the hood, it will create a virtual module with a default Hono server.
When 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).Configuration
Ok, by default it works, but you may want to customize the server and use some middleware.> [!IMPORTANT]
> Until you define your own
serverEntryPoint, the file name ${appDirectory}/server.ts and the folder name ${appDirectory}/server are reserved words.
>
> reactRouterHonoServer plugin is looking for them to find your server file.
$3
> [!NOTE]
> It uses the reactRouter plugin to build your app and will automatically load its config.#### Node
> [!TIP]
> Check this example to see how to use it.
`ts
// vite.config.ts
import { reactRouter } from "@react-router/dev/vite";
import { reactRouterHonoServer } from "react-router-hono-server/dev"; // add this
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";export default defineConfig({
plugins: [
reactRouterHonoServer(), // add this
reactRouter(),
tsconfigPaths()
],
});
`#### Bun
> [!TIP]
> Check this example to see how to use it.
> [!IMPORTANT]
> React 19 and
bun --bun flag should work with the same entry.server.tsx file as Node. Check Using bun --bun flag?
> Check this example to see how to use it.`ts
// vite.config.ts
import { reactRouter } from "@react-router/dev/vite";
import { reactRouterHonoServer } from "react-router-hono-server/dev"; // add this
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";export default defineConfig({
plugins: [
reactRouterHonoServer({ runtime: "bun"} ), // add this
reactRouter(),
tsconfigPaths()
],
});
`##### Using
bun --bun flag?
> [!TIP]
> Check this example to see how to use it.bun --bun flag, you need to have the same entry.server.tsx file as Node.When building for production, at least for React 19, it uses a special
react-dom/server.bun import with only the renderToReadableStream function available.React Router Hono Server vite plugin will alias that to
react-dom/server.node
`ts
import { PassThrough } from "node:stream";import type { AppLoadContext, EntryContext } from "react-router";
import { createReadableStreamFromReadable } from "@react-router/node";
import { ServerRouter } from "react-router";
import { isbot } from "isbot";
import type { RenderToPipeableStreamOptions } from "react-dom/server";
import { renderToPipeableStream } from "react-dom/server";
export const streamTimeout = 5_000;
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
routerContext: EntryContext,
loadContext: AppLoadContext,
// If you have middleware enabled:
// loadContext: unstable_RouterContextProvider
) {
return new Promise((resolve, reject) => {
let shellRendered = false;
let userAgent = request.headers.get("user-agent");
// Ensure requests from bots and SPA Mode renders wait for all content to load before responding
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
let readyOption: keyof RenderToPipeableStreamOptions =
(userAgent && isbot(userAgent)) || routerContext.isSpaMode
? "onAllReady"
: "onShellReady";
const { pipe, abort } = renderToPipeableStream(
,
{
[readyOption]() {
shellRendered = true;
const body = new PassThrough();
const stream = createReadableStreamFromReadable(body);
responseHeaders.set("Content-Type", "text/html");
resolve(
new Response(stream, {
headers: responseHeaders,
status: responseStatusCode,
}),
);
pipe(body);
},
onShellError(error: unknown) {
reject(error);
},
onError(error: unknown) {
responseStatusCode = 500;
// Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest.
if (shellRendered) {
console.error(error);
}
},
},
);
// Abort the rendering stream after the
streamTimeout so it has time to
// flush down the rejected boundaries
setTimeout(abort, streamTimeout + 1000);
});
}
`#### Cloudflare Workers
> [!TIP]
> Check this example to see how to use it.
> [!IMPORTANT]
> You need to add the
cloudflareDevProxy plugin to use the Cloudflare Workers runtime on dev.
`ts
// vite.config.ts
import { reactRouter } from "@react-router/dev/vite";
import { cloudflareDevProxy } from "@react-router/dev/vite/cloudflare"; // add this
import { reactRouterHonoServer } from "react-router-hono-server/dev"; // add this
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";export default defineConfig({
plugins: [
cloudflareDevProxy(),
reactRouterHonoServer({ runtime: "cloudflare"} ), // add this
reactRouter(),
tsconfigPaths()
],
});
`##### React 19
> [!IMPORTANT]
> Check this example to see how to use it.
> [!TIP]
> You may need to add the
force_react_19 flag to the reactRouterHonoServer plugin config.`ts
// vite.config.ts
reactRouterHonoServer({ runtime: "cloudflare", flag: { force_react_19: true } }), // add this
`#### AWS Lambda
> [!TIP]
> AWS shares the same runtime as Node.
`ts
// vite.config.ts
import { reactRouter } from "@react-router/dev/vite";
import { reactRouterHonoServer } from "react-router-hono-server/dev"; // add this
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";export default defineConfig({
plugins: [
reactRouterHonoServer({ runtime: "aws", dev: { export: "handler" } }), // add this
reactRouter(),
tsconfigPaths()
],
});
`$3
> [!TIP]
> You can use the CLI to create the server file for you.
>
> `bash
> npx react-router-hono-server reveal file
> `In your
app folder, create a file named server.ts and export as default the server created by createHonoServer.`bash
touch app/server.ts
``ts
// app/server.ts
import { createHonoServer } from "react-router-hono-server/node";export default createHonoServer({/ options /});
`#### Alternative
You can define your server in
app/server/index.ts.> [!TIP]
> You can use the CLI to create the server file for you.
>
>
`bash
> npx react-router-hono-server reveal folder
> `It is useful if you have many middleware and want to keep your server file clean.
`ts
// app/server/index.tsimport { createHonoServer } from "react-router-hono-server/node";
export default createHonoServer({/ options /});
`#### I don't like this default
No problem, you can define your files wherever you want.
Use the
serverEntryPoint option of the Vite plugin reactRouterHonoServer to point to your server file.
$3
$3
It is not an error, you can keep the React Router defaults for build and dev!
`json
"scripts": {
"build": "react-router build",
"dev": "react-router dev",
"start": "node ./build/server/index.js",
},
`
$3
It is not an error, you can keep the React Router defaults for build!
`json
"scripts": {
"build": "react-router build",
"dev": "bunx --bun vite",
"start": "bun ./build/server/index.js",
},
`$3
It is not an error, you can keep the React Router defaults for build and dev!
`json
"scripts": {
"build": "react-router build",
"dev": "react-router dev",
"start": "wrangler dev",
},
`#### Wrangler
Add a file named
wrangler.toml at the root of your project (close to package.json).Adapt the
main and assets fields based on your build output, if you changed them from the defaults.
`toml
workers_dev = true
name = "my-worker"
compatibility_date = "2024-11-18"
main = "./build/server/index.js"
assets = { directory = "./build/client/" }
`##### Custom assets serving
You can set Cloudflare
experimental_serve_directly and delegate assets serving to Hono, like for Node and Bun.> [!TIP]
> Check https://developers.cloudflare.com/workers/static-assets/binding/#experimental_serve_directly
> [!TIP]
> Check this example to see how to use it.
`toml
[assets]
directory = "./build/client/"
binding = "ASSETS"
experimental_serve_directly = false
`$3
It is not an error, you can keep the React Router defaults for build and dev!
`json
"scripts": {
"build": "react-router build",
"dev": "react-router dev",
"start": "I don't know how to start it in production mode š
",
},
`How it works
This helper works differently depending on the environment.
In development, it uses @hono/vite-dev-server and loads your server and React Router app with
import('virtual:react-router/server-build').
It can be configured in vite.config.ts.In
production, it will create a standard node HTTP server listening at HOST:PORT.
You can customize the production server port using the port option of createHonoServer.When 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.To run the server in production, use
node ./build/server/index.js.That's all!
$3
####
reactRouterHonoServer (Vite Plugin)
`ts
type Runtime = "node" | "bun" | "cloudflare" | "aws";type ReactRouterHonoServerPluginOptions = {
/**
* The runtime to use for the server.
*
* Defaults to
node.
*/
runtime?: Runtime;
/**
* The path to the server file, relative to vite.config.ts.
*
* If it is a folder (app/server), it will look for an index.ts file.
*
* Defaults to ${appDirectory}/server[.ts | /index.ts] if present.
*
* Fallback to a virtual module virtual:react-router-hono-server/server.
*/
serverEntryPoint?: string;
/**
* The paths that are not served by the dev-server.
*
* Defaults include appDirectory content.
*/
dev?: {
/**
* The paths that are not served by the dev-server.
*
* Defaults include appDirectory content.
*/
exclude?: (string | RegExp)[];
/**
* The name of the export to use for the server.
*
* Defaults to default.
*/
export?: string;
};
};
`####
createHonoServer
##### All adapters
`ts
export type HonoServerOptions = {
/**
* The base Hono app to use
*
* It will be used to mount the React Router server on the basename path
* defined in the React Router config
*
* {@link Hono}
*/
app?: Hono;
/**
* Enable the default logger
*
* Defaults to true
*/
defaultLogger?: boolean;
/**
* The port to start the server on
*
* Defaults to process.env.PORT || 3000
*/
port?: number;
/**
* Customize the Hono server, for example, adding middleware
*
* It is applied after the default middleware and before the React Router middleware
*/
configure?: (server: Hono) => Promise | void;
/**
* Augment the React Router AppLoadContext
*
* Don't forget to declare the AppLoadContext in your app, next to where you create the Hono server
*
* `ts
* declare module "react-router" {
* interface AppLoadContext {
* // Add your custom context here
* whatever: string;
* }
* }
* `
*
* To make the typing works correctly, in your react-router.config.ts, add future v8_middleware flag to true.
*
* `ts
* import { defineConfig } from "@react-router/dev";
*
* export default defineConfig({
* future: {
* v8_middleware: true,
* },
* // other config options...
* });
* `
*/
getLoadContext?: (
c: Context,
options: {
build: ServerBuild;
mode: "development" | "production" | "test";
}
) => Promise | AppLoadContext;
/**
* Hook to add middleware that runs before any built-in middleware, including assets serving.
*
* You can use it to add protection middleware, for example.
*/
beforeAll?: (app: Hono) => Promise | void;
};
`You can add additional Hono middleware with the
configure function. If you do not provide a function, it will create a default Hono server.If you want to set up the React Router
AppLoadContext, pass in a function to getLoadContext.Modify the
AppLoadContext interface used in your app.Since 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.
###### Example
`ts
// app/server.tsimport { createHonoServer } from "react-router-hono-server/node";
/**
* Declare our loaders and actions context type
*/
declare module "react-router" {
interface AppLoadContext {
/**
* The app version from the build assets
*/
readonly appVersion: string;
}
}
export default createHonoServer({
getLoadContext(_, { build, mode }) {
const isProductionMode = mode === "production";
return {
appVersion: isProductionMode ? build.assets.version : "dev",
};
},
});
``ts
// app/routes/test.tsx
import type { Route } from "./+types/test";
export async function loader({ context }: Route.LoaderArgs) {
// get the context provided from getLoadContext
return { appVersion: context.appVersion }
}
`> [!TIP]
> If you declare your
getLoadContext function in a separate file, you can use the helper createGetLoadContext from react-router-hono-server/{adapter}
> `ts
> import { createGetLoadContext } from "react-router-hono-server/node";
>
> export const getLoadContext = createGetLoadContext((c, { mode, build }) => {
> const isProductionMode = mode === "production";
> return {
> appVersion: isProductionMode ? build.assets.version : "dev",
> };
> });
> `##### Node
`ts
export interface HonoServerOptions extends HonoServerOptionsBase {
/**
* Listening listener (production mode only)
*
* It is called when the server is listening
*
* Defaults log the port
*/
listeningListener?: (info: AddressInfo) => void;
/**
* Customize the node server (ex: using http2)
*
* {@link https://hono.dev/docs/getting-started/nodejs#http2}
*/
customNodeServer?: CreateNodeServerOptions;
/**
* Callback executed just after serve from @hono/node-server
*
* Only applied to production mode
*
* For example, you can use this to bind @hono/node-ws's injectWebSocket
*/
onServe?: (server: ServerType) => void;
/**
* The Node.js Adapter rewrites the global Request/Response and uses a lightweight Request/Response to improve performance.
*
* If you want this behavior, set it to true
*
* šØ Setting this to true can break request.clone() if you later check instanceof Request.
*
* {@link https://github.com/honojs/node-server?tab=readme-ov-file#overrideglobalobjects}
*
* @default false
*/
overrideGlobalObjects?: boolean;
/**
* Customize the hostname of the node server
*/
hostname?: string;
/**
* Customize the serve static options
*/
serveStaticOptions?: {
/**
* Customize the public assets (what's in your public directory) serve static options.
*
*/
publicAssets?: Omit, "root">;
/**
* Customize the client assets (what's in your build/client/assets directory - React Router) serve static options.
*
*/
clientAssets?: Omit, "root">;
};
}
`##### Bun
`ts
export interface HonoServerOptions extends HonoServerOptionsBase {
/**
* Customize the bun server
*
* {@link https://bun.sh/docs/api/http#start-a-server-bun-serve}
*/
customBunServer?: Serve.Options;
/**
* Customize the serve static options
*/
serveStaticOptions?: {
/**
* Customize the public assets (what's in your public directory) serve static options.
*
*/
publicAssets?: Omit, "root">;
/**
* Customize the client assets (what's in your build/client/assets directory - React Router) serve static options.
*
*/
clientAssets?: Omit, "root">;
};
}
`##### Cloudflare Workers
`ts
export interface HonoServerOptions extends Omit, "port"> {}
`##### AWS Lambda
`ts
type InvokeMode = "default" | "stream";interface HonoAWSServerOptions extends Omit, "port"> {
/**
* The invoke mode to use
*
* Defaults to
default
*
* {@link https://hono.dev/docs/getting-started/aws-lambda#lambda-response-streaming}
*/
invokeMode: InvokeMode;
}
`Middleware
šØ Redirecting from a middleware
> [!IMPORTANT]
> 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.
>
> redirect will use c.redirect for "normal" requests and a single-fetch-like response for React Router .data requests.
>
> 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.
>
> 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.
>
> It returns a single-fetch-like response.
>
> If you use c.redirect, it will not work as expected and you will get a Unable to decode turbo-stream response error.
>
>`ts
> import { redirect } from "react-router-hono-server/http";
>`
>
> I'm sorry for this inconvenience, I hope it can be "fixed" in future React Router versions.Middleware are functions that are called before React Router calls your loader/action.
Hono is the perfect tool for this, as it supports middleware out of the box.
See the Hono docs for more information.
You can imagine many use cases for middleware, such as authentication, protecting routes, caching, logging, etc.
See how Shelf.nu uses them!
> [!TIP]
> This lib exports one middleware
cache (react-router-hono-server/middleware) that you can use to cache your responses.$3
You can use the beforeAll option to add middleware that runs before any built-in middleware, including assets serving.You can use it to add protection middleware, for example.
> [!TIP]
> When you check the path to protect, don't forget to use
c.req.path.includes("") to handle .data requests (loader)!`ts
import { reactRouterRedirect } from "react-router-hono-server/http";
import { createHonoServer } from "react-router-hono-server/node";export default createHonoServer({
beforeAll(app) {
app.use(async (c, next) => {
if (c.req.path.includes("/protected") && !c.req.header("Authorization")) {
return reactRouterRedirect("/login");
}
return next();
});
},
});
`$3
It is easy to use remix-hono middleware with this package.
`ts
import { createCookieSessionStorage } from "react-router";
import { createHonoServer } from "react-router-hono-server/node";
import { session } from "remix-hono/session";export default createHonoServer({
configure: (server) => {
server.use(
session({
autoCommit: true,
createSessionStorage() {
const sessionStorage = createCookieSessionStorage({
cookie: {
name: "session",
httpOnly: true,
path: "/",
sameSite: "lax",
secrets: [process.env.SESSION_SECRET],
secure: process.env.NODE_ENV === "production",
},
});
return {
...sessionStorage,
// If a user doesn't come back to the app within 30 days, their session will be deleted.
async commitSession(session) {
return sessionStorage.commitSession(session, {
maxAge: 60 60 24 * 30, // 30 days
});
},
};
},
})
);
},
});
`$3
createMiddleware or createFactory functions from hono/factory.Then, use them with the
configure function of createHonoServer.`ts
import { createMiddleware } from "hono/factory";
import { createHonoServer } from "react-router-hono-server/node";export default createHonoServer({
configure: (server) => {
server.use(
createMiddleware(async (c, next) => {
console.log("middleware");
return next();
})
);
},
});
`$3
You can use React Router middleware with this package.> [!TIP]
> Check this example to see how to use it.
> [!IMPORTANT]
> To make the typing works correctly, in your
react-router.config.ts or where you want, add future v8_middleware flag type to true.
>
> `ts
> declare module "react-router" {
> interface Future {
> v8_middleware: true; // š Enable middleware types
> }
> }
> `
>
> If you already have a custom getLoadContext function, you now have to return an instance of RouterContextProvider from it. Check this example for more information.
>
> `ts
> import { RouterContextProvider, createContext } from "react-router";
> import { createHonoServer } from "react-router-hono-server/node";
>
> console.log("loading server");
>
> type GlobalAppContext = {
> appVersion: string;
> };
>
> export const globalAppContext = createContext();
>
> export default await createHonoServer({
> getLoadContext(_c, { mode, build }) {
> const isProductionMode = mode === "production";
> const context = new RouterContextProvider();
>
> context.set(globalAppContext, { appVersion: isProductionMode ? build.assets.version : "dev" });
>
> return context;
> },
> });
> `Then, you can use the
use function from react-router to access the context in your loaders and actions.`ts
$3
#### Node
This package has a built-in helper to use @hono/node-ws> [!TIP]
> Check this example to see how to use it.
`ts
import type { WSContext } from "hono/ws";
import { createHonoServer } from "react-router-hono-server/node";// Store connected clients
const clients = new Set();
export default createHonoServer({
useWebSocket: true,
// š Unlock this š from @hono/node-ws
configure: (app, { upgradeWebSocket }) => {
app.get(
"/ws",
upgradeWebSocket((c) => ({
// https://hono.dev/helpers/websocket
onOpen(_, ws) {
console.log("New connection ā¬ļø");
clients.add(ws);
},
onMessage(event, ws) {
console.log("Context", c.req.header("Cookie"));
console.log("Event", event);
console.log(
Message from client: ${event.data});
// Broadcast to all clients except sender
clients.forEach((client) => {
if (client.readyState === 1) {
client.send(${event.data});
}
});
},
onClose(_, ws) {
console.log("Connection closed");
clients.delete(ws);
},
}))
);
},
});
`#### Bun
This package has a built-in helper to use
hono/bun in prod and @hono/node-ws in dev> [!TIP]
> Check this example to see how to use it.
`ts
import { WSContext } from "hono/ws";
import { createHonoServer } from "react-router-hono-server/bun";// Store connected clients
const clients = new Set();
export default createHonoServer({
useWebSocket: true,
// š Unlock this š from @hono/node-ws in dev, hono/bun in prod
configure(app, { upgradeWebSocket }) {
app.get(
"/ws",
upgradeWebSocket((c) => ({
// https://hono.dev/helpers/websocket
onOpen(_, ws) {
console.log("New connection š„");
clients.add(ws);
},
onMessage(event, ws) {
console.log("Context", c.req.header("Cookie"));
console.log("Event", event);
console.log(
Message from client: ${event.data});
// Broadcast to all clients except sender
clients.forEach((client) => {
if (client.readyState === 1) {
client.send(${event.data});
}
});
},
onClose(_, ws) {
console.log("Connection closed");
clients.delete(ws);
},
}))
);
},
});
`#### Cloudflare Workers
Cloudflare requires a different approach to WebSockets, based on Durable Objects.
> [!TIP]
> Check this example to see how to use it.
> [!IMPORTANT]
> For now, HMR is not supported in Cloudflare Workers. Will try to come back to it later.
>
> Work in progress on Cloudflare team: https://github.com/flarelabs-net/vite-plugin-cloudflare
#### AWS
Not supported.
Basename and Hono sub apps
> [!NOTE]
> By default, the React Router app is mounted at
/ (default basename value).
>
> You may not need to use this option. It's for advanced use cases.> [!TIP]
> Check this example to see how to use it.
You can use the
basename option in your React Router config (react-router.config.ts) to mount your React Router app on a subpath.It will automatically mount the app on the subpath.
`ts
// react-router.config.ts
import type { Config } from "@react-router/dev/config";export default {
basename: "/app", // Now the React Router app will be mounted on /app
} satisfies Config;
`Then, 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.`ts
import { Hono } from "hono";
import { createHonoServer } from "react-router-hono-server/node";
import { API_BASENAME, api } from "./api";
import { getLoadContext } from "./context";// Create a root Hono app
const app = new Hono();
// Mount the API app at /api
app.route(API_BASENAME, api);
export default createHonoServer({
// Pass the root Hono app to the server.
// It will be used to mount the React Router app on the
basename defined in react-router.config.ts
app,
getLoadContext,
});
`
> [!NOTE]
> You now have two entry points!
> - /api - for your API
> - /app - for your React Router appPre-rendering
You should be able to use pre-rendering with this package.$3
> [!TIP]
> Check this example to see how to use it.> [!IMPORTANT]
> You need to add the
serverBuildFile option to your react-router.config.ts file.
>
> The file path is fixed to assets/server-build.js.Add the pre-render option to your
react-router.config.ts`ts
import type { Config } from "@react-router/dev/config";export default {
prerender: ["/"],
} satisfies Config;
`$3
> [!TIP]
> Check this example to see how to use it.Add the pre-render option to your
react-router.config.ts`ts
import type { Config } from "@react-router/dev/config";export default {
prerender: ["/"],
} satisfies Config;
`
Migrate from v1
_You should not expect any breaking changes._$3
`bash
npm install react-router-hono-server@latest
`$3
#### Option 1 - You previously had all your server code in app/entry.server.tsx
`bash
touch app/server.ts
`
or
`bash
npx react-router-hono-server reveal file
`#### Option 2 - You previously had your server code in a
server folder
`bash
mkdir app/server
touch app/server/index.ts
`
or
`bash
npx react-router-hono-server reveal folder
`$3
Move your previous server code to the new file you created in the previous step.> [!NOTE]
> You can remove the import from
react-router-hono-server/node in your entry.server.tsx file and any other server code.Many options are gone,
serverBuildFile assetsDir and buildDirectory.We 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.> [!IMPORTANT]
> You now need to export the server created by
createHonoServer as default.
>
> `ts
> import { createHonoServer } from "react-router-hono-server/node";
>
> export default createHonoServer({/ other options /});
> `$3
> [!IMPORTANT]
>
devServer is now reactRouterHonoServer.Many options are gone or have changed.
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.
##### You used
buildEnd from remix() plugin or a custom buildDirectory option
You may know that it has been moved to react-router.config.ts (see here for more information).If you used this hook for Sentry, check this example to see how to migrate.
If you used a custom
buildDirectory option, check this example to see how to migrate.$3
`json
"scripts": {
"build": "react-router build",
"dev": "react-router dev",
"start": "node ./build/server/index.js",
},
`Special Thanks
Inspired by remix-express-vite-plugin from @kiliman
remix` handler was forked from remix-hono by @sergiodxa as it is a small and simple core dependency of this library.I will still help maintain it.
This project follows the all-contributors specification. Contributions of any kind welcome!