diff --git a/.changeset/fix-beforeload-null-context.md b/.changeset/fix-beforeload-null-context.md
new file mode 100644
index 00000000000..5399fc50176
--- /dev/null
+++ b/.changeset/fix-beforeload-null-context.md
@@ -0,0 +1,9 @@
+---
+'@tanstack/router-core': patch
+---
+
+fix(router-core): treat null return from beforeLoad as no-op for route context
+
+When `beforeLoad` returns `null`, it is now treated the same as `undefined` (no-op) instead of being stored as `__beforeLoadContext: null`. This is consistent with how the `context` route option already handles null returns via `?? undefined`, and prevents null from silently interfering with context accumulation.
+
+Fixes #7110
diff --git a/packages/react-router/tests/routeContext.test.tsx b/packages/react-router/tests/routeContext.test.tsx
index d11f86420e6..daaa0656494 100644
--- a/packages/react-router/tests/routeContext.test.tsx
+++ b/packages/react-router/tests/routeContext.test.tsx
@@ -2630,6 +2630,42 @@ describe('useRouteContext in the component', () => {
expect(content).toBeInTheDocument()
})
+ test('route context preserved when beforeLoad returns null', async () => {
+ const rootRoute = createRootRoute({
+ beforeLoad: () => null,
+ component: () => {
+ const context = rootRoute.useRouteContext()
+ return
{JSON.stringify(context)}
+ },
+ })
+ const routeTree = rootRoute.addChildren([])
+ const router = createRouter({ routeTree, history, context: { foo: 'bar' } })
+
+ render()
+
+ const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
+
+ expect(content).toBeInTheDocument()
+ })
+
+ test('route context preserved when async beforeLoad returns null', async () => {
+ const rootRoute = createRootRoute({
+ beforeLoad: async () => null,
+ component: () => {
+ const context = rootRoute.useRouteContext()
+ return {JSON.stringify(context)}
+ },
+ })
+ const routeTree = rootRoute.addChildren([])
+ const router = createRouter({ routeTree, history, context: { foo: 'bar' } })
+
+ render()
+
+ const content = await screen.findByText(JSON.stringify({ foo: 'bar' }))
+
+ expect(content).toBeInTheDocument()
+ })
+
test('route context (sleep in beforeLoad), present in the root route', async () => {
const rootRoute = createRootRoute({
beforeLoad: async () => {
diff --git a/packages/router-core/src/load-matches.ts b/packages/router-core/src/load-matches.ts
index 3e99c828936..a2b6d798cf6 100644
--- a/packages/router-core/src/load-matches.ts
+++ b/packages/router-core/src/load-matches.ts
@@ -490,7 +490,7 @@ const executeBeforeLoad = (
}
const updateContext = (beforeLoadContext: any) => {
- if (beforeLoadContext === undefined) {
+ if (beforeLoadContext === undefined || beforeLoadContext === null) {
inner.router.batch(() => {
pending()
resolve()