Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a346ccc
refactor: move mini-oxygen dev runtime to Vite 8 environments
frandiox Mar 21, 2026
9d09cd0
refactor: delegate mini-oxygen runner invokes to Vite
frandiox Mar 21, 2026
640413e
Simplify Vite 8 cleanup around MiniOxygen
frandiox Mar 21, 2026
6d2b93b
Fix Vite 8 CLI typecheck fallout
frandiox Mar 21, 2026
d03b9d0
Remove unused MiniOxygen Vite root binding
frandiox Mar 22, 2026
ed01667
fix: harden mini-oxygen environment lifecycle
frandiox Mar 22, 2026
bc22c8e
Fix CLI tests for Vite 8 compatibility
frandiox Mar 24, 2026
9872e79
Support Vite 6
frandiox Mar 24, 2026
72b84a9
fix: restore HMR in Vite Environment API setup
frandiox Mar 24, 2026
2540800
refactor: simplify mini-oxygen Vite HMR wiring
frandiox Mar 25, 2026
0d81997
refactor: trim mini-oxygen environment plumbing
frandiox Mar 25, 2026
82a9261
docs: clarify mini-oxygen Vite runtime behavior
frandiox Mar 25, 2026
9947984
docs: clarify mini-oxygen environment lifecycle
frandiox Mar 25, 2026
f13fe7d
fix: use accurate type cast for response body
fredericoo Mar 27, 2026
bd048f0
fix: extract magic number to WARMUP_SETTLE_DELAY_MS
fredericoo Mar 27, 2026
7af923d
chore: add TODO for Vite 6 getBuiltins shim removal
fredericoo Mar 27, 2026
e8adc4e
chore: changeset for mini-oxygen review feedback
fredericoo Mar 30, 2026
c7477e6
fix: correct vite compat range in changeset
fredericoo Mar 31, 2026
d715d11
fix: bump react-router from 7.12.0 to 7.14.0
fredericoo Apr 8, 2026
5844d82
merge: resolve conflicts with main (2026.1.4 release)
fredericoo Apr 9, 2026
aad2855
Merge branch 'main' into fd-vite-8
andguy95 Apr 9, 2026
204113b
fix: package.json merge conflicts
andguy95 Apr 9, 2026
42b7b77
Merge remote-tracking branch 'origin/main' into fd-vite-8
fredericoo Apr 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/warm-planets-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@shopify/mini-oxygen': minor
'@shopify/hydrogen': minor
'@shopify/cli-hydrogen': minor
'@shopify/hydrogen-react': minor
'skeleton': patch
'@shopify/create-hydrogen': patch
---

Add support for Vite 7 and Vite 8. Hydrogen remains backwards-compatible with Vite 5+, while Mini Oxygen, the Hydrogen CLI, and Hydrogen React require Vite 6 or newer.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Do we have any CI tests that prove this, and would catch a regression? Should we, if we're claiming this?


Mini Oxygen's dev server has been refactored to use the [Vite Environment API](https://vite.dev/guide/api-environment), which is the standard way to run non-browser runtimes in Vite. This replaces the previous custom middleware approach with a first-class `FetchableDevEnvironment`, improving compatibility with Vite's built-in HMR and module invalidation.

New Hydrogen projects created with `npm create @shopify/hydrogen` will default to Vite 8. The `vite-tsconfig-paths` plugin is no longer needed in the skeleton template since Vite 8 supports `resolve.tsconfigPaths` natively.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

As mentioned in OP, perhaps it would be good to sync with the RR7 folks and see if they add official support for Vite 8 as well. Mostly to remove warnings from the terminal, otherwise it already works.

