Skip to content

[Bug?]: [v2] routerLoad (3rd createHandler argument) dropped during v2 rewrite β€” TanStack Router SSR returns HTTP 500Β #2221

Description

@nerdchanii

Duplicates

  • I have searched the existing issues

Latest version

  • I have tested the latest version

Current behavior 😯

During the v2 rewrite (PR #1942, "Devinxi"), the 3rd argument to createHandler β€” routerLoad, introduced in #1840 to enable TanStack Router SSR β€” appears to have been removed without a replacement API or migration guide.

Any SolidStart v2 app using TanStack Router SSR with the official template's pattern returns HTTP 500 with TypeError: Cannot read properties of undefined (reading 'state'), because the router store is never initialized on the server.

Concretely:

  • createHandler(fn, options = {}) now accepts only 2 arguments; the 3rd argument is silently dropped.
  • The router store never gets initialized on the server.
  • SSR throws; server returns HTTP 500.

Expected behavior πŸ€”

Per the v1 behavior documented in #1840:

  • routerLoad is invoked at the top of provideRequestEvent, initializing the router store before render.
  • TanStack Router SSR works correctly; server returns HTTP 200 with rendered HTML.

Steps to reproduce πŸ•Ή

Steps:

  1. Create a SolidStart v2 app using the official with-tanstack-router template (@solidjs/start@2.0.0-beta.0, released 2026-07-15).
  2. Note the template calls createHandler(fn, undefined, routerLoad) in src/entry-server.tsx:
    createHandler(fn, undefined, routerLoad)
  3. Make an SSR request:
    curl http://localhost:3000/
  4. Actual: HTTP 500 with TypeError: Cannot read properties of undefined (reading 'state').
  5. Root cause: The 3rd argument is ignored by the rewritten createHandler, which now accepts only 2 arguments.

Quick verification β€” I built @solidjs/start locally from the patched handler.ts (restoring routerLoad) and tried it against the same template:

# Before patch (v2.0.0-beta.0)
$ curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/
500

# After patch (with routerLoad restored)
$ curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/
200

$ curl -s http://localhost:3000/ | grep -o '<title>[^<]*</title>'
<title>SolidStart</title>

After the patch: SSR renders correctly (HTTP 200, proper HTML), no SSR errors in console, client navigation works as expected.

Context πŸ”¦

Why this reads as an accidental regression (though I may be missing context):

  • The DevinxiΒ #1942 PR body, code review, and linked issue don't mention removing routerLoad anywhere β€” it may simply have been absorbed in a ~30k-line rewrite.
  • Official docs (create-handler.mdx) still document routerLoad?: (event: FetchEvent) => Promise<void> as a valid parameter even after the rewrite.
  • The official v2 with-tanstack-router template still calls createHandler(fn, undefined, routerLoad), which no longer works.
  • I couldn't find any issue or PR that discusses an intentional removal.
  • Git history of packages/start/src/server/handler.ts: introduced β†’ removed β†’ no re-add.

Scope note: This seems distinct from #2145, #2206, and #2133 β€” those address the stream output layer ([object Object] serialization), whereas this is about the router initialization layer (the SSR hook that primes the router store before render). Different layer, likely a different fix.

Timeline:

Date Event PR/Commit
2025-03-03 #1840 β€” "feat: add ssr for tanstack router" (@brenelz) introduces routerLoad as optional 3rd createHandler arg 26e97be1
2025-11-07 #1942 β€” "Devinxi" (@Brendonovich) full v2 rewrite of handler.ts (vinxi β†’ h3). routerLoad removed; createBaseHandler arg order changed from (fn, createPageEvent, options, routerLoad) to (createPageEvent, fn, options). No replacement API or migration note. bb63bb39
2026-05-06 solid-docs create-handler.mdx updated AFTER #1942 and STILL documents routerLoad?: (event: FetchEvent) => Promise<void> as a valid API (docs commit)
2026-07-15 @solidjs/start@2.0.0-beta.0 released (npm release)
2026-07-17β†’18 #2206 (@brenelz) stream output fix β€” UNRELATED to router initialization (fixes the stream transport layer, not the router init layer) (separate issue)

One possible direction (Approach B β€” very much open to alternatives): restore routerLoad as an optional, backward-compatible 3rd argument to createHandler:

export function createHandler(
  fn: (context: PageEvent) => JSX.Element,
  options: HandlerOptions | ((context: PageEvent) => HandlerOptions | Promise<HandlerOptions>) = {},
  routerLoad?: (event: FetchEvent) => Promise<void>, // restored, optional
): H3 {
  if (routerLoad) {
    const _createPageEvent = createPageEvent;
    const customCreatePageEvent = async (e: FetchEvent) => {
      const pageEvent = await _createPageEvent(e);
      await routerLoad(e);
      return pageEvent;
    };
    return createBaseHandler(customCreatePageEvent, fn, options);
  }
  return createBaseHandler(createPageEvent, fn, options);
}

When options is an async function, users can technically call router.update() + await router.load() inside it β€” but that only really works for routers that expose those methods. routerLoad felt like the cleaner general-purpose hook, which I assume is why #1840 added it.

If the team would prefer a more general onRequest/initSSR callback (or something else entirely), I'd be glad to go in whatever direction feels right.

A few things I'd love a steer on:

  • Does restoring routerLoad (or something like it) feel like the right call, or is there a differently-shaped SSR initializer the team would rather go with?
  • If something does get restored, would it make sense to sync the docs and the template at the same time? Right now the docs still list routerLoad but the runtime has dropped it.
  • Would it be worth revisiting createBaseHandler's arg order too, or keep the v2 order and just restore the public createHandler entry point?

Happy to open a PR in whichever direction the team prefers β€” and very happy to be corrected if I've misread any of the history.

Your environment 🌎

System:
  OS: macOS 25.5.0
  CPU: arm64
Binaries:
  Node: 20.x
npmPackages:
  @solidjs/start: 2.0.0-beta.0

Related:
#1840 (introduced routerLoad)
#1942 (v2 rewrite, where routerLoad appears dropped)
#2206 / #2145 / #2133 (stream output layer, separate)
TanStack/router#3521 (original SSR feature request motivating #1840)
solid-cli/PR#78 (where this surfaced via the with-tanstack-router template)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions