Versions
- vinext: 0.0.49
- @cloudflare/vite-plugin: 1.37.0
- @vitejs/plugin-rsc: 0.5.26
- vite: 8.0.13
- react / react-dom: 19.2.6
- @next/third-parties: 16.2.6
Symptom
vinext dev returns HTTP 500 with:
[vite] Internal server error: Element type is invalid: expected a string
(for built-in components) or a class/function (for composite components)
but got: undefined.
at renderElement (.../react-dom-server.edge.development...)
at retryNode (...)
at renderNodeDestructive (...)
...
(Some users see but got: object instead of undefined depending on which import is being resolved — same root cause.)
Minimal repro
package.json:
{
"name": "vinext-repro",
"private": true,
"type": "module",
"scripts": { "dev": "vinext dev --port 4030" },
"dependencies": {
"@next/third-parties": "^16.2.3",
"react": "^19.2.0",
"react-dom": "^19.2.0"
},
"devDependencies": {
"@cloudflare/vite-plugin": "^1.36.4",
"@vitejs/plugin-rsc": "^0.5.26",
"vinext": "0.0.49",
"vite": "^8.0.10",
"wrangler": "^4.91.0"
}
}
vite.config.ts:
import { cloudflare } from '@cloudflare/vite-plugin';
import { defineConfig } from 'vite';
import vinext from 'vinext';
export default defineConfig({
plugins: [
vinext(),
cloudflare({
inspectorPort: false,
viteEnvironment: { name: 'rsc', childEnvironments: ['ssr'] },
}),
],
});
wrangler.jsonc:
worker/index.ts:
import handler from 'vinext/server/app-router-entry';
export default {
async fetch(request: Request, env: { ASSETS: Fetcher }, ctx: ExecutionContext) {
return handler.fetch(request, env, ctx);
},
};
app/layout.tsx:
import { GoogleAnalytics } from '@next/third-parties/google';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<GoogleAnalytics gaId="G-FAKEID000" />
{children}
</body>
</html>
);
}
app/page.tsx:
export default function Home() {
return <h1>hi</h1>;
}
Reproduce:
bun install
bun run dev
curl http://localhost:4030/ # → 500
Scope
| Variant |
Result |
<GoogleAnalytics /> from @next/third-parties/google |
500 (this report) |
<Script /> directly from next/script (vinext shim) |
200 ✅ |
<GoogleAnalytics /> with plain vite + vinext (no @cloudflare/vite-plugin) |
200 ✅ |
So the bug only triggers when all three are present:
- The Cloudflare Vite plugin
- vinext's
next/script alias path
- A
use client module that is TypeScript-compiled CJS (the failing file is node_modules/@next/third-parties/dist/google/ga.js)
Diagnosis
@next/third-parties/dist/google/ga.js starts with:
"use strict";
'use client';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GoogleAnalytics = GoogleAnalytics;
// ...
const script_1 = __importDefault(require("next/script"));
// ...
return jsxs(Fragment, { children: [jsx(script_1.default, { ... }), ...] });
This is tsc-compiled output — common for any TS package targeting CJS. The "use strict"; is emitted by tsc before the user-authored 'use client';.
The interaction (best-effort theory; happy to hand off to maintainers):
-
@vitejs/plugin-rsc's cjs-module-runner-transform (node_modules/@vitejs/plugin-rsc/dist/cjs-BdahOUyh.js) matches the file (it's in node_modules and contains require/exports) and rewrites require("next/script") to __cjs_interop__(await import("next/script")). This runs because @cloudflare/vite-plugin enables dev.moduleRunnerTransform in the rsc environment.
-
@vitejs/plugin-rsc's rsc:use-client transform should have intercepted the file first and converted it into a client-reference proxy export. Plain next/script (vinext's ESM shim, where 'use client' is the first line) does get this treatment. The TS-compiled CJS variant of the same pattern apparently does not — the resulting module reaches SSR as a CJS export object whose named exports (GoogleAnalytics) are server-side functions trying to render a next/script import that has been wrapped through __cjs_interop__, returning either the raw namespace (→ "got: object") or undefined for the named/default field (→ "got: undefined").
Whether the root cause is:
- the
rsc:use-client plugin not transforming TS-compiled-CJS variants, or
- the
cjs-module-runner-transform plugin clobbering them before the use-client transform runs, or
- vinext's
next/script alias not registering a client-reference for this import path under the Cloudflare runner,
is best left to maintainers to confirm. The behavioural fact is that the same component import pattern fails for a node_modules CJS package and succeeds for ESM, only when the Cloudflare Vite plugin is active.
Workaround
Replace @next/third-parties usage with hand-written <script async src="..."> tags. (next/script itself works.)
Suggested test cases
- Render any
"use client" directive-prefixed CJS package from node_modules in an App Router server component under vinext + @cloudflare/vite-plugin.
- Compare behaviour with and without
@cloudflare/vite-plugin.
- Compare behaviour for
'use client'-first vs "use strict";\n'use client'; source files.
Versions
Symptom
vinext devreturns HTTP 500 with:(Some users see
but got: objectinstead ofundefineddepending on which import is being resolved — same root cause.)Minimal repro
package.json:{ "name": "vinext-repro", "private": true, "type": "module", "scripts": { "dev": "vinext dev --port 4030" }, "dependencies": { "@next/third-parties": "^16.2.3", "react": "^19.2.0", "react-dom": "^19.2.0" }, "devDependencies": { "@cloudflare/vite-plugin": "^1.36.4", "@vitejs/plugin-rsc": "^0.5.26", "vinext": "0.0.49", "vite": "^8.0.10", "wrangler": "^4.91.0" } }vite.config.ts:wrangler.jsonc:{ "$schema": "node_modules/wrangler/config-schema.json", "name": "vinext-repro", "main": "./worker/index.ts", "compatibility_date": "2026-04-21", "compatibility_flags": ["nodejs_compat"], "assets": { "binding": "ASSETS", "not_found_handling": "none" } }worker/index.ts:app/layout.tsx:app/page.tsx:Reproduce:
bun install bun run dev curl http://localhost:4030/ # → 500Scope
<GoogleAnalytics />from@next/third-parties/google<Script />directly fromnext/script(vinext shim)<GoogleAnalytics />with plainvite + vinext(no@cloudflare/vite-plugin)So the bug only triggers when all three are present:
next/scriptalias pathuse clientmodule that is TypeScript-compiled CJS (the failing file isnode_modules/@next/third-parties/dist/google/ga.js)Diagnosis
@next/third-parties/dist/google/ga.jsstarts with:This is
tsc-compiled output — common for any TS package targeting CJS. The"use strict";is emitted by tsc before the user-authored'use client';.The interaction (best-effort theory; happy to hand off to maintainers):
@vitejs/plugin-rsc'scjs-module-runner-transform(node_modules/@vitejs/plugin-rsc/dist/cjs-BdahOUyh.js) matches the file (it's innode_modulesand containsrequire/exports) and rewritesrequire("next/script")to__cjs_interop__(await import("next/script")). This runs because@cloudflare/vite-pluginenablesdev.moduleRunnerTransformin therscenvironment.@vitejs/plugin-rsc'srsc:use-clienttransform should have intercepted the file first and converted it into a client-reference proxy export. Plainnext/script(vinext's ESM shim, where'use client'is the first line) does get this treatment. The TS-compiled CJS variant of the same pattern apparently does not — the resulting module reaches SSR as a CJS export object whose named exports (GoogleAnalytics) are server-side functions trying to render anext/scriptimport that has been wrapped through__cjs_interop__, returning either the raw namespace (→ "got: object") or undefined for the named/default field (→ "got: undefined").Whether the root cause is:
rsc:use-clientplugin not transforming TS-compiled-CJS variants, orcjs-module-runner-transformplugin clobbering them before the use-client transform runs, ornext/scriptalias not registering a client-reference for this import path under the Cloudflare runner,is best left to maintainers to confirm. The behavioural fact is that the same component import pattern fails for a node_modules CJS package and succeeds for ESM, only when the Cloudflare Vite plugin is active.
Workaround
Replace
@next/third-partiesusage with hand-written<script async src="...">tags. (next/scriptitself works.)Suggested test cases
"use client"directive-prefixed CJS package from node_modules in an App Router server component undervinext + @cloudflare/vite-plugin.@cloudflare/vite-plugin.'use client'-first vs"use strict";\n'use client';source files.