<Back to Updates

How to Adopt All Future Flags

May 21, 2025

Future Flag Migration for React Router 7

Before upgrading to React Router 7, you need to make sure to adopt all Remix future flags. The CLI upgrade command (npx shopify hydrogen upgrade) provides guides on how to do this, but this guide consolidates all the steps.

First of all, the legacy classic Remix compiler is no longer supported, and you must upgrade to Vite if you haven't already. After running npx shopify hydrogen upgrade, run npx shopify hydrogen setup vite to migrate to Vite.

Next enable each of these future flags. It's easiest to do one at a time, making sure the app is functional after each step. At the end you should have all future flags enabled within the remix vite plugin:

Code Example


remix({
  presets: [hydrogen.v3preset()],
  future: {
    v3_fetcherPersist: true,
    v3_relativeSplatPath: true,
    v3_throwAbortReason: true,
    v3_lazyRouteDiscovery: true,
    v3_routeConfig: true,
    v3_singleFetch: true,
  },
})
  

v3_fetcherPersist

Enabling this flag is unlikely to affect your app. See the Remix docs for more information.

v3_relativeSplatPath

If you have any routes with a path + a splat like dashboard.$.tsx that have relative links like <Link to="relative"> or <Link to="../relative"> beneath it, you will need to update your code.

See the Remix docs for more information.

v3_throwAbortReason

When a server-side request is aborted, such as when a user navigates away from a page before the loader finishes, Remix will throw the request.signal.reason instead of an error like new Error("query() call aborted..."). Enabling this is unlikely to affect your app.

See the Remix docs for more information.

v3_lazyRouteDiscovery

Unlikely to affect your app. See the Remix docs for more information.

v3_singleFetch

Single Fetch is a new data loading strategy and streaming format. For more detailed upgrade instructions, see the v3_singleFetch PR or refer to the Remix guide.

  1. In your entry.server.tsx, add nonce to the <RemixServer>:

    Code Example

    
    const body = await renderToReadableStream(
      <NonceProvider>
        <RemixServer
          context={remixContext}
          url={request.url}
    +     nonce={nonce}
        />
      </NonceProvider>
    );
  2. Return plain objects from loaders and actions instead of using json or defer:

    Code Example

    
    // Before
    import {json} from "@shopify/remix-oxygen";
    
    export async function loader({}: LoaderFunctionArgs) {
      let tasks = await fetchTasks();
      return json(tasks);
    }
    
    // After
    export async function loader({}: LoaderFunctionArgs) {
      let tasks = await fetchTasks();
      return tasks;
    }
          

    Code Example

    
    // When setting headers
    import {data} from "@shopify/remix-oxygen";
    
    export async function loader({}: LoaderFunctionArgs) {
      let tasks = await fetchTasks();
      return data(tasks, {
        headers: {
          "Cache-Control": "public, max-age=604800"
        }
      });
    }
          

Update your shouldRevalidate in root.tsx:

Code Example


export const shouldRevalidate: ShouldRevalidateFunction = ({
 formMethod,
 currentUrl,
 nextUrl,
}) => {
  if (formMethod && formMethod !== 'GET') return true;
  if (currentUrl.toString() === nextUrl.toString()) return true;

  // Defaulting to no revalidation for root loader data to improve performance.
  // When using this feature, you risk your UI getting out of sync with your server.
  // Use with caution. If you are uncomfortable with this optimization, update the
  // line below to `return defaultShouldRevalidate` instead.
  // For more details see: https://remix.run/docs/en/main/route/should-revalidate
  return false;
};
  

v3_routeConfig

Config-based routing is the new default in React Router v7, configured via the routes.ts file. Support for routes.ts in Remix is a migration path toward React Router v7.

  1. Install the package: npm install -D @remix-run/route-config @remix-run/route-config
  2. Add a routes.ts file:

    Code Example

    
    import {flatRoutes} from '@remix-run/fs-routes';
    import {type RouteConfig} from '@remix-run/route-config';
    import {hydrogenRoutes} from '@shopify/hydrogen';
    
    export default hydrogenRoutes([
      ...(await flatRoutes()),
    ]) satisfies RouteConfig;
          

Update vite.config.js to use hydrogen.v3preset() and enable v3_routeConfig:

Code Example


export default defineConfig({
  plugins: [
    hydrogen(),
    oxygen(),
    remix({
      presets: [hydrogen.v3preset()],
      future: {
        v3_fetcherPersist: true,
        v3_relativeSplatPath: true,
        v3_throwAbortReason: true,
        v3_lazyRouteDiscovery: true,
        v3_singleFetch: true,
        v3_routeConfig: true,
      },
    }),
    tsconfigPaths(),
  ],
});
  

See the Remix docs for more information.

Get building

Spin up a new Hydrogen app in minutes.

See documentation