Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .agents/skills/bundle-size-optimization
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ TanStack Router is a type-safe router with built-in caching and URL state manage
- Framework-agnostic core logic separated from React/Solid bindings
- Type-safe routing with search params and path params
- Use workspace protocol for internal dependencies (`workspace:*`)
- Always use curly braces for `if`, `else`, loops, and similar control statements. Never write one-line bodies like `if (foo) x = 1`.

## Dev environment tips

Expand Down
34 changes: 34 additions & 0 deletions benchmarks/bundle-size/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,41 @@ Each package has `minimal` and `full` scenarios:
pnpm nx run @benchmarks/bundle-size:build
```

Run one or more scenarios during local optimization:

```bash
pnpm nx run @benchmarks/bundle-size:build -- --scenario react-router.minimal
pnpm nx run @benchmarks/bundle-size:build -- --scenario react-router.minimal,react-router.full
```

Filtered runs build only the package projects needed by selected scenarios. Full runs build all package projects needed by all scenarios. If the required packages are already built and unchanged, skip that step:

```bash
pnpm nx run @benchmarks/bundle-size:build -- --scenario react-router.minimal --skip-package-builds
```

This writes:

- `benchmarks/bundle-size/results/current.json`
- `benchmarks/bundle-size/results/benchmark-action.json`

`current.json` includes run status, selected package build projects, per-scenario totals, per-file sizes, and the emitted JS files used for measurement. Dist paths use `scenarioDir`/`outDir`, e.g. `react-router.minimal` maps to `benchmarks/bundle-size/dist/react-router-minimal/`.

## Local Query Tools

```bash
pnpm benchmark:bundle-size:query --id react-router.minimal
pnpm benchmark:bundle-size:diff --baseline /tmp/base-current.json --id react-router.minimal
pnpm benchmark:bundle-size:history --id react-router.minimal --top-deltas 20
```

For source attribution, run an analysis build. This uses hidden source maps and writes source estimates into `current.json`; those estimates are for investigation only, not tracking.

```bash
pnpm nx run @benchmarks/bundle-size:build -- --scenario react-router.minimal --analysis
pnpm benchmark:bundle-size:analyze --id react-router.minimal --top-sources 30
```

## CI Reporting

- PR workflow generates a sticky comment with:
Expand All @@ -56,6 +86,10 @@ The measurement script supports optional interfaces for historical backfilling:
- `--sha`
- `--measured-at`
- `--append-history`
- `--scenario`
- `--analysis`
- `--sourcemap`
- `--skip-package-builds`

These are intended for one-off scripts that replay historical commits and append results to the same history dataset shape used for chart generation.
If `--append-history` points at a `data.js` file, output is written as `window.BENCHMARK_DATA = ...` for direct GitHub Pages compatibility.
9 changes: 9 additions & 0 deletions benchmarks/bundle-size/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,21 @@
"scripts": {
"build": "node ../../scripts/benchmarks/bundle-size/measure.mjs"
},
"nx": {
"targets": {
"build": {
"cache": false,
"dependsOn": []
}
}
},
"dependencies": {
"@tanstack/react-router": "workspace:^",
"@tanstack/solid-router": "workspace:^",
"@tanstack/vue-router": "workspace:^",
"@tanstack/react-start": "workspace:^",
"@tanstack/solid-start": "workspace:^",
"@tanstack/vue-start": "workspace:^",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use workspace:* for the newly added internal dependency.

Line 22 uses workspace:^, but repo rules require workspace:* for internal packages.

Proposed fix
-    "`@tanstack/vue-start`": "workspace:^",
+    "`@tanstack/vue-start`": "workspace:*",
As per coding guidelines: "`**/package.json`: Use workspace protocol for internal dependencies (`workspace:*`)".
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"@tanstack/vue-start": "workspace:^",
"`@tanstack/vue-start`": "workspace:*",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/bundle-size/package.json` at line 22, The dependency entry
"`@tanstack/vue-start`": "workspace:^" in package.json uses the wrong workspace
protocol; update the version specifier to use the repo-required workspace
protocol by changing the dependency value for "`@tanstack/vue-start`" from
"workspace:^" to "workspace:*" so internal package resolution follows the
project's guidelines.

"react": "^19.0.0",
"react-dom": "^19.0.0",
"solid-js": "^1.9.10",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,20 @@ import { pluginReact } from '@rsbuild/plugin-react'
import { tanstackStart } from '@tanstack/react-start/plugin/rsbuild'

const outDir = process.env.BUNDLE_SIZE_DIST_DIR ?? 'dist-rsbuild'
const clientOutput = process.env.BUNDLE_SIZE_RSB_CLIENT_OUTPUT
const startOptions = clientOutput
? {
rsbuild: {
client: {
output: clientOutput as 'module' | 'iife',
},
},
}
: undefined
Comment on lines +7 to +15
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

For the current versions of @tanstack/react-start/plugin/rsbuildand@rsbuild/core, what exact values are supported for rsbuild.client.output?

💡 Result:

As of May 2026, there is no configuration property named rsbuild.client.output in either @rsbuild/core or the @tanstack/react-start/plugin/rsbuild plugin [1][2][3]. The confusion likely stems from the Rsbuild Environments API, which allows for separate configurations for different build targets (such as client and server) [4]. In Rsbuild, configuration for the client-side build is typically defined within the environments.client object, and output-related settings are managed under the standard output configuration object [4][3][5]. If you are looking to configure output settings specifically for a client environment, you should use the following structure in your rsbuild.config.ts: export default defineConfig({ environments: { client: { output: { // Your output configuration here (e.g., distPath, filename) }, }, }, }); For authoritative documentation on available output options, refer to the official Rsbuild configuration guide, which details properties such as distPath, filename, and assetPrefix [3][5][6]. The TanStack Start Rsbuild plugin manages its own internal configuration and does not expose a custom rsbuild.client.output property [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the relevant config file
FILE="benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts"
if [ -f "$FILE" ]; then
  echo "=== $FILE ==="
  nl -ba "$FILE" | sed -n '1,120p'
else
  echo "File not found: $FILE"
  ls -R benchmarks/bundle-size/scenarios/react-start-minimal | head
fi

echo
echo "=== Search for BUNDLE_SIZE_RSB_CLIENT_OUTPUT usage ==="
rg -n "BUNDLE_SIZE_RSB_CLIENT_OUTPUT" -S .

echo
echo "=== Search for 'tanstackStart(' usage in this config ==="
rg -n "tanstackStart\(" -S benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts

echo
echo "=== Search for 'output:' with module/iife strings in repo ==="
rg -n "output:\s*['\"](module|iife)['\"]" -S . || true

echo
echo "=== Search for 'client: {' and rsbuild: structure patterns ==="
rg -n "rsbuild:\s*{\s*client:\s*{|\bclient:\s*{\s*output" -S benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts || true

Repository: TanStack/router

Length of output: 199


🌐 Web query:

tanstack react-start rsbuild plugin client output module iife

💡 Result:

Answer: In Rsbuid (Rspack-based), “client output module iife” doesn’t correspond to a single TanStack Start setting. What you actually control is Rsbuild output format: 1) Default: IIFE for web targets Rsbuild states that when building a web application, it generates output in the IIFE format by default [1]. 2) Switching away from IIFE to ES modules If you want ES module output instead of IIFE, Rsbuild provides output.module (boolean). Rsbuild says: “When building a web application, Rsbuild generates the output in the IIFE format by default. If you want to output ES Modules instead, set output.module to true.” [1]. Example: export default { output: { module: true, }, }; When output.module is enabled, Rsbuild also adds type="module" to generated script tags [1]. 3) Node target default differs For completeness: Rsbuild says Node.js apps output ES modules format by default, and you can change to CommonJS via output.module = false when output.target is node [1]. 4) How this relates to TanStack Start “rsbuild plugin” TanStack Start’s rsbuild integration is implemented as Rsbuild plugins (e.g., Start rsbuild plugin core plus framework-specific rsbuild plugin entries) [2][3]. However, the IIFE-vs-ESM detail is governed by Rsbuild’s output configuration (not an “iife” option specific to TanStack Start, based on the Rsbuild output.module documentation) [1]. So, to get “client module” output (ESM) rather than IIFE in a TanStack Start + Rsbuild web build, set Rsbuild output.module=true; otherwise you’ll get IIFE by default for web [1].

Citations:


🌐 Web query:

@tanstack/react-start plugin rsbuild output module iife

💡 Result:

In Rsbuild, the default web-app JavaScript format is IIFE. If you want an ES-module output instead, set output.module = true; this results in ES modules and Rsbuild sets scriptLoading to module in the generated HTML. [1] So for the TanStack React Start rsbuild plugin: it doesn’t (by itself) switch you away from IIFE. To get module output (instead of IIFE), you configure Rsbuild’s output.module. [1] Example (rsbuild.config.ts): import { defineConfig } from '@rsbuild/core' import { pluginReact } from '@rsbuild/plugin-react' export default defineConfig({ plugins: [pluginReact], output: { module: true, }, }) (With output.module enabled, Rsbuild also adds type="module" to generated script tags.) [1] Separately, note that Rspack has explicit output.iife and output.module controls; output.iife defaults to true, and output.module toggles module output and implies output.iife=false. [2]

Citations:


🌐 Web query:

rsbuild client output module iife configuration

💡 Result:

In Rsbuild, “IIFE” output is controlled by building for the web target, not by an explicit “IIFE” flag. 1) When does Rsbuild emit IIFE? - For web applications (output.target = 'web', which is the default), Rsbuild generates output in the IIFE format by default. [1] 2) How to configure client output “module” (ESM) vs IIFE - Rsbuild has output.module (boolean). It is described as outputting JavaScript in ES modules format, and it is only available when output.target is 'web' or 'node'. [1] - If you want ES Modules instead of the default IIFE for web apps, set output.module = true. [1] Example (web/client => IIFE default): export default { output: { target: 'web', // output.module omitted/false => IIFE by default }, }; Example (web/client => ESM, not IIFE): export default { output: { target: 'web', module: true, }, }; 3) Node client (if you’re using environments for client/server) - For output.target = 'node', Rsbuild outputs ES modules by default, and you can set output.module = false to output CommonJS instead. [1] If you share your exact “rsbuild client” setup (e.g., whether you use environments and what you set for output.target), I can map it precisely to the correct config block.

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and print the relevant config file
FILE="benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts"
if [ -f "$FILE" ]; then
  echo "=== $FILE ==="
  nl -ba "$FILE" | sed -n '1,120p'
else
  echo "File not found: $FILE"
  ls -R benchmarks/bundle-size/scenarios/react-start-minimal | head
fi

echo
echo "=== Search for BUNDLE_SIZE_RSB_CLIENT_OUTPUT usage ==="
rg -n "BUNDLE_SIZE_RSB_CLIENT_OUTPUT" -S .

echo
echo "=== Search for 'tanstackStart(' usage in this config ==="
rg -n "tanstackStart\(" -S benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts

echo
echo "=== Search for 'output:' with module/iife strings in repo ==="
rg -n "output:\s*['\"](module|iife)['\"]" -S . || true

echo
echo "=== Search for 'client: {' and rsbuild: structure patterns ==="
rg -n "rsbuild:\s*{\s*client:\s*{|\bclient:\s*{\s*output" -S benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts || true

Repository: TanStack/router

Length of output: 199


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts"
echo "=== FILE CONTENTS: $FILE ==="
if [ -f "$FILE" ]; then
  sed -n '1,160p' "$FILE" | cat
else
  echo "MISSING: $FILE"
  exit 1
fi

echo
echo "=== SHOW PACKAGE JSON(S) FOR DEPENDENCY VERSIONS ==="
# Try common locations; if missing, still show what exists.
for p in \
  "benchmarks/bundle-size/package.json" \
  "benchmarks/package.json" \
  "package.json" \
  ; do
  if [ -f "$p" ]; then
    echo "--- $p ---"
    cat "$p"
  fi
done

echo
echo "=== RIPGREP: look for tanstackStart and rsbuild.client.output usage ==="
rg -n "tanstackStart\\(" benchmarks/bundle-size/scenarios/react-start-minimal -S || true
rg -n "rsbuild:\\s*\\{[\\s\\S]*client[\\s\\S]*output" benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts -S || true
rg -n "client:\\s*\\{[\\s\\S]*output" benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts -S || true

Repository: TanStack/router

Length of output: 7409


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find implementation/types for tanstackStart in the monorepo
rg -n "export (function|const|default) tanstackStart|tanstackStart\\s*\\(" packages -S || true
rg -n "tanstackStart" packages/@tanstack -S || true

# Look for rsbuild plugin option typing, especially rsbuild.client.output
rg -n "rsbuild\\s*:\\s*\\{" packages -S || true
rg -n "client\\s*:\\s*\\{[\\s\\S]*output" packages -S || true
rg -n "output\\s*:\\s*'module'\\s*\\|\\s*'iife'|\"module\"\\s*\\|\\s*\"iife\"" -S packages || true
rg -n "module'\\s*\\|\\s*'iife|iife'\\s*\\|" -S packages || true

# Locate files under `@tanstack/react-start/plugin/rsbuild`
fd -t f "rsbuild" packages | head -n 50
fd -t f "*react-start*rsbuild*" packages | head -n 50

Repository: TanStack/router

Length of output: 5395


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find implementation/types for tanstackStart in the monorepo
rg -n "export (function|const|default) tanstackStart|tanstackStart\\s*\\(" packages -S || true
rg -n "tanstackStart" packages/@tanstack -S || true

# Look for rsbuild plugin option typing, especially rsbuild.client.output
rg -n "rsbuild\\s*:\\s*\\{" packages -S || true
rg -n "client\\s*:\\s*\\{[\\s\\S]*output" packages -S || true
rg -n "output\\s*:\\s*'module'\\s*\\|\\s*'iife'|\"module\"\\s*\\|\\s*\"iife\"" -S packages || true
rg -n "module'\\s*\\|\\s*'iife|iife'\\s*\\|" -S packages || true

# Locate files under `@tanstack/react-start/plugin/rsbuild`
fd -t f "rsbuild" packages | head -n 50
fd -t f "*react-start*rsbuild*" packages | head -n 50

Repository: TanStack/router

Length of output: 5395


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print relevant parts of TanStack Start rsbuild plugin implementation and schema
sed -n '1,220p' packages/react-start/src/plugin/rsbuild.ts | cat
echo "-----"
sed -n '1,220p' packages/start-plugin-core/src/rsbuild/schema.ts | cat
echo "-----"
sed -n '1,120p' packages/router-core/src/manifest.ts | cat

Repository: TanStack/router

Length of output: 7144


🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n "parseStartConfig\\(" packages/start-plugin-core -S
rg -n "tanstackStartRsbuildOptionsSchema\\.parse\\(" packages/start-plugin-core -S
rg -n "tanStackStartRsbuild\\(" packages/start-plugin-core -S
rg -n "tanStackStartRsbuild" packages/start-plugin-core -S

Repository: TanStack/router

Length of output: 684


🏁 Script executed:

#!/bin/bash
set -euo pipefail
sed -n '1,220p' packages/start-plugin-core/src/rsbuild/plugin.ts | cat
echo "-----"
sed -n '1,120p' packages/start-plugin-core/src/rsbuild/index.ts | cat

Repository: TanStack/router

Length of output: 9554


Validate BUNDLE_SIZE_RSB_CLIENT_OUTPUT before assigning (avoid unsafe cast)

Line 11 force-casts the env value to 'module' | 'iife'. tanStackStartRsbuild already validates rsbuild.client.output at runtime with z.enum(['module', 'iife']), so invalid values won’t silently proceed—but the as assertion still bypasses the intended TypeScript type safety. Narrow the env value first.

Proposed fix
 const outDir = process.env.BUNDLE_SIZE_DIST_DIR ?? 'dist-rsbuild'
 const clientOutput = process.env.BUNDLE_SIZE_RSB_CLIENT_OUTPUT
+const isValidClientOutput =
+  clientOutput === 'module' || clientOutput === 'iife'
+
+if (clientOutput && !isValidClientOutput) {
+  throw new Error(
+    `Invalid BUNDLE_SIZE_RSB_CLIENT_OUTPUT: ${clientOutput}. Expected "module" or "iife".`,
+  )
+}
+
 const startOptions = clientOutput
   ? {
       rsbuild: {
         client: {
-          output: clientOutput as 'module' | 'iife',
+          output: clientOutput,
         },
       },
     }
   : undefined
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/bundle-size/scenarios/react-start-minimal/rsbuild.config.ts`
around lines 7 - 15, The env value BUNDLE_SIZE_RSB_CLIENT_OUTPUT is being
force-cast to 'module'|'iife' when building startOptions; instead narrow and
validate it first to preserve TypeScript safety: check that
process.env.BUNDLE_SIZE_RSB_CLIENT_OUTPUT is one of the allowed strings
('module' or 'iife') (or use a small type-guard/lookup) and only then assign it
to clientOutput so startOptions.rsbuild.client.output receives a properly typed
value; refer to startOptions, rsbuild.client.output and the runtime z.enum in
tanStackStartRsbuild when adding the validation.


export default defineConfig({
logLevel: 'silent',
plugins: [pluginReact({ splitChunks: false }), tanstackStart()],
plugins: [pluginReact({ splitChunks: false }), tanstackStart(startOptions)],
output: {
distPath: {
root: outDir,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createRouter } from '@tanstack/vue-router'
import { routeTree } from './routeTree.gen'

export function getRouter() {
return createRouter({
routeTree,
scrollRestoration: true,
})
}
218 changes: 218 additions & 0 deletions benchmarks/bundle-size/scenarios/vue-start-full/src/routes/__root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import {
Asset,
Await,
Block,
Body,
CatchBoundary,
CatchNotFound,
ClientOnly,
DefaultGlobalNotFound,
ErrorComponent,
HeadContent,
Html,
Link,
Match,
MatchRoute,
Matches,
Navigate,
Outlet,
RouterContextProvider,
ScriptOnce,
Scripts,
ScrollRestoration,
createLink,
createRootRoute,
linkOptions,
useAwaited,
useBlocker,
useCanGoBack,
useChildMatches,
useElementScrollRestoration,
useLinkProps,
useLoaderData,
useLoaderDeps,
useLocation,
useMatch,
useMatchRoute,
useMatches,
useNavigate,
useParams,
useParentMatches,
useRouteContext,
useRouter,
useRouterState,
useSearch,
useTags,
} from '@tanstack/vue-router'
import {
createMiddleware,
createServerFn,
useServerFn,
} from '@tanstack/vue-start'

const requestMiddleware = createMiddleware().server(async ({ next }) => {
return next()
})

const functionMiddleware = createMiddleware({ type: 'function' })
.client(async ({ next }) => {
return next()
})
.server(async ({ next }) => {
return next()
})

const helloServerFn = createServerFn({ method: 'GET' })
.middleware([requestMiddleware, functionMiddleware])
.handler(async () => {
return 'hello from server fn'
})

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

function RootComponent() {
const router = useRouter()
const [awaited] = useAwaited({ promise: Promise.resolve('ready') })
const linkProps = useLinkProps({ to: '/' } as any)
const matchRoute = useMatchRoute()
const matches = useMatches()
const parentMatches = useParentMatches()
const childMatches = useChildMatches()
const match = useMatch({ strict: false, shouldThrow: false } as any)
const loaderDeps = useLoaderDeps({ strict: false } as any)
const loaderData = useLoaderData({ strict: false } as any)
const params = useParams({ strict: false } as any)
const search = useSearch({ strict: false } as any)
const routeContext = useRouteContext({ strict: false } as any)
const routerState = useRouterState({ select: (state) => state.status } as any)
Comment on lines +78 to +89
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify strict TS settings and enumerate current `as any` usages in the changed benchmark surfaces.
set -euo pipefail

echo "== tsconfig files with strict options =="
fd -i 'tsconfig.*json' | xargs -I{} sh -c 'echo "--- {}"; rg -n "\"strict\"|\"noImplicitAny\"|\"strictNullChecks\"" "{}" || true'

echo
echo "== 'as any' usages in bundle-size scenarios =="
rg -n --type=ts --type=tsx '\bas any\b' benchmarks/bundle-size/scenarios

Repository: TanStack/router

Length of output: 25034


Avoid as any in strict benchmark router-start calls

benchmarks/bundle-size/tsconfig.json enables "strict": true, but benchmarks/bundle-size/scenarios/vue-start-full/src/routes/__root.tsx uses as any in the core hook calls (lines 78-89), which bypasses type checking and can hide real API mismatches. Replace these as any assertions on useLinkProps, useMatch, useLoaderDeps, useLoaderData, useParams, useSearch, and useRouteContext with properly typed options (or narrower casts) so strict TS protections remain effective.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@benchmarks/bundle-size/scenarios/vue-start-full/src/routes/__root.tsx` around
lines 78 - 89, The hook calls currently use "as any" which bypasses strict
types; remove those "as any" assertions and pass properly typed option objects
or explicit generics that match each hook's signature (e.g., for useLinkProps,
useMatch, useLoaderDeps, useLoaderData, useParams, useSearch, useRouteContext
and useRouterState) or use a narrower cast like Partial<ExpectedOptionsType>
when needed; locate the calls to useLinkProps, useMatchRoute, useMatches,
useParentMatches, useChildMatches, useMatch, useLoaderDeps, useLoaderData,
useParams, useSearch, useRouteContext and useRouterState in the file and replace
the blanket any casts with correct option types or explicit generic parameters
so the compiler enforces strict typing.

const location = useLocation()
const canGoBack = useCanGoBack()
const navigate = useNavigate()
const scrollEntry = useElementScrollRestoration({ id: 'root-scroll' })
const tags = useTags()
const invokeServerFn = useServerFn(helloServerFn)

useBlocker({
shouldBlockFn: () => false,
disabled: true,
withResolver: false,
})

const linkFactoryResult = linkOptions({ to: '/' } as any)
const routeMatchResult = matchRoute({ to: '/' } as any)
const SvgLink = createLink('svg')

const startSurface = [createMiddleware, createServerFn, useServerFn]
const hooksAndComponents = [
useAwaited,
useLinkProps,
useMatchRoute,
useMatches,
useParentMatches,
useChildMatches,
useMatch,
useLoaderDeps,
useLoaderData,
useBlocker,
useNavigate,
useParams,
useSearch,
useRouteContext,
useRouter,
useRouterState,
useLocation,
useCanGoBack,
useElementScrollRestoration,
useTags,
Await,
CatchBoundary,
CatchNotFound,
ClientOnly,
DefaultGlobalNotFound,
ErrorComponent,
Link,
Match,
MatchRoute,
Matches,
Navigate,
Outlet,
RouterContextProvider,
ScrollRestoration,
Block,
ScriptOnce,
Asset,
HeadContent,
Scripts,
Body,
Html,
]

;(globalThis as any).__TANSTACK_BUNDLE_SIZE_KEEP__ = {
hooksAndComponents,
startSurface,
}

void awaited
void linkFactoryResult
void matches.value
void parentMatches.value
void childMatches.value
void match.value
void loaderDeps.value
void loaderData.value
void params.value
void search.value
void routeContext.value
void routerState.value
void location.value
void canGoBack.value
void navigate
void scrollEntry
void tags()
void routeMatchResult.value
void invokeServerFn

return (
<Html>
<head>
<HeadContent />
</head>
<Body>
<ScriptOnce>{'window.__tsr_bundle_size = true'}</ScriptOnce>
<Asset
tag="meta"
attrs={{ name: 'bundle-size', content: 'vue-start-full' }}
/>
<Link {...(linkProps as any)}>home</Link>
<SvgLink to="/" aria-label="svg-home">
<circle cx="8" cy="8" r="7" />
</SvgLink>
<MatchRoute to="/">{() => <span data-test="match-route" />}</MatchRoute>
<ClientOnly fallback={<span data-test="client-only-fallback" />}>
<span data-test="client-only" />
</ClientOnly>
<Await
promise={Promise.resolve('done')}
children={() => <span data-test="await" />}
/>
<Block shouldBlockFn={() => false} disabled withResolver={false}>
{() => <span data-test="block" />}
</Block>
<CatchNotFound fallback={() => <DefaultGlobalNotFound />}>
<span data-test="catch-not-found" />
</CatchNotFound>
<RouterContextProvider router={router}>
<span data-test="nested-router-context" />
</RouterContextProvider>
<ScrollRestoration />
<Outlet />
<Scripts />
<div data-test="full-root">
<div>hello world</div>
</div>
</Body>
</Html>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createFileRoute } from '@tanstack/vue-router'

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

function IndexComponent() {
return <div>hello world</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueJsx from '@vitejs/plugin-vue-jsx'
import { tanstackStart } from '@tanstack/vue-start/plugin/vite'

export default defineConfig({
plugins: [tanstackStart(), vue(), vueJsx()],
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createRouter } from '@tanstack/vue-router'
import { routeTree } from './routeTree.gen'

export function getRouter() {
return createRouter({
routeTree,
scrollRestoration: true,
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import {
Body,
HeadContent,
Html,
Outlet,
Scripts,
createRootRoute,
} from '@tanstack/vue-router'

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

function RootComponent() {
return (
<Html>
<head>
<HeadContent />
</head>
<Body>
<Outlet />
<Scripts />
</Body>
</Html>
)
}
Loading
Loading