Skip to content

createEffect and onMount don't fire on hydrated routes (Solid Start + TanStack Solid Router) #7494

@mryarbles

Description

@mryarbles

createEffect and onMount don't fire on hydrated routes (Solid Start + TanStack Solid Router)

Summary

For routes rendered via Solid Start (SSR + client hydration), Solid lifecycle hooks (createEffect, onMount) never fire on the client, even though:

  • The route component body does execute on the client (signals can be created)
  • onCleanup does fire (so a Solid scope is being created and then disposed)
  • Plain JS scheduling (setTimeout, queueMicrotask, requestAnimationFrame, Promise.then) all fire normally
  • Event handlers attached via JSX work (the hydrated DOM is interactive)

Routes with ssr: false are unaffected — they go through a fresh-render path on the client and lifecycle hooks work as expected.

Versions

  • @tanstack/solid-start: ^1.168.13
  • @tanstack/solid-router: ^1.170.8
  • @tanstack/solid-router-ssr-query: ^1.167.0
  • @tanstack/router-plugin: ^1.168.11
  • solid-js: ^1.9.13
  • vite: ^8.0.0

Minimal reproduction

Any default-SSR route:

// src/routes/index.tsx
import { createFileRoute } from '@tanstack/solid-router'
import { createEffect, createSignal, onCleanup, onMount } from 'solid-js'

export const Route = createFileRoute('/')({ component: Home })

function Home() {
  const tag = `[diag] ${typeof window === 'undefined' ? 'SSR' : 'CLIENT'}`
  console.log(`${tag} 1. body`)

  const [count] = createSignal(0)
  console.log(`${tag} 2. createSignal returned`, count())

  createEffect(() => {
    console.log(`${tag} 3. createEffect`)
  })

  onMount(() => {
    console.log(`${tag} 4. onMount`)
  })

  onCleanup(() => {
    console.log(`${tag} 5. onCleanup`)
  })

  if (typeof window !== 'undefined') {
    queueMicrotask(() => console.log(`${tag} 6. queueMicrotask`))
    setTimeout(() => console.log(`${tag} 7. setTimeout`), 0)
    requestAnimationFrame(() => console.log(`${tag} 8. rAF`))
  }

  return <h1>hello</h1>
}

Observed client console output

[diag] CLIENT 1. body
[diag] CLIENT 2. createSignal returned 0
[diag] CLIENT 6. queueMicrotask
[diag] CLIENT 7. setTimeout
[diag] CLIENT 8. rAF
[diag] CLIENT 5. onCleanup

Missing: 3. createEffect and 4. onMount.

Expected

createEffect and onMount should fire on the client during/after hydration, as they do for non-routed Solid components and for ssr: false routes.

Interpretation

onCleanup firing strongly suggests the route component is called inside a disposable scope (probably as part of route resolution / dry-run for matching) that is torn down before reactive effects can flush. The actual interactive DOM is then hydrated from SSR'd HTML in a separate scope where the function isn't re-invoked, so lifecycle hooks scheduled by the first invocation never run.

If that's the intended design, it should probably be documented prominently (since onMount-for-client-side-init is a near-universal Solid pattern). If it's not intended, the route component's reactive scope needs to be allowed to flush effects before disposal.

Workarounds (currently in use)

For SSR'd routes:

  1. Render-body side effect with !isServer guard — works for idempotent operations (writing to documentElement, preloading images, etc.).
  2. ssr: false on the route — gives up SSR for the route but lifecycle hooks then work normally.
  3. setTimeout(() => ..., 0) from the render body — defers past the disposed scope, fires reliably.

Cross-route observation

In our app, /account/children (which has ssr: false) uses createQuery + createMutation successfully and lifecycle hooks fire. The index route (/) — default ssr: true — exhibits the bug. This is consistent across browser sessions and across pnpm dev restarts.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    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