Skip to main content

Self-hosting Hydrogen

Note

This guide might not be compatible with features introduced in Hydrogen version 2025-05 and above. Check the latest documentation if you encounter any issues.

If you don’t want to deploy to Oxygen, then you can host Hydrogen on other JavaScript runtimes, such as Vercel, Netlify, Fly.io, and Cloudflare Workers.



Anchor to Steps to update your Hydrogen appSteps to update your Hydrogen app

Note

Your hosting platform might have other requirements or implementation details not covered here.

Anchor to Step 1: Choose a React Router deployment targetStep 1: Choose a React Router deployment target

Hydrogen projects are React Router apps. To self-host a Hydrogen app, configure the project for the runtime or platform that serves your app. Start with React Router's deployment guide and deployment templates.

For platform-specific setup, refer to your host's React Router guide, such as:

  1. Remove Oxygen-specific packages and code that your new target doesn't use. If your server entry no longer imports @shopify/remix-oxygen, then uninstall it:
npm uninstall @shopify/remix-oxygen
  1. In vite.config.ts, remove the Oxygen Vite plugin from the plugins array, such as oxygen(), but keep Hydrogen's Vite plugin, hydrogen().
  2. Install the packages that your selected host requires, such as a React Router preset, runtime adapter, server package, or environment type package. Follow your host's React Router guide for exact commands and configuration.

Anchor to Step 2: Edit app files for your target runtimeStep 2: Edit app files for your target runtime

When you move from Oxygen to another runtime, update the files that define your server runtime and build target:

  • server.js, server.ts, or another server entry file.
  • react-router.config.ts.
  • vite.config.ts.
  • app/entry.server.jsx or app/entry.server.tsx when your runtime needs a custom server entry.

React Router provides templates for different deployment targets. Consult your chosen target's template files for specific configuration and build steps. For non-Node.js runtimes, you might also need a custom app/entry.server.tsx file.

Anchor to Step 3: Keep Hydrogen context in React RouterStep 3: Keep Hydrogen context in React Router

Hydrogen utilities need the Storefront API client, session, cache, and other runtime services in React Router's load context. When you replace the Oxygen runtime, make sure that your new server entry creates a Hydrogen context and passes it through getLoadContext.

The following example shows the Hydrogen-specific part of a Fetch API server entry. It uses AppSession as a placeholder for the session implementation that your runtime uses.

File

/server.js

import {createHydrogenContext, createRequestHandler} from '@shopify/hydrogen';
import * as reactRouterBuild from 'virtual:react-router/server-build';

export default {
async fetch(request, env, executionContext) {
const session = await AppSession.init(request, [env.SESSION_SECRET]);
const hydrogenContext = createHydrogenContext({
env,
request,
cache: await caches.open('hydrogen'),
waitUntil: executionContext.waitUntil.bind(executionContext),
session,
});

const handleRequest = createRequestHandler({
build: reactRouterBuild,
mode: process.env.NODE_ENV,
getLoadContext: () => hydrogenContext,
});

const response = await handleRequest(request);

if (session.isPending) {
response.headers.set('Set-Cookie', await session.commit());
}

return response;
},
};
import {createHydrogenContext, createRequestHandler} from '@shopify/hydrogen';
import * as reactRouterBuild from 'virtual:react-router/server-build';

export default {
async fetch(request: Request, env: Env, executionContext: ExecutionContext) {
const session = await AppSession.init(request, [env.SESSION_SECRET]);
const hydrogenContext = createHydrogenContext({
env,
request,
cache: await caches.open('hydrogen'),
waitUntil: executionContext.waitUntil.bind(executionContext),
session,
});

const handleRequest = createRequestHandler({
build: reactRouterBuild,
mode: process.env.NODE_ENV,
getLoadContext: () => hydrogenContext,
});

const response = await handleRequest(request);

if (session.isPending) {
response.headers.set('Set-Cookie', await session.commit());
}

return response;
},
};

The cache object should implement the standard Web Cache API. Check with your chosen hosting platform for more details about its caching implementation.

The waitUntil method keeps serverless request work running after the handler sends a response. Check whether your hosting platform supports this pattern, or one like it. For example, Vercel Functions provide a compatible waitUntil method. You don't need this method when you deploy to a stateful Node.js server, because the server keeps running after the handler returns a response.

Consult the createHydrogenContext API reference and createRequestHandler API reference for the complete list of required and optional parameters.


Was this page helpful?