From 6703f29d46ed6fc9519983355976e446c80a5038 Mon Sep 17 00:00:00 2001 From: mantrakp04 Date: Thu, 30 Apr 2026 13:06:26 -0700 Subject: [PATCH] Enhance TanStack Start integration and improve user experience - Updated the default not found component in the TanStack Start demo to provide a user-friendly 404 page. - Refactored the Header component to use a client-mounted UserButton for better rendering performance. - Improved cookie handling in the template library to support dynamic imports for TanStack Start server APIs. - Added a new function to retrieve the server request host, enhancing compatibility with different platforms. These changes improve the overall integration of TanStack Start, enhance user experience, and ensure better performance across the application. --- .claude/CLAUDE-KNOWLEDGE.md | 3 --- .../src/components/header.tsx | 12 ++++++++++- examples/tanstack-start-demo/src/router.tsx | 9 +++++++++ packages/template/src/lib/cookie.ts | 5 +++-- .../apps/implementations/client-app-impl.ts | 20 +++++++++++++------ 5 files changed, 37 insertions(+), 12 deletions(-) diff --git a/.claude/CLAUDE-KNOWLEDGE.md b/.claude/CLAUDE-KNOWLEDGE.md index 361785937..17a34543b 100644 --- a/.claude/CLAUDE-KNOWLEDGE.md +++ b/.claude/CLAUDE-KNOWLEDGE.md @@ -361,6 +361,3 @@ A: Invalid `tools` entries are rejected by `requestBodySchema` in `apps/backend/ ## Q: Why did the internal metrics E2E snapshots need to change in April 2026? A: The `/api/v1/internal/metrics` response now intentionally includes `analytics_overview.daily_anonymous_visitors_fallback`, `analytics_overview.anonymous_visitors_fallback`, and `active_users_by_country`. Those additions are reflected in `packages/stack-shared/src/interface/admin-metrics.ts` and the backend route, so the E2E snapshots must include them instead of treating them as regressions. - -## Q: How should a TanStack Start SDK package be added without dragging Dashboard V2 logic into the same PR? -A: Keep the integration PR scoped to generated package registration (`packages/tanstack-start/package.json`, `.gitignore`, `scripts/generate-sdks.ts`, `scripts/utils.ts`), template/package dependency metadata, and SDK runtime changes needed by TanStack Start (`cookie.ts`, token-store handling, handler SSR guard). Leave dashboard routes, hooks, app wiring, and admin API types in the dashboard PR. diff --git a/examples/tanstack-start-demo/src/components/header.tsx b/examples/tanstack-start-demo/src/components/header.tsx index 377754bc3..b8e292cba 100644 --- a/examples/tanstack-start-demo/src/components/header.tsx +++ b/examples/tanstack-start-demo/src/components/header.tsx @@ -1,5 +1,6 @@ import { Link } from "@tanstack/react-router"; import { UserButton } from "@stackframe/tanstack-start"; +import { useEffect, useState } from "react"; export function Header() { return ( @@ -14,10 +15,19 @@ export function Header() { Protected - +
); } + +function ClientMountedUserButton() { + const [isMounted, setIsMounted] = useState(false); + useEffect(() => { + setIsMounted(true); + }, []); + + return isMounted ? :
; +} diff --git a/examples/tanstack-start-demo/src/router.tsx b/examples/tanstack-start-demo/src/router.tsx index 327204f6f..9edcb7fb2 100644 --- a/examples/tanstack-start-demo/src/router.tsx +++ b/examples/tanstack-start-demo/src/router.tsx @@ -5,5 +5,14 @@ export function getRouter() { return createRouter({ routeTree, scrollRestoration: true, + defaultNotFoundComponent: () => ( +
+
+

404

+

Page not found

+

This route is not part of the TanStack Start demo.

+
+
+ ), }); } diff --git a/packages/template/src/lib/cookie.ts b/packages/template/src/lib/cookie.ts index 13a2b64c5..5a7a1d4bd 100644 --- a/packages/template/src/lib/cookie.ts +++ b/packages/template/src/lib/cookie.ts @@ -69,22 +69,23 @@ type DeleteCookieOptions = { noOpIfServerComponent?: boolean, domain?: string }; // IF_PLATFORM tanstack-start type TanStackStartServerCookieApi = typeof import("@tanstack/react-start/server"); -const tanStackStartServerCookieApiImportPath = "@tanstack/react-start/server"; let tanStackStartServerCookieApiPromise: Promise | null = null; let tanStackStartCookieHelperPromise: Promise | null = null; declare global { + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface ImportMetaEnv { SSR: boolean, } + // eslint-disable-next-line @typescript-eslint/consistent-type-definitions interface ImportMeta { readonly env: ImportMetaEnv, } } async function getTanStackStartServerCookieApi(): Promise { - tanStackStartServerCookieApiPromise ??= import(tanStackStartServerCookieApiImportPath); + tanStackStartServerCookieApiPromise ??= import(/* @vite-ignore */ "@tanstack/react-start/server"); return await tanStackStartServerCookieApiPromise; } // END_PLATFORM diff --git a/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts b/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts index fe2d14771..65fafff5f 100644 --- a/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts +++ b/packages/template/src/lib/stack-app/apps/implementations/client-app-impl.ts @@ -84,6 +84,17 @@ const prefetchedCrossDomainHandoffTtlMs = 55 * 60 * 1000; const allClientApps = new Map]>(); +async function getServerRequestHost(): Promise { + // IF_PLATFORM next + return (await sc.headers?.())?.get("host") ?? null; + // ELSE_IF_PLATFORM tanstack-start + const api = await import(/* @vite-ignore */ "@tanstack/react-start/server"); + return api.getRequestHeader("host") ?? null; + // ELSE_PLATFORM + return null; + // END_PLATFORM +} + type StackClientAppImplConstructorOptionsResolved = StackClientAppConstructorOptions & { inheritsFrom?: undefined }; export class _StackClientAppImplIncomplete implements StackClientApp { @@ -821,12 +832,9 @@ export class _StackClientAppImplIncomplete { // IF_PLATFORM tanstack-start if (!isBrowserLike()) { - return this._getOrCreateTokenStore(use(createCookieHelper()), overrideTokenStoreInit); + return this._getOrCreateTokenStore(use(createCookieHelper()), overrideTokenStoreInit); } // END_PLATFORM suspendIfSsr();