#98820·Next.js

React Compiler + Turbopack: lazy useState initializer throws TDZ when helper function declarations appear later in the same hook

Author: michaltarasiukCreated Sep 17, 2026Updated Sep 17, 2026
LabelsSWCTurbopack

Link to the code that reproduces this issue

https://github.com/michaltarasiuk/usestate-tdz-repro

To Reproduce

To Reproduce

  1. Clone https://github.com/michaltarasiuk/usestate-tdz-repro
  2. Run bun install
  3. Confirm next.config.ts has reactCompiler: true and experimental.turbopackRustReactCompiler: true
  4. Run bun dev (Turbopack is the default in Next.js 16)
  5. Open http://localhost:3000

Expected: the page renders and shows Stored value: default.

Actual: Next.js shows a runtime error overlay:

Runtime ReferenceError
Cannot access 'parseItem' before initialization

The broken hook is hooks/use-local-storage-broken.ts. It calls useState(getItem) before function parseItem is declared in the same hook. That is valid JavaScript (nested function declarations hoist), but React Compiler lowers parseItem to a const while leaving useState(getItem) above it.

Note: the error does not reproduce without React Compiler. With reactCompiler disabled, the page works and shows Stored value: default. After changing next.config.ts, restart with rm -rf .next && bun dev.

Current vs. Expected behavior

Expected: the hook runs on the client and the page renders with Stored value: default. In plain JavaScript, nested function declarations hoist within the hook body, so this is valid:

const [item, setItem] = useState(getItem);

function parseItem(stored: string | null) { … }
function getItem() {
  return parseItem(localStorage.getItem(key));
}

When useState runs the lazy initializer, getItem can call parseItem because both are hoisted.

Actual: with reactCompiler: true and experimental.turbopackRustReactCompiler: true, the client bundle throws:

Runtime ReferenceError
Cannot access 'parseItem' before initialization
    at getItem (hooks/use-local-storage-broken.ts:…)

React Compiler lowers parseItem to a const but leaves useState(getItem) above it. The lazy initializer runs before parseItem is assigned, so getItem hits a temporal dead zone.

Compiled output (simplified from .next/dev/static/chunks/…):

const [item, setItem] = useState(getItem);
// …
const parseItem = t4;
function getItem() {
  return parseItem(localStorage.getItem(key));
}

Without React Compiler, the same source works. Turbopack alone is not enough to trigger this; the compiler transform is the difference.

Provide environment information

Operating System:
  Platform: darwin
  Arch: arm64
  Version: Darwin Kernel Version 23.6.0
Binaries:
  Node: 24.3.0
  npm: 11.6.2
  Yarn: N/A
  pnpm: N/A
Relevant Packages:
  next: 16.3.5
  eslint-config-next: N/A
  react: 19.2.8
  react-dom: 19.2.8
  typescript: 5.9.3
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Turbopack, SWC

Which stage(s) are affected? (Select all that apply)

next dev (local)

Additional context

No response