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: Update your Remix adapterStep 1: Update your Remix adapter

Remix, Hydrogen’s underlying framework, provides a range of adapters that allow you to adjust your app configuration and build step to target different deployment runtimes.

  1. In your Hydrogen app, uninstall the Remix adapter for Oxygen:
npm uninstall @shopify/remix-oxygen
  1. Install the Remix adapter for your selected hosting platform. For example:
npm install @remix-run/cloudflare

Anchor to Step 2: Edit app files for your adapterStep 2: Edit app files for your adapter

Typically, when changing Remix adapters, you’ll need to make updates to the following files:

  • server.js
  • remix.config.js
  • app/entry.server.jsx

Remix provides templated examples for a range of adapters. Consult your chosen adapter's template files for specific configurations or required edits.

When making changes, make sure that you continue to pass Hydrogen’s Storefront API client to the Remix loader context. Typically, this involves editing the updated server.js file using the createRequestHandler function provided by your chosen adapter.

The following example shows some changes that your app might require:

File

/server.js

import {createStorefrontClient} from '@shopify/hydrogen';
import {createRequestHandler} from '@remix/ADAPTER_NAME';

export default {
async fetch(request, env, executionContext) {
const {storefront} = createStorefrontClient({
// Required: Storefront API credentials
privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
storefrontApiVersion: env.PUBLIC_STOREFRONT_API_VERSION,
storeDomain: `https://${env.PUBLIC_STORE_DOMAIN}`,
storefrontHeaders: {
// Pass a buyerIp to prevent being flagged as a bot
buyerIp: 'customer_IP_address', // Platform-specific method to get request IP
cookie: request.headers.get('cookie'), // Required for Shopify Analytics
purpose: request.headers.get('purpose'), // Used for debugging purposes
},
i18n: {
country: 'country_code',
language: 'language_code',
},
cache: () => {},
waitUntil: () => {},
// Additional platform-specific configuration...
});
const handleRequest = createRequestHandler({
// Inject the Storefront API client into the Remix context
getLoadContext: () => ({storefront}),
// Additional platform-specific configuration...
});
return handleRequest(request);
},
};
import {createStorefrontClient} from '@shopify/hydrogen';
import {createRequestHandler} from '@remix/ADAPTER_NAME';

export default {
async fetch(request: Request, env: Env, executionContext: ExecutionContext) {
const {storefront} = createStorefrontClient({
// Required: Storefront API credentials
privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
storefrontApiVersion: env.PUBLIC_STOREFRONT_API_VERSION,
storeDomain: `https://${env.PUBLIC_STORE_DOMAIN}`,
storefrontHeaders: {
// Pass a buyerIp to prevent being flagged as a bot
buyerIp: 'customer_IP_address', // Platform-specific method to get request IP
cookie: request.headers.get('cookie'), // Required for Shopify Analytics
purpose: request.headers.get('purpose'), // Used for debugging purposes
},
i18n: {
country: 'country_code',
language: 'language_code',
},
cache: () => {},
waitUntil: () => {},
// Additional platform-specific configuration...
});
const handleRequest = createRequestHandler({
// Inject the Storefront API client into the Remix context
getLoadContext: () => ({storefront}),
// Additional platform-specific configuration...
});
return handleRequest(request);
},
};

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 is used in serverless contexts to keep the request function alive after a response has been sent. Check whether your hosting platform supports this pattern, or one like it. For example, Vercel Edge Functions provide a compatible waitUntil method. This method isn't required when deployed to a stateful Node.js server, because the server keeps running after the response has been returned.

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


Was this page helpful?