Skip to content

Lowercase named route component silently fails Fast Refresh with auto code splitting #7737

Description

@medz

Which project does this relate to?

Start / Router

Describe the bug

A route component function named component (lowercase) is runtime-valid, but it silently fails React Fast Refresh/HMR when used as the route component option with TanStack Start + Vite route splitting.

Vite reports a successful HMR update, the updated split module is fetched, and there is no warning or full reload, but the DOM stays stale until a manual browser reload. Renaming the same component to a PascalCase function fixes the issue immediately.

This is a DX footgun because the code is valid and the dev server says it updated, but the UI does not change.

Minimal failing route

import { createFileRoute } from '@tanstack/react-router'

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

function component() {
  return <div>111</div>
}

Steps:

  1. Run the dev server.
  2. Open the route in the browser.
  3. Change 111 to 112 and save.
  4. Observe that Vite logs a successful HMR update for the route module and the split component module.
  5. The browser DOM still shows 111.
  6. Manual reload shows 112.

During local debugging, the Vite websocket delivered a js-update for /src/routes/index.tsx, the browser fetched /src/routes/index.tsx?...&tsr-split=component, and that split module contained the new JSX text. The DOM still did not update.

Working version

import { createFileRoute } from '@tanstack/react-router'

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

function Home() {
  return <div>112</div>
}

With the PascalCase function name, the transformed split module registers the component for React Refresh, e.g. it contains a $RefreshReg$ call for Home, and HMR updates the DOM correctly.

The same naming rule also matters for root/shell components. This works:

export const Route = createRootRoute({
  shellComponent: RootComponent,
})

function RootComponent() {
  return <Outlet />
}

Expected behavior

One of these would avoid the silent stale UI state:

  • warn when a route component option points at a lowercase function/component identifier
  • fallback to a full reload when React Refresh cannot register the split route component
  • generate/hoist a PascalCase wrapper for the split component
  • document this as a route component requirement

Actual behavior

HMR appears to succeed, but the rendered route remains stale until manual reload.

Platform

Local reproduction:

  • @tanstack/react-router: 1.170.17
  • @tanstack/react-start: 1.168.27
  • @tanstack/router-cli: 1.167.18
  • @vitejs/plugin-react: 6.0.3
  • vite: 8.1.3
  • react: 19.2.7
  • react-dom: 19.2.7
  • @cloudflare/vite-plugin: 1.43.0
  • Node: 26.0.0
  • Bun: 1.3.14
  • OS: macOS
  • Browser: Chrome / in-app Chromium

Vite plugin order:

plugins: [
  cloudflare({ viteEnvironment: { name: 'ssr' } }),
  tailwindcss(),
  tanstackStart({ router: { generatedRouteTree: 'routes.generated.ts' } }),
  viteReact(),
]

Related issues / PRs

This looks related to prior React Refresh/HMR component-shape fixes, but I could not find an exact report for a lowercase named route component silently failing:

Metadata

Metadata

Assignees

No one assigned

    Labels

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