15 changes: 7 additions & 8 deletions docs/preview/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,27 @@
"start": "react-router-serve build/server/index.js"
},
"dependencies": {
"@react-router/node": "^7.13.0",
"@react-router/serve": "^7.13.0",
"react-router": "^7.13.0",
"react-router-dom": "^7.13.0",
"@react-router/node": "^7.14.0",
"@react-router/serve": "^7.14.0",
"he": "^1.2.0",
"isbot": "^5.1.21",
"marked": "^9.1.0",
"react": "catalog:",
"react-dom": "catalog:",
"react-router": "^7.14.0",
"react-router-dom": "^7.14.0",
"react-syntax-highlighter": "^15.5.0"
},
"devDependencies": {
"@react-router/dev": "^7.13.0",
"@react-router/fs-routes": "^7.13.0",
"@react-router/dev": "^7.14.0",
"@react-router/fs-routes": "^7.14.0",
"@tailwindcss/vite": "^4.0.14",
"@types/he": "^1.2.1",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@types/react-syntax-highlighter": "^15.5.7",
"typescript": "^5.2.2",
"vite": "^6.4.2",
"vite-tsconfig-paths": "^4.3.1"
"vite": "^8.0.1"
},
"engines": {
"node": "^22 || ^24"
Expand Down
5 changes: 3 additions & 2 deletions docs/preview/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import path from 'node:path';
import {defineConfig} from 'vite';
import {reactRouter} from '@react-router/dev/vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import tailwindcss from '@tailwindcss/vite';

const {INIT_CWD, GEN_DOCS_PATH} = process.env;
Expand All @@ -16,9 +15,11 @@ if (!GEN_DOCS_PATH && INIT_CWD === process.env.PWD) {
}

export default defineConfig({
resolve: {
tsconfigPaths: true,
},
plugins: [
reactRouter(),
tsconfigPaths(),
tailwindcss(),
{
name: 'docs:preview',
Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"react-intersection-observer": "^8.32.0",
"react-router": "7.12.0",
"react-router-dom": "7.12.0",
"react-router": "7.14.0",
"react-router-dom": "7.14.0",
"uuid": "^11.1.0"
},
"devDependencies": {
Expand All @@ -77,8 +77,8 @@
"@eslint/js": "9.19.0",
"@playwright/test": "^1.57.0",
"@qwik.dev/partytown": "^0.11.2",
"@react-router/dev": "7.12.0",
"@react-router/fs-routes": "7.12.0",
"@react-router/dev": "7.14.0",
"@react-router/fs-routes": "7.14.0",
"@shopify/cli": "3.91.1",
"@total-typescript/ts-reset": "^0.6.1",
"@types/eslint": "9.6.1",
Expand Down
3 changes: 1 addition & 2 deletions packages/cli/assets/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"isbot": "^5.1.21"
},
"devDependencies": {
"vite": "^6.2.0",
"vite-tsconfig-paths": "^4.3.1"
"vite": "^8.0.1"
}
}
5 changes: 3 additions & 2 deletions packages/cli/assets/vite/vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import {defineConfig} from 'vite';
import {hydrogen} from '@shopify/hydrogen/vite';
import {oxygen} from '@shopify/mini-oxygen/vite';
import {vitePlugin as remix} from '@remix-run/dev';
import tsconfigPaths from 'vite-tsconfig-paths';

export default defineConfig({
resolve: {
tsconfigPaths: true,
},
plugins: [
hydrogen(),
oxygen(),
Expand All @@ -17,7 +19,6 @@ export default defineConfig({
v3_routeConfig: false,
},
}),
tsconfigPaths(),
],
build: {
// Allow a strict Content-Security-Policy
Expand Down
14 changes: 7 additions & 7 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@
"test:watch": "cross-env SHOPIFY_UNIT_TEST=1 LANG=en-US.UTF-8 vitest --test-timeout=60000"
},
"devDependencies": {
"@react-router/dev": "7.12.0",
"@react-router/dev": "7.14.0",
"@types/diff": "^5.0.2",
"@types/gunzip-maybe": "^1.4.0",
"@types/node": "catalog:",
"esbuild": "^0.25.0",
"@types/prettier": "^2.7.2",
"@types/source-map-support": "^0.5.10",
"@types/tar-fs": "^2.0.1",
"@vitest/coverage-v8": "^1.0.4",
"devtools-protocol": "^0.0.1177611",
"esbuild": "^0.27.0",
"fast-glob": "^3.2.12",
"flame-chart-js": "2.3.2",
"get-port": "^7.0.0",
"type-fest": "^4.33.0",
"vite": "^6.4.2",
"vitest": "^1.0.4"
"vite": "^8.0.1",
"vitest": "^3.2.4"
},
"dependencies": {
"@ast-grep/napi": "0.34.1",
Expand All @@ -48,7 +48,7 @@
"chokidar": "3.5.3",
"cli-truncate": "^4.0.0",
"diff": "^5.1.0",
"esbuild": "^0.25.12",
"esbuild": "^0.27.0",
"get-east-asian-width": "^1.3.0",
"get-port": "^7.0.0",
"gunzip-maybe": "^1.4.2",
Expand All @@ -63,11 +63,11 @@
},
"peerDependencies": {
"@graphql-codegen/cli": "^5.0.2",
"@react-router/dev": "^7.12.0",
"@react-router/dev": "7.14.0",
"@shopify/hydrogen-codegen": "workspace:*",
"@shopify/mini-oxygen": "workspace:*",
"graphql-config": "^5.0.3",
"vite": "^5.1.0 || ^6.2.0"
"vite": "^5.1.0 || ^6.2.0 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"@graphql-codegen/cli": {
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/hydrogen/build.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ describe('build', () => {

const output = outputMock.output();
expect(output).toMatch(expectedBundlePath);
expect(output).toMatch('building for productio');
expect(output).toMatch('building client environment for production');
expect(output).toMatch('dist/client/assets/root-');
expect(output).toMatch('building SSR bundle for productio');
expect(output).toMatch('building ssr environment for production');
expect(
fileExists(joinPath(tmpDir, expectedBundlePath)),
).resolves.toBeTruthy();
Expand Down
1 change: 0 additions & 1 deletion packages/cli/src/commands/hydrogen/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,6 @@ export async function runDev({
);
}

const h2PluginOptions = h2Plugin.api?.getPluginOptions?.();
const remixConfig = (await getViteConfig(root)).remixConfig;

let codegenProcess: ReturnType<typeof spawnCodegenProcess> | undefined;
Expand Down
36 changes: 16 additions & 20 deletions packages/cli/src/lib/bundle/vite-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ type BundleAnalyzerOptions = {
minify?: (code: string, filepath: string) => Promise<string>;
};

type BundleModuleInfo = {
code?: string;
renderedLength?: number;
originalLength?: number;
};

export function hydrogenBundleAnalyzer(pluginOptions?: BundleAnalyzerOptions) {
let config: ResolvedConfig;

Expand Down Expand Up @@ -86,9 +92,13 @@ export function hydrogenBundleAnalyzer(pluginOptions?: BundleAnalyzerOptions) {
const resultError = await Promise.all(
modsToAnalyze.map(async (mod) => {
const relativeModId = relativePath(root, mod.id);
const modBundleInfo = workerFile.modules[mod.id];
const modBundleInfo = workerFile.modules[mod.id] as
| BundleModuleInfo
| undefined;
const originalCodeBytes =
modBundleInfo?.originalLength ?? mod.code?.length ?? 0;
typeof modBundleInfo?.originalLength === 'number'
? modBundleInfo.originalLength
: (mod.code?.length ?? 0);

let resultingCodeBytes = modBundleInfo?.renderedLength ?? 0;

Expand Down Expand Up @@ -185,34 +195,20 @@ export function hydrogenBundleAnalyzer(pluginOptions?: BundleAnalyzerOptions) {
},
};

bundle[BUNDLE_ANALYZER_JSON_FILE] = {
this.emitFile({
type: 'asset',
fileName: BUNDLE_ANALYZER_JSON_FILE,
needsCodeReference: false,
source: JSON.stringify(metafile, null, 2),
names: [BUNDLE_ANALYZER_JSON_FILE],
originalFileNames: [BUNDLE_ANALYZER_JSON_FILE],
// name and originalFileName should be deprecated .. but
// for some reason, removing them breaks typescript check
name: BUNDLE_ANALYZER_JSON_FILE,
originalFileName: BUNDLE_ANALYZER_JSON_FILE,
};
});

bundle[BUNDLE_ANALYZER_HTML_FILE] = {
this.emitFile({
type: 'asset',
fileName: BUNDLE_ANALYZER_HTML_FILE,
needsCodeReference: false,
source: injectAnalyzerTemplateData(
analysisTemplate,
JSON.stringify(metafile),
),
names: [BUNDLE_ANALYZER_HTML_FILE],
originalFileNames: [BUNDLE_ANALYZER_HTML_FILE],
// name and originalFileName should be deprecated .. but
// for some reason, removing them breaks typescript check
name: BUNDLE_ANALYZER_HTML_FILE,
originalFileName: BUNDLE_ANALYZER_HTML_FILE,
};
});
Comment on lines -201 to +211
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same functionality, just fixing some types

},
} satisfies Plugin;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/cpu-profiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {handleMiniOxygenImportFail} from './mini-oxygen/common.js';
import {importLocal} from './import-utils.js';

export async function createCpuStartupProfiler(root: string) {
type MiniOxygenType = typeof import('@shopify/mini-oxygen/node');
type MiniOxygenType = typeof import('~/mini-oxygen/node/index.js');
Copy link
Copy Markdown
Contributor Author

@frandiox frandiox Mar 25, 2026

Choose a reason for hiding this comment

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

This type of change (here and in other files) is just a fix to remove build order requirement for TS types (before we needed mini-oxygen built before CLI, now it doesn't matter).

It can be ignored for review.

const {createMiniOxygen} = await importLocal<MiniOxygenType>(
'@shopify/mini-oxygen/node',
root,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/mini-oxygen/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
import colors from '@shopify/cli-kit/node/colors';
import {DEV_ROUTES} from '../request-events.js';
import {AbortError} from '@shopify/cli-kit/node/error';
import type {RequestHookInfo} from '@shopify/mini-oxygen';
import type {RequestHookInfo} from '~/mini-oxygen/worker/handler.js';

// Default port used for debugging in VSCode and Chrome DevTools.
export const DEFAULT_INSPECTOR_PORT = 9229;
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/mini-oxygen/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export type MiniOxygen = MiniOxygenInstance;
export {DEFAULT_INSPECTOR_PORT} from './common.js';

export async function buildAssetsUrl(port: number, root: string) {
type MiniOxygenType = typeof import('@shopify/mini-oxygen');
type MiniOxygenType = typeof import('~/mini-oxygen/worker/index.js');
const {buildAssetsUrl: _buildAssetsUrl} = await importLocal<MiniOxygenType>(
'@shopify/mini-oxygen',
root,
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/lib/mini-oxygen/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {readFile} from '@shopify/cli-kit/node/fs';
import {renderSuccess} from '@shopify/cli-kit/node/ui';
import colors from '@shopify/cli-kit/node/colors';
import {AbortError} from '@shopify/cli-kit/node/error';
import type {MiniOxygenOptions as InternalMiniOxygenOptions} from '@shopify/mini-oxygen/node';
import type {MiniOxygenOptions as InternalMiniOxygenOptions} from '~/mini-oxygen/node/index.js';
import {DEFAULT_INSPECTOR_PORT} from '../flags.js';
import type {MiniOxygenInstance, MiniOxygenOptions} from './types.js';
import {
Expand Down Expand Up @@ -32,7 +32,7 @@ export async function startNodeServer({
inspectorPort,
root,
}: MiniOxygenOptions): Promise<MiniOxygenInstance> {
type MiniOxygenType = typeof import('@shopify/mini-oxygen/node');
type MiniOxygenType = typeof import('~/mini-oxygen/node/index.js');
const {startServer, Request, Response} = await importLocal<MiniOxygenType>(
'@shopify/mini-oxygen/node',
root,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/lib/mini-oxygen/workerd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export async function startWorkerdServer({
buildPathClient,
env,
}: MiniOxygenOptions): Promise<MiniOxygenInstance> {
type MiniOxygenType = typeof import('@shopify/mini-oxygen');
type MiniOxygenType = typeof import('~/mini-oxygen/worker/index.js');
const {createMiniOxygen, Response} = await importLocal<MiniOxygenType>(
'@shopify/mini-oxygen',
root,
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/lib/request-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import path from 'node:path';
import {EventEmitter} from 'node:events';
import {ReadableStream} from 'node:stream/web';
import {getGraphiQLUrl} from './graphiql-url.js';
import type {Request, Response} from '@shopify/mini-oxygen/node';
import type {Request, Response} from '~/mini-oxygen/node/index.js';
import type {
Request as WorkerdRequest,
Response as WorkerdResponse,
ResponseInit,
} from '@shopify/mini-oxygen';
} from '~/mini-oxygen/worker/index.js';
import {mapSourcePosition} from 'source-map-support';

export const H2O_BINDING_NAME = 'H2O_LOG_EVENT';
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/lib/vite-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ export function findHydrogenPlugin<Config extends MinimalViteConfig>(
) {
return findPlugin<HydrogenPlugin>(config, 'hydrogen:main');
}

export function findOxygenPlugin<Config extends MinimalViteConfig>(
config: Config,
) {
Expand Down
6 changes: 3 additions & 3 deletions packages/hydrogen-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@
"@testing-library/user-event": "^14.6.1",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
"@vitejs/plugin-react": "^4.3.4",
"@vitejs/plugin-react": "^6.0.1",
"@vitest/coverage-v8": "^3.2.4",
"cpy-cli": "^5.0.0",
"eslint": "9.19.0",
Expand All @@ -160,13 +160,13 @@
"rimraf": "^6.0.1",
"ts-expect": "^1.3.0",
"typescript": "5.9.2",
"vite": "^6.4.2",
"vite": "^8.0.1",
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This change in hydrogen-react means we start building it with Vite 8

"vitest": "^3.2.4"
},
"peerDependencies": {
"react": "^18.3.1 || ~19.0.3 || ~19.1.4 || ^19.2.3",
"react-dom": "^18.3.1 || ~19.0.3 || ~19.1.4 || ^19.2.3",
"vite": "^5.1.0 || ^6.2.1"
"vite": "^5.1.0 || ^6.2.1 || ^7.0.0 || ^8.0.0"
},
"dependencies": {
"@google/model-viewer": "^4.0.0",
Expand Down
10 changes: 5 additions & 5 deletions packages/hydrogen/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@
"@shopify/graphql-client": "1.4.1"
},
"devDependencies": {
"react-router": "7.12.0",
"@react-router/dev": "7.12.0",
"react-router": "7.14.0",
"@react-router/dev": "7.14.0",
"@shopify/generate-docs": "0.16.4",
"@shopify/hydrogen-codegen": "workspace:*",
"@testing-library/jest-dom": "^6.6.3",
Expand All @@ -113,10 +113,10 @@
"vitest": "^3.2.4"
},
"peerDependencies": {
"react-router": "^7.12.0",
"@react-router/dev": "^7.12.0",
"react-router": "7.14.0",
"@react-router/dev": "7.14.0",
"react": "^18.3.1 || ~19.0.3 || ~19.1.4 || ^19.2.3",
"vite": "^5.1.0 || ^6.2.1"
"vite": "^5.1.0 || ^6.2.1 || ^7.0.0 || ^8.0.0"
},
"peerDependenciesMeta": {
"vite": {
Expand Down
1 change: 1 addition & 0 deletions packages/hydrogen/src/product/VariantSelector.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function fillLocation(partial: Partial<Location> = {}) {
search: '',
hash: '',
state: null,
unstable_mask: undefined,
...partial,
};
}
Expand Down
Loading
Loading