BuilderIO / BuilderIO/builder

npm init builder.io@latest scaffold is broken on Next.js 16: stale Turbopack detection injects a webpack-only DevTools config

Open
#4,763 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
8.8k
Forks
1.2k
Avg merge
1d 6h
Merged PRs (30d)
17

Description

> **Tracker note:** The fix for this does **not** live in `BuilderIO/builder`. It belongs to the
> separately-published `create-builder.io` and `@builder.io/dev-tools` packages, neither of which
> declares a `repository` or `bugs` URL on npm (both only list `homepage: https://builder.io/`).
> Filing here as the closest public Builder tracker so it isn't lost — please move it if there's a
> better internal home.

## Summary

`npm init builder.io@latest` → "Next.js" produces a project that **cannot start** on Next.js 16.

```
⨯ ERROR: This build is using Turbopack, with a `webpack` config and no `turbopack` config.
This may be a mistake.

As of Next.js 16 Turbopack is enabled by default and
custom webpack configurations may need to be migrated to Turbopack.
```

This is a hard error, not a warning: `next dev` never serves a page, and `next build` fails with
`Error: Call retries were exceeded`. It affects **every new user** running the quickstart now that
`create-next-app` resolves to Next 16.

Reproduced verbatim against a clean Next.js 16.2.12 scaffold with `create-builder.io@1.0.29` and
`@builder.io/dev-tools@1.78.0`.

## Root cause

The CLI's Turbopack guard tests for the literal string `--turbopack` in `package.json` →
`scripts.dev`. From the published `create-builder.io@1.0.29` bundle:

```js
// Turbopack detection
async function qu(t) {
let i = await Oi(t, t.getRepoRootDir());
return i?.scripts?.dev ? i.scripts.dev.includes("--turbopack") : false;
}

// used by the Next.js config injector
if (await qu(t)) return console.warn(
"Turbopack does not support the Builder.io devtools cli. Please disable Turbopack " +
"or manually integrate Builder.io into your Next.js project."
), n;
```

Next.js 15's `create-next-app` emitted `"dev": "next dev --turbopack"`, so the guard worked.
**Next.js 16 made Turbopack the default and dropped the flag** — the generated script is now
plain `"dev": "next dev"`. The check returns `false`, the guard fails open, and the CLI proceeds
to inject `BuilderDevTools()` from `@builder.io/dev-tools/next` into `next.config.ts`.

That plugin adds a webpack-only key unconditionally
(`node_modules/@builder.io/dev-tools/next/index.mjs`, v1.78.0):

```js
export default (opts = {}) => (nextConfig = {}) =>
Object.assign({}, nextConfig, {
webpack(config, options) {
if (opts.enabled !== false) config.plugins.push(new BuilderDevToolsPlugin(opts));
if (typeof nextConfig.webpack === "function") return nextConfig.webpack(config, options);
return config;
},
});
```

Next 16 sees a `webpack` config with no `turbopack` config → hard error.

### Failure chain

```mermaid
flowchart TD
A["npm init builder.io@latest"] --> B["create-builder.io@1.0.29"]
B --> C["npx create-next-app builder-app
(unpinned → Next 16.2.12)"]
C --> D["scripts.dev = 'next dev'
NO --turbopack flag"]
D --> E{"guard: scripts.dev.includes('--turbopack')"}
E -->|"false — fails open"| F["inject BuilderDevTools() into next.config.ts"]
F --> G["dev-tools/next adds a 'webpack' key"]
G --> H["Next 16: webpack config + no turbopack config"]
H --> I["HARD ERROR — dev and build both fail"]
E -->|"true (Next 15 only)"| J["warn + skip injection ✅"]

style I fill:#ffcccc
style E fill:#ffe4b5
```

## Issue 2 — DevTools is silently dead under Turbopack

The obvious workaround (`turbopack: {}`) is a trap. Under Turbopack the `webpack()` hook is
**never invoked**, so `BuilderDevToolsPlugin` never registers. The error disappears, the app runs,
and DevTools is inert with no diagnostic.

Measured on the repro app — served HTML scanned for injected assets:

| Config | Turbopack error | Dev server | DevTools overlay |
|---|---|---|---|
| none (as the CLI leaves it) | ❌ hard error | never serves | n/a |
| `turbopack: {}` | ✅ gone | ✅ serves | ❌ **silently absent** |
| `next dev --webpack` | ✅ gone | ✅ serves | ✅ plugin initialises |

With `turbopack: {}`, occurrence counts in the served HTML were `dev-tools -> 0`,
`builder -> 0`, `Builder -> 0`. The only `devtools` hits were Next's own `next-devtools` chunk.

`@builder.io/dev-tools` has no Turbopack support at all — its export map is `core`, `figma`,
`figma/jsx-runtime`, `next`, `node`, `remix`, `remix/server-build`, `server`, `vite`, `webpack`,
`angular`. **No turbopack entry.**

This is what users actually hit in practice: they find `turbopack: {}` via the error message's own
TIP, get a running app, and then report "the DevTools overlay never appears."

## Issue 3 — `/` renders the stock Next.js splash page

Separate from the above, and a real onboarding regression.

The CLI writes `app/[...page]/page.tsx` — a **required** catch-all, which does not match zero
segments — and never touches `app/page.tsx`. So the root URL always falls through to the untouched
`create-next-app` homepage. There is no Builder-branded landing page or "Get Started" button
anywhere in this flow.

Verified by route probe:

```
/ -> catch-all: false | next-splash: true
/some-builder-page -> catch-all: true | next-splash: false
```

The legacy starter used `pages/[[...page]].tsx` — an **optional** catch-all, which *does* match `/`
— so Builder content used to be served at the root. Users following the quickstart reasonably
expect a branded landing page and instead get "To get started, edit page.tsx" with zero Builder
branding, which reads as a failed install.

For the record, API-key wiring is fine and is **not** implicated: the CLI writes
`NEXT_PUBLIC_BUILDER_API_KEY` into `.env.local` (first match of `.env.local`,
`.env.development.local`, `.env.production.local`, `.env.development`, `.env.production`, `.env`)
and consumes it in the generated `builder-page.tsx` — gen1 via
`builder.init(process.env.NEXT_PUBLIC_BUILDER_API_KEY!)`, gen2 as `apiKey` on ``.

## Issue 4 — `next build --webpack` is separately broken

Even on the `--webpack` path, production builds fail. The plugin loads
(`[builder-dev-tools] webpack setupDevTools init`) and then throws:

```
unhandledRejection Error: createDevTools() requires a TypeScript instance passed to the 'ts' option
```

So `--webpack` rescues `dev` but not `build`.

## Proposed fix

1. **Fix the Turbopack detection.** Gate on the installed Next.js major version, not the
`--turbopack` substring: Next ≥ 16 means Turbopack unless `--webpack` is explicitly passed.
The CLI **already ships a version probe** it doesn't use here — it spawns `npx next -v` and
parses `{ major, minor, patch }`. Reuse it.
2. **Add Turbopack support to `@builder.io/dev-tools`** — the real fix. Until that exists, when
Turbopack is detected the CLI should either skip injection (the existing warning path) or write
`"dev": "next dev --webpack"` into `package.json` alongside the plugin, so the injected config is
actually honoured. Silently injecting a no-op webpack plugin is the worst of the three.
3. **Fix `next build --webpack`** — pass the TypeScript instance so `createDevTools()` stops throwing.
4. **Pin `create-next-app`** to a known-good major. It is currently invoked unpinned
(`npx create-next-app builder-app`), so the CLI inherits every future Next.js breaking change on
the day it ships.
5. **Reconsider `[...page]` vs `[[...page]]`.** The optional catch-all restores Builder content at
`/` and matches the legacy starter, though it would overwrite the create-next-app homepage. At
minimum, print a next step telling users which URL to visit.

### Underlying anti-pattern

Gating behaviour on a CLI **flag string** rather than the framework version is what broke here, and
it will break again on the next default flip. Worth fixing as a pattern, not just this instance.

## Workaround for users today

1. Use `"dev": "next dev --webpack"` — **not** `turbopack: {}` alone, or DevTools stays silently
disabled.
2. Visit a non-root path such as `/test`; `/` will always show the Next.js splash page.
3. Create a page at that URL in the Builder space matching `NEXT_PUBLIC_BUILDER_API_KEY` in
`.env.local`.

## Environment

| | |
|---|---|
| `create-builder.io` | 1.0.29 |
| `@builder.io/dev-tools` | 1.78.0 |
| `next` | 16.2.12 |
| `react` / `react-dom` | 19.2.4 |
| Repro | `npx create-next-app@latest --ts --app`, then the CLI's `next.config.ts` transform |

## Side note for maintainers (recommendation only — no action taken)

While tracing this, `packages/create-builder.io` in `BuilderIO/builder` was confirmed to be **dead
code**. It is version `0.1.4`, whereas npm serves `create-builder.io@1.0.29` from a different
codebase. The published bundle contains **zero** references to `starters/create-builder`,
`BuilderIO/builder`, or `archive/main.zip`, so neither the in-repo CLI nor the `starters/` tree is
reachable from the public quickstart.

This actively misdirects investigation — `packages/create-builder.io` and
`starters/create-builder/nextjs` are the first things anyone greps for, and both look plausible
while being unreachable. Recommend deprecating or removing them, **pending maintainer sign-off**.

Also note `starters/` is listed in `.prettierignore`, which causes some tooling to skip that tree
entirely; a raw filesystem scan is needed to audit it.

For completeness, no template in `BuilderIO/builder` has this bug. Only three `next.config.*` files
contain a `webpack` block and all are pinned below Next 16, so none need a `turbopack` key:

| File | Next | webpack block |
|---|---|---|
| `examples/next-js-builder-site/next.config.js` | 12.3.5 | Preact aliasing |
| `packages/react-tests/next14-pages/next.config.js` | 14.2.25 | react alias |
| `packages/sdks/snippets/gen1-next-14/next.config.js` | 14.2.25 | react alias |

Contributor guide

No contributing guide indexed for this repository

Research direction

Reproduce the quickstart with `create-builder.io@1.0.29`, Next.js 16.2.12, and `@builder.io/dev-tools@1.78.0`; inspect the published CLI's Turbopack guard and `@builder.io/dev-tools/next/index.mjs`. Check `next dev`, `next build`, and the generated `app/[...page]/page.tsx`; done requires an agreed scope and validated behavior for the dev server, production build, and root route.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, react, typescript
Domain
cli, frontend, tooling
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.