Skip to content

feat(dev): expose setReactRouterDevLoadContext #13326

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: dev
Choose a base branch
from

Conversation

albertusdev
Copy link

@albertusdev albertusdev commented Mar 29, 2025

Purpose

This PR exposes the setReactRouterDevLoadContext function from the vite plugin, allowing developers to easily set a custom load context for the dev server without requiring a custom server entrypoint.

Problem

Currently, to provide a custom "load context" for server-side loaders and actions during development, developers must either:

  1. Set up a custom server entrypoint with more complex configuration, as shown in Vercel's documentation:
// vite.config.ts
export default defineConfig(({ isSsrBuild }) => ({
  build: {
    rollupOptions: isSsrBuild
      ? {
          input: './server/app.ts',
        }
      : undefined,
  },
  plugins: [reactRouter(), /* other plugins */],
}));

// server/app.ts
export default app.fetch;
  1. Or even goes to the length of implementing a custom server & dev server as seen in https://github.com/remix-run/react-router-templates/tree/main/node-custom-server

Both approaches require additional configuration and setup just to supply environment variables or other data to loaders and actions during development.

Solution

By exposing the existing setReactRouterDevLoadContext function, developers can achieve the same result with a much simpler approach:

// vite.config.ts
import { reactRouter, setReactRouterDevLoadContext } from '@react-router/dev/vite';

// Set a custom load context for the dev server
setReactRouterDevLoadContext((request) => {
  return {
    env: {
      API_KEY: process.env.API_KEY,
      // Other environment variables
    },
    // Other context data
  };
});

export default {
  plugins: [
    reactRouter(),
    // Other plugins
  ],
};

Copy link

changeset-bot bot commented Mar 29, 2025

⚠️ No Changeset found

Latest commit: 17792f0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Mar 29, 2025

Hi @albertusdev,

Welcome, and thank you for contributing to React Router!

Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once.

You may review the CLA and sign it by adding your name to contributors.yml.

Once the CLA is signed, the CLA Signed label will be added to the pull request.

If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run.

Thanks!

- The Remix team

@remix-cla-bot
Copy link
Contributor

remix-cla-bot bot commented Mar 29, 2025

Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳

@albertusdev
Copy link
Author

hi @brophdawg11! sorry for the tag here, not sure which contributors should I ping to get this small PR reviewed. appreciate your help here!

@MichaelDeBoey MichaelDeBoey changed the title feat: expose setReactRouterDevLoadContext function feat(dev): expose setReactRouterDevLoadContext Apr 6, 2025
@MichaelDeBoey MichaelDeBoey requested a review from Copilot April 6, 2025 07:25
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

@brophdawg11 brophdawg11 requested review from pcattori and removed request for brophdawg11 May 1, 2025 20:30
@markdalgleish
Copy link
Member

Thanks for the PR!

One concern I have with the approach in this PR is it assumes the server is running in the same environment as Vite, but with our newly added support for the Vite Environment API, this is no longer guaranteed. For example, when using vite-plugin-cloudflare, your app code is running in workerd and doesn't use the context passed to setReactRouterDevLoadContext. For these consumers, this feature would appear to be broken.

Any API for make setting a load context easier would probably work better a file to be configured in your React Router config, e.g. export default { loadContextFile: "load-context.ts" }, and maybe even provide a default value for this if a new future flag is enabled, this way we can ensure it's executed within the same environment as your app code.

We'd still need to discuss whether this is something we want to support, so could you help us by providing a bit more detail about your use case?

@albertusdev
Copy link
Author

albertusdev commented May 10, 2025

Thanks for the PR!

One concern I have with the approach in this PR is it assumes the server is running in the same environment as Vite, but with our newly added support for the Vite Environment API, this is no longer guaranteed. For example, when using vite-plugin-cloudflare, your app code is running in workerd and doesn't use the context passed to setReactRouterDevLoadContext. For these consumers, this feature would appear to be broken.

Any API for make setting a load context easier would probably work better a file to be configured in your React Router config, e.g. export default { loadContextFile: "load-context.ts" }, and maybe even provide a default value for this if a new future flag is enabled, this way we can ensure it's executed within the same environment as your app code.

We'd still need to discuss whether this is something we want to support, so could you help us by providing a bit more detail about your use case?

@markdalgleish sorry for the late reply! thank you for raising this point.

You are right. My use case here is to make it so easy to make setting a load context much much easier and consistent when running react-router codebase in a local development environment by reusing the @react-router/dev package as the dev server. Looking at the current documentation, it's not obvious how to setup this and it seems like people need to run a custom Hono/Node.js server in their local development just to have a custom loadContext function, which is not ideal IMHO.

When I raised this PR a month ago or so Cloudflare hasn't released their Vite plugin yet in GA, and I was trying to deploy a react-router project to Vercel with custom load context, and it was not the easiest thing to do 😅 Like I mentioned in the PR description, I think there were samples on the Vercel examples that was working to make the load context function works fine in a deployed Vercel environment by modifying the rollupOptions to a custom server, but for some reason I wasn't able to make it work on a local development environment, hence why I raised this PR.

Since then, I gave up on deploying it to Vercel and because I saw that Cloudflare just released the official Vite plugin + support for react-router, I decided to switch to Cloudflare Workers instead.

Thanks to Wrangler + Miniflare which provides a consistent local development environment, this was no longer an issue for me when I knew that we could modify the workers/app.ts to something like this:

import type { ExportedHandler, ExportedHandlerFetchHandler } from '@cloudflare/workers-types'
import { createRequestHandler } from 'react-router'

import { getLoadContext } from '../load-context'

type Env = Record<string, string>

const onRequest = createRequestHandler(
  () => import('virtual:react-router/server-build'),
  import.meta.env.MODE
)

export default {
  async fetch(request, env, ctx) {
    return onRequest(request as unknown as Request, {
      cloudflare: { env, ctx },
      ...getLoadContext({
        request: request as unknown as Request,
        context: { cloudflare: { env, ctx } },
      }),
    }) as unknown as ReturnType<ExportedHandlerFetchHandler<Env>>
  },
} satisfies ExportedHandler<Env>

This setup works like a charm for my usecase, albeit the documentation was not obvious and it took me some time to figure this out.

+I really like your suggestion to probably expose a loadContext config as part of the react-router config to make this much easier. I do believe that it's currently very painful for codebase that's not using Cloudflare/Wrangler but wants to have a custom loadContext function that also works on their local development environment without needing to customize the dev server.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants