Skip to content

Conversation

@FatahChan
Copy link
Contributor

@FatahChan FatahChan commented Oct 14, 2025

Fixes #5455

Previously, when prerendering was enabled without specifying pages, only the root path (/) was prerendered plus whatever was crawled from the / if crawlLinks is enabled. This change automatically discovers and prerenders all static routes.

Changes

Core Prerendering Improvements

  • Automatic static path discovery: Added logic to discover all static routes
  • Enhanced filtering: Added getPrerenderablePaths() function that filters out:
    • Layout routes (starting with _)
    • Dynamic routes (containing $) - excluded from discovery, but if a link exists to the dynamic route and crawlLinks is enabled then it will also be prerendered
    • Routes without components
  • Improved redirect handling: Added proper redirect following during prerendering

E2E Testing

  • New test suite: Added comprehensive basic-prerendering e2e test
  • Dummy server management: Created scripts to manage dummy servers for API testing during prerendering
  • API data fetching: Fixed issue where API calls failed during prerendering in test mode
  • Content verification: Tests verify that prerendered content is correctly generated

Technical Details

  • Uses GeneratorPlugin to discover route structure
  • Filters routes based on isNonPath, dynamic parameters, and component existence
  • Maintains backward compatibility - existing page specifications still work
  • Added redirect count limit and proper redirect handling

Expected Behavior

When prerender.enabled: true && prerender.autoStaticPathsDiscovery: true (default: true) :

  • ✅ All static routes are automatically discovered and merged with specified pages if any
  • ✅ Dynamic routes are excluded from discovery (but can still be prerendered via crawlLinks if linked)
  • ✅ Layout routes are excluded (as expected)
  • ✅ API routes are excluded (as expected)
  • ✅ Routes with API dependencies work correctly during prerendering

This brings TanStack Start's prerendering behavior in line with Next.js where all static routes are automatically prerendered by default.

Testing

The new basic-prerendering e2e test verifies:

  • Static routes are automatically discovered and prerendered
  • Prerendered HTML contains the expected content
  • API data fetching works correctly during prerendering
  • Routes that require external data sources are properly handled

Breaking Changes

None - this change is fully backward compatible. Existing page specifications continue to work exactly as before.

Migration Guide

No migration needed. Users who previously had to manually specify all static routes can now simply use:

tanstackStart({
  prerender: {
    enabled: true,
    // No need to specify pages - all static routes will be discovered automatically
  }
})

Summary by CodeRabbit

  • New Features

    • Prerendering: automatic static-route discovery (merged with configured pages), configurable maxRedirects and failOnError, crawl-linked-pages support, improved redirect handling and tracking of prerendered pages; router plugin now conditionally enables prerendering and a helper to infer full paths is exported.
    • Exposes discovered prerenderable paths globally for tooling.
  • Documentation

    • Expanded prerender guides with Automatic Static Route Discovery, Crawling Links, examples and updated notes.
  • Tests

    • Added prerender test mode, new npm scripts, Playwright helpers and E2E prerender verification tests.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 14, 2025

Walkthrough

Adds automatic static-route discovery via a generator plugin and global, extends prerender config with autoStaticPathsDiscovery, maxRedirects, and failOnError, enhances prerender engine to follow internal redirects and track prerendered pages, exposes inferFullPath, and updates docs, e2e tests, and scripts for a prerender mode.

Changes

Cohort / File(s) Summary
Docs updates
docs/start/framework/react/guide/static-prerendering.md, docs/start/framework/solid/guide/static-prerendering.md
Document new prerender options (autoStaticPathsDiscovery, maxRedirects, failOnError); add Automatic Static Route Discovery and Crawling Links sections; clarify merging behavior with pages.
Core prerender engine
packages/start-plugin-core/src/prerender.ts
Initialize pages to / when none; merge globalThis.TSS_PRERENDABLE_PATHS when autoStaticPathsDiscovery enabled; add prerendered tracking; enhance internal localFetch to follow internal redirects up to maxRedirects (warn on external or exhausted redirects); return prerendered paths.
Schema / types
packages/start-plugin-core/src/schema.ts
Add optional autoStaticPathsDiscovery?: boolean and maxRedirects?: number to prerender schema and input types.
Start-router plugin integration
packages/start-plugin-core/src/start-router-plugin/plugin.ts
Conditionally register prerenderRoutesPlugin() when prerender enabled; make plugin list dynamic.
Generator plugin & globals
packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts, packages/start-plugin-core/src/global.d.ts
New generator plugin computes prerenderable (static, non-dynamic, has component) paths and assigns globalThis.TSS_PRERENDABLE_PATHS; add global declaration for that variable.
Router generator exports
packages/router-generator/src/index.ts
Export inferFullPath from ./utils (public API addition).
E2E: prerender mode support & tests
e2e/react-start/basic/package.json, e2e/react-start/basic/playwright.config.ts, e2e/react-start/basic/vite.config.ts, e2e/react-start/basic/tests/prerendering.spec.ts, e2e/react-start/basic/tests/search-params.spec.ts, e2e/react-start/basic/tests/utils/isPrerender.ts
Add prerender build/test scripts and isPrerender flag; dynamic webServer command by mode; pass prerender config into plugin when prerendering; add/adjust E2E tests verifying prerendered output and content; add dummy server start/stop scripts.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Generator as prerenderRoutesPlugin
  participant Global as globalThis
  participant Core as prerender engine
  participant App as local SSR server
  participant FS as file system

  rect rgb(232,240,255)
    Generator->>Generator: compute prerenderable paths (static, non‑dynamic, has component)
    Generator->>Global: set TSS_PRERENDABLE_PATHS
  end

  rect rgb(240,255,240)
    Core->>Core: init pages ("/" if none)
    alt autoStaticPathsDiscovery = true
      Core->>Global: read TSS_PRERENDABLE_PATHS
      Core->>Core: merge into pages set (dedupe)
    end
  end

  loop for each page
    Core->>App: localFetch(page, maxRedirects)
    alt internal 3xx (<= maxRedirects)
      App-->>Core: 3xx Location: /target
      Core->>App: follow redirect
      App-->>Core: 200 OK
    else external 3xx or redirects exhausted
      Core-->>Core: warn and skip
    end
    Core->>FS: write HTML
    Core->>Core: mark page prerendered
  end

  Core-->>Developer: return prerendered paths
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Focus review on:
    • packages/start-plugin-core/src/prerender.ts (redirect follow logic, localFetch changes, return shape).
    • packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts (filtering and path inference).
    • E2E changes gating on isPrerender and new test scripts.

Possibly related PRs

Suggested reviewers

  • birkskyum

Poem

A rabbit sniffs the route and finds the way,
Gathers static paths at break of day,
Hops links that stay within our site, not sea,
Writes each page and ticks them off with glee,
Hop, build, run — small files for you and me. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix: not all pages are pre-rendered" accurately summarizes the main change in the PR. The issue #5455 reported that static pages not explicitly listed or linked were missing from prerendering output. This PR directly fixes that problem by automatically discovering all eligible static routes through the new autoStaticPathsDiscovery feature. The title is concise, clear, and specific enough that a teammate scanning the history would immediately understand the primary change relates to fixing prerendering coverage.
Linked Issues Check ✅ Passed The PR fully addresses all coding requirements from issue #5455. Automatic discovery of static routes is implemented via the new prerenderRoutesPlugin that inspects route structure and stores eligible paths in TSS_PRERENDABLE_PATHS [prerender-routes-plugin.ts, prerender.ts]. Exclusions are properly applied: layout routes (prefix underscore), dynamic routes (containing $), routes without components, and non-path routes are all filtered out [prerender-routes-plugin.ts]. The crawlLinks behavior for linked dynamic routes is preserved as documented. Configuration is added to schema.ts with autoStaticPathsDiscovery and maxRedirects options. Backward compatibility is maintained: explicit page lists continue to work while discovered paths merge with configured pages when the option is enabled.
Out of Scope Changes Check ✅ Passed All code changes directly relate to implementing the requirements from issue #5455 and the stated PR objectives. Core implementation changes (discovery plugin, schema additions, prerender flow integration) address the core functionality. Documentation updates (react and solid guides) explain the new features and behavior. E2E tests and supporting infrastructure (isPrerender utility, configuration updates, build scripts) are necessary to verify the fix works correctly and represent standard practice for feature PRs. Global variable declarations and utility exports support the discovery mechanism. No changes appear unrelated to the primary objective of automatically discovering and prerendering static routes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud
Copy link

nx-cloud bot commented Oct 14, 2025

View your CI Pipeline Execution ↗ for commit 00a811c

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 4m 22s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1s View ↗

☁️ Nx Cloud last updated this comment at 2025-10-25 23:25:55 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Oct 14, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@5475

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@5475

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@5475

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@5475

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/nitro-v2-vite-plugin@5475

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@5475

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@5475

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-ssr-query@5475

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@5475

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@5475

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@5475

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@5475

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@5475

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@5475

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@5475

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@5475

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@5475

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-ssr-query-core@5475

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@5475

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@5475

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@5475

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@5475

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@5475

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-ssr-query@5475

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@5475

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@5475

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@5475

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@5475

@tanstack/start-plugin-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-plugin-core@5475

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@5475

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-static-server-functions@5475

@tanstack/start-storage-context

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-storage-context@5475

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@5475

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@5475

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@5475

commit: 00a811c

@github-actions github-actions bot added the documentation Everything documentation related label Oct 14, 2025
@FatahChan FatahChan marked this pull request as ready for review October 14, 2025 18:37
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/start-plugin-core/src/prerender.ts (1)

251-259: Retry logic is blocked by the seen set — retries won’t requeue

After a failure, calling addCrawlPageTask(page) is a no-op because seen already contains the path. Remove it from seen (or requeue directly) before retrying.

           await new Promise((resolve) =>
             setTimeout(resolve, prerenderOptions.retryDelay),
           )
           retriesByPath.set(page.path, retries + 1)
-          addCrawlPageTask(page)
+          // Allow requeue on retry
+          seen.delete(page.path)
+          addCrawlPageTask(page)

Alternatively, re-add the task directly to queue without relying on seen.

🧹 Nitpick comments (19)
e2e/react-start/basic-prerendering/src/routes/posts.index.tsx (1)

1-9: LGTM! Clean index route implementation.

The code correctly implements an index route for /posts/ using TanStack Router conventions. The component is appropriately simple for e2e testing with a data-testid attribute for test selection.

Optional refinement: Consider adding an explicit return type annotation to PostsIndexComponent for stricter type safety:

-function PostsIndexComponent() {
+function PostsIndexComponent(): JSX.Element {
   return <div data-testid="PostsIndexComponent">Select a post.</div>
 }

This is a minor enhancement as TypeScript already infers the return type correctly.

e2e/react-start/basic-prerendering/.gitignore (1)

2-3: Include pnpm lockfile ignore for consistency

We’re ignoring package-lock.json and yarn.lock, but not pnpm-lock.yaml. If these fixtures remain package-manager agnostic, please add pnpm-lock.yaml to avoid committing that lockfile accidentally.

e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx (1)

11-13: Consider removing the wrapper component.

PostDeepErrorComponent is a simple pass-through wrapper that doesn't add any value. You can directly use ErrorComponent in the route definition instead.

Apply this diff to simplify the error handling:

-export const Route = createFileRoute('/posts_/$postId/deep')({
-  loader: async ({ params: { postId } }) => fetchPost({ data: postId }),
-  errorComponent: PostDeepErrorComponent,
-  component: PostDeepComponent,
-})
-
-function PostDeepErrorComponent({ error }: ErrorComponentProps) {
-  return <ErrorComponent error={error} />
-}
+export const Route = createFileRoute('/posts_/$postId/deep')({
+  loader: async ({ params: { postId } }) => fetchPost({ data: postId }),
+  errorComponent: ErrorComponent,
+  component: PostDeepComponent,
+})

Remove the unused import if no longer needed:

-import { ErrorComponent, Link, createFileRoute } from '@tanstack/react-router'
-import type { ErrorComponentProps } from '@tanstack/react-router'
+import { ErrorComponent, Link, createFileRoute } from '@tanstack/react-router'
e2e/react-start/basic-prerendering/src/utils/seo.ts (2)

1-11: Add explicit return type annotation for better type safety.

The function lacks an explicit return type, which reduces type safety and makes the API less clear to consumers.

Apply this diff to add an explicit return type:

+type MetaTag = 
+  | { title: string }
+  | { name: string; content: string | undefined }
+
 export const seo = ({
   title,
   description,
   keywords,
   image,
 }: {
   title: string
   description?: string
   image?: string
   keywords?: string
-}) => {
+}): MetaTag[] => {

Note: If you apply the filter suggestion from the previous comment, adjust the type to remove | undefined from content:

type MetaTag = 
  | { title: string }
  | { name: string; content: string }

18-19: Hardcoded Twitter handles may need configuration.

The Twitter creator and site handles are hardcoded as @tannerlinsley. While this might be acceptable for an e2e test scenario, consider whether these should be:

  • Configurable parameters if this utility might be reused
  • Documented as intentionally hardcoded for test purposes

If configurability is needed, consider adding these as optional parameters with defaults:

 export const seo = ({
   title,
   description,
   keywords,
   image,
+  twitterCreator = '@tannerlinsley',
+  twitterSite = '@tannerlinsley',
 }: {
   title: string
   description?: string
   image?: string
   keywords?: string
+  twitterCreator?: string
+  twitterSite?: string
 }) => {
   const tags = [
     { title },
     { name: 'description', content: description },
     { name: 'keywords', content: keywords },
     { name: 'twitter:title', content: title },
     { name: 'twitter:description', content: description },
-    { name: 'twitter:creator', content: '@tannerlinsley' },
-    { name: 'twitter:site', content: '@tannerlinsley' },
+    { name: 'twitter:creator', content: twitterCreator },
+    { name: 'twitter:site', content: twitterSite },
     { name: 'og:type', content: 'website' },
     { name: 'og:title', content: title },
     { name: 'og:description', content: description },
e2e/react-start/basic-prerendering/src/routes/stream.tsx (1)

49-61: Consider using idiomatic JSX children syntax.

While the current children prop approach works with Await, the more idiomatic React pattern places children between JSX tags.

Apply this diff for more idiomatic syntax:

-      <Await
-        promise={promise}
-        children={(promiseData) => (
+      <Await promise={promise}>
+        {(promiseData) => (
           <div className="p-2" data-testid="promise-data">
             {promiseData}
             <div data-testid="stream-data">
               {streamData.map((d) => (
                 <div key={d}>{d}</div>
               ))}
             </div>
           </div>
-        )}
-      />
+        )}
+      </Await>
packages/start-plugin-core/src/schema.ts (1)

189-190: Constrain redirectCount to non-negative integers.

This count represents a discrete redirect limit; allowing floats or negatives introduces ambiguous behavior downstream. Please tighten the schema with .int().min(0) (or .nonnegative()) so only sensible values pass validation.

-        redirectCount: z.number().optional(),
+        redirectCount: z.number().int().min(0).optional(),
e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx (1)

3-8: Simplify type assertion.

The explicit as string type assertion on line 6 is unnecessary since 'world' is already a string literal.

Apply this diff to remove the redundant type assertion:

-    return { hello: 'world' as string }
+    return { hello: 'world' }
e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx (1)

8-15: Clarify fallback behavior for unknown target values.

The switch statement lacks a default case, which means if target is neither 'internal' nor 'external', the loader completes without redirecting and the component renders. While this may be intentional for testing purposes, consider adding a comment to clarify this fallback behavior or explicitly handle unexpected values.

Consider adding either a comment or a default case:

   loader: ({ params: { target }, deps: { externalHost, reloadDocument } }) => {
     switch (target) {
       case 'internal':
         throw redirect({ to: '/posts', reloadDocument })
       case 'external':
         throw redirect({ href: externalHost })
+      // Intentionally allows other target values to render the component
     }
   },

Or with explicit handling:

   loader: ({ params: { target }, deps: { externalHost, reloadDocument } }) => {
     switch (target) {
       case 'internal':
         throw redirect({ to: '/posts', reloadDocument })
       case 'external':
         throw redirect({ href: externalHost })
+      default:
+        // Allow rendering component for testing fallback behavior
+        break
     }
   },
e2e/react-start/basic-prerendering/src/routes/scripts.tsx (1)

5-19: Consider filtering undefined scripts for clarity.

The conditional script inclusion works correctly, but the scripts array will contain undefined in production. While frameworks typically handle this, it's cleaner to filter out undefined values explicitly.

Apply this diff for a cleaner implementation:

 export const Route = createFileRoute('/scripts')({
   head: () => ({
-    scripts: [
-      {
-        src: 'script.js',
-      },
-      isProd
-        ? undefined
-        : {
-            src: 'script2.js',
-          },
-    ],
+    scripts: [
+      {
+        src: 'script.js',
+      },
+      ...(!isProd ? [{ src: 'script2.js' }] : []),
+    ],
   }),
   component: ScriptsComponent,
 })
e2e/react-start/basic-prerendering/src/routes/deferred.tsx (1)

40-55: Consider using JSX children instead of the children prop.

Static analysis tools flag the use of the children prop with Await components. While this works, the canonical React pattern is to use JSX children. Verify whether Await supports JSX children syntax.

If Await supports JSX children, apply this diff:

 <Suspense fallback={<div>Loading person...</div>}>
   <Await
     promise={deferredPerson}
-    children={(data) => (
+  >
+    {(data) => (
       <div data-testid="deferred-person">
         {data.name} - {data.randomNumber}
       </div>
-    )}
+    )}
   />
 </Suspense>
 <Suspense fallback={<div>Loading stuff...</div>}>
   <Await
     promise={deferredStuff}
-    children={(data) => <h3 data-testid="deferred-stuff">{data}</h3>}
+  >
+    {(data) => <h3 data-testid="deferred-stuff">{data}</h3>}
   />
 </Suspense>
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)

5-9: Consider aligning PostType.id with upstream API (number) or coerce to string

JSONPlaceholder returns numeric ids. Either:

  • Change id to number, or
  • Explicitly coerce to string when returning to avoid downstream surprises.
-export type PostType = {
-  id: string
+export type PostType = {
+  id: number
   title: string
   body: string
 }

Or map: return { ...post, id: String(post.id) }.

packages/start-plugin-core/src/prerender.ts (2)

92-105: Redirect handling: parse Location and allow https/ports; avoid fragile string replace

Current checks only handle http://localhost and naive replace. Parse the URL to support https and ports; resolve relative paths cleanly.

-    if (
+    if (
       response.status >= 300 &&
       response.status < 400 &&
       response.headers.get('location') &&
       maxRedirects > 0
     ) {
-      const location = response.headers.get('location')!
-      if (location.startsWith('http://localhost') || location.startsWith('/')) {
-        const newUrl = location.replace('http://localhost', '')
-        return localFetch(newUrl, options, maxRedirects - 1)
-      } else {
-        logger.warn(`Skipping redirect to external location: ${location}`)
-      }
+      const location = response.headers.get('location')!
+      try {
+        const target = new URL(location, url) // resolves relative to current
+        if (target.hostname === 'localhost') {
+          const newPath = `${target.pathname}${target.search}${target.hash}`
+          return localFetch(newPath, options, maxRedirects - 1)
+        }
+        logger.warn(`Skipping redirect to external location: ${target.href}`)
+      } catch {
+        logger.warn(`Invalid redirect location: ${location}`)
+      }
     }

146-146: Reduce log noise when listing pages

Logging the entire pages array can get verbose. Consider logging count and sample instead.

-    logger.info(startConfig.pages)
+    logger.info(`Initial pages to crawl: ${startConfig.pages.length}`)
e2e/react-start/basic-prerendering/stop-dummy-server.mjs (1)

6-8: Consider adding error handling.

The script lacks error handling around e2eStopDummyServer. If the stop operation fails, the error will be unhandled and may not surface clearly in CI logs.

Consider wrapping the call in a try-catch block:

 console.log('Stopping dummy server...')
-await e2eStopDummyServer(packageJson.name)
-console.log('Dummy server stopped successfully!')
+try {
+  await e2eStopDummyServer(packageJson.name)
+  console.log('Dummy server stopped successfully!')
+} catch (error) {
+  console.error('Failed to stop dummy server:', error)
+  process.exit(1)
+}
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)

2-2: Consider using named import for zod.

While this works, the zod documentation recommends using a named import rather than the default export for better tree-shaking and consistency.

-import z from 'zod'
+import { z } from 'zod'
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)

3-3: Improve type safety for children prop.

The children prop is typed as any, which bypasses TypeScript's type checking.

Use React.ReactNode for better type safety:

-export function NotFound({ children }: { children?: any }) {
+export function NotFound({ children }: { children?: React.ReactNode }) {
e2e/react-start/basic-prerendering/vite.config.ts (1)

17-24: Consider exact path matching for the prerender filter.

The current filter uses includes() which performs substring matching. This could potentially exclude unintended routes if a route path contains any of the excluded strings as a substring (e.g., /my-redirect-page would be excluded because it contains /redirect).

If this behavior is intentional for this test suite, the current implementation is fine. Otherwise, consider exact path matching:

         filter: (page) =>
           ![
             '/this-route-does-not-exist',
             '/redirect',
             '/i-do-not-exist',
             '/not-found/via-beforeLoad',
             '/not-found/via-loader',
-          ].some((p) => page.path.includes(p)),
+          ].includes(page.path),
e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx (1)

2-2: Use named import for z
Replace import z from 'zod’ with import { z } from 'zod’ to match the prevalent pattern across the codebase.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 294b82a and cea50c6.

⛔ Files ignored due to path filters (8)
  • e2e/react-start/basic-prerendering/public/android-chrome-192x192.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/android-chrome-512x512.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/apple-touch-icon.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/favicon-16x16.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/favicon-32x32.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/favicon.ico is excluded by !**/*.ico
  • e2e/react-start/basic-prerendering/public/favicon.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (74)
  • docs/start/framework/react/guide/static-prerendering.md (2 hunks)
  • e2e/react-start/basic-prerendering/.gitignore (1 hunks)
  • e2e/react-start/basic-prerendering/.prettierignore (1 hunks)
  • e2e/react-start/basic-prerendering/package.json (1 hunks)
  • e2e/react-start/basic-prerendering/playwright.config.ts (1 hunks)
  • e2e/react-start/basic-prerendering/postcss.config.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/public/script.js (1 hunks)
  • e2e/react-start/basic-prerendering/public/script2.js (1 hunks)
  • e2e/react-start/basic-prerendering/public/site.webmanifest (1 hunks)
  • e2e/react-start/basic-prerendering/server.js (1 hunks)
  • e2e/react-start/basic-prerendering/src/client.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/throwRedirect.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/routeTree.gen.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/router.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/links.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/users.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/server.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/styles/app.css (1 hunks)
  • e2e/react-start/basic-prerendering/src/utils/posts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/utils/seo.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/utils/users.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/start-dummy-server.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/stop-dummy-server.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/tailwind.config.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/tests/prerendering.spec.ts (1 hunks)
  • e2e/react-start/basic-prerendering/tests/setup/global.setup.ts (1 hunks)
  • e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts (1 hunks)
  • e2e/react-start/basic-prerendering/tsconfig.json (1 hunks)
  • e2e/react-start/basic-prerendering/vite.config.ts (1 hunks)
  • packages/start-plugin-core/src/prerender.ts (7 hunks)
  • packages/start-plugin-core/src/schema.ts (1 hunks)
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic-prerendering/tsconfig.json
  • e2e/react-start/basic-prerendering/public/script.js
  • e2e/react-start/basic-prerendering/src/utils/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx
  • e2e/react-start/basic-prerendering/public/site.webmanifest
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-prerendering/src/components/NotFound.tsx
  • e2e/react-start/basic-prerendering/src/styles/app.css
  • e2e/react-start/basic-prerendering/public/script2.js
  • e2e/react-start/basic-prerendering/src/utils/seo.ts
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx
  • e2e/react-start/basic-prerendering/tests/setup/global.setup.ts
  • e2e/react-start/basic-prerendering/tailwind.config.mjs
  • e2e/react-start/basic-prerendering/src/routes/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/tests/prerendering.spec.ts
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx
  • e2e/react-start/basic-prerendering/playwright.config.ts
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-prerendering/start-dummy-server.mjs
  • e2e/react-start/basic-prerendering/src/router.tsx
  • e2e/react-start/basic-prerendering/vite.config.ts
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts
  • e2e/react-start/basic-prerendering/postcss.config.mjs
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-prerendering/src/client.tsx
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-prerendering/stop-dummy-server.mjs
  • e2e/react-start/basic-prerendering/src/utils/posts.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/links.tsx
  • e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx
  • e2e/react-start/basic-prerendering/src/server.ts
  • e2e/react-start/basic-prerendering/src/components/throwRedirect.ts
  • e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts
  • e2e/react-start/basic-prerendering/package.json
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts
  • e2e/react-start/basic-prerendering/server.js
  • e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx
  • e2e/react-start/basic-prerendering/src/routes/index.tsx
  • e2e/react-start/basic-prerendering/src/routeTree.gen.ts
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/static-prerendering.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/static-prerendering.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic-prerendering/src/utils/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-prerendering/src/components/NotFound.tsx
  • e2e/react-start/basic-prerendering/src/utils/seo.ts
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx
  • packages/start-plugin-core/src/schema.ts
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx
  • e2e/react-start/basic-prerendering/tests/setup/global.setup.ts
  • e2e/react-start/basic-prerendering/src/routes/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/tests/prerendering.spec.ts
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx
  • e2e/react-start/basic-prerendering/playwright.config.ts
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/router.tsx
  • e2e/react-start/basic-prerendering/vite.config.ts
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-prerendering/src/client.tsx
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-prerendering/src/utils/posts.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/links.tsx
  • e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx
  • e2e/react-start/basic-prerendering/src/server.ts
  • e2e/react-start/basic-prerendering/src/components/throwRedirect.ts
  • e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts
  • packages/start-plugin-core/src/prerender.ts
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts
  • e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx
  • e2e/react-start/basic-prerendering/src/routes/index.tsx
  • e2e/react-start/basic-prerendering/src/routeTree.gen.ts
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/links.tsx
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx
  • e2e/react-start/basic-prerendering/src/routes/index.tsx
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/src/prerender.ts
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use workspace:* protocol for internal dependencies in package.json files

Files:

  • e2e/react-start/basic-prerendering/package.json
🧠 Learnings (1)
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript in strict mode with extensive type safety across the codebase

Applied to files:

  • e2e/react-start/basic-prerendering/tsconfig.json
🧬 Code graph analysis (51)
e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx (2)
e2e/react-start/basic-prerendering/src/routes/deferred.tsx (1)
  • Route (18-29)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx (3)
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx (1)
e2e/react-start/basic-prerendering/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx (2)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx (1)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (5)
e2e/react-start/basic-cloudflare/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/server-routes/src/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-basic-cloudflare/src/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-basic-cloudflare/src/routes/__root.tsx (1)
  • NotFound (61-61)
e2e/react-start/basic-cloudflare/src/routes/__root.tsx (1)
  • NotFound (55-55)
e2e/react-start/basic-prerendering/src/utils/seo.ts (1)
scripts/llms-generate.mjs (1)
  • title (96-96)
e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx (1)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
  • fetchPost (17-33)
e2e/react-start/basic-prerendering/src/routes/posts.tsx (1)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
  • fetchPosts (35-42)
e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/tests/setup/global.setup.ts (8)
e2e/react-router/basic-file-based-code-splitting/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/solid-router/basic-file-based/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/basic-virtual-file-based/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/basic-virtual-named-export-config-file-based/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/rspack-basic-virtual-named-export-config-file-based/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/basic/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/basic-esbuild-file-based/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-router/basic-file-based/tests/setup/global.setup.ts (1)
  • setup (4-6)
e2e/react-start/basic-prerendering/src/routes/users.tsx (3)
e2e/react-start/basic-prerendering/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
examples/solid/start-basic/src/routes/users.tsx (1)
  • UsersComponent (18-48)
e2e/react-start/basic-prerendering/src/routes/posts.index.tsx (1)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/stream.tsx (1)
e2e/react-start/basic-prerendering/src/routes/deferred.tsx (1)
  • Route (18-29)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (4)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/scripts.tsx (2)
e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx (1)
  • Route (3-18)
packages/router-core/src/ssr/tsrScript.ts (1)
  • p (7-9)
e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/playwright.config.ts (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx (1)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (3)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (3)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/start-dummy-server.mjs (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/basic-prerendering/src/router.tsx (2)
e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx (2)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
  • fetchPost (17-33)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (4)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (3)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (2)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts (2)
e2e/solid-router/basic-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/react-router/generator-cli-only/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1)
e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx (1)
  • RedirectOnClick (10-26)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx (3)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (3)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx (1)
packages/router-core/src/ssr/tsrScript.ts (1)
  • p (7-9)
e2e/react-start/basic-prerendering/stop-dummy-server.mjs (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
e2e/react-router/js-only-file-based/src/posts.js (1)
  • queryURL (5-5)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (3)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/users.index.tsx (3)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/index.tsx (1)
  • Route (4-6)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (3)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx (3)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/links.tsx (3)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-prerendering/src/routes/index.tsx (1)
  • Route (4-6)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx (1)
e2e/react-start/basic-prerendering/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (2)
e2e/react-start/basic-prerendering/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
packages/start-plugin-core/src/prerender.ts (1)
packages/router-generator/src/generator.ts (1)
  • getCrawlingResult (1183-1186)
e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/api.users.ts (2)
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx (3)
e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
examples/react/start-basic-cloudflare/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
packages/react-router/src/CatchBoundary.tsx (2)
  • CatchBoundaryImpl (31-78)
  • CatchBoundary (5-29)
e2e/react-start/basic-prerendering/src/routes/deferred.tsx (1)
e2e/react-start/basic/src/routes/deferred.tsx (1)
  • deferredStuff (19-27)
e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx (2)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-router/basic-file-based/src/routes/search-params/default.tsx (1)
  • search (18-27)
e2e/react-start/basic-prerendering/src/routes/index.tsx (1)
e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx (1)
  • CustomMessage (3-10)
e2e/react-start/basic-prerendering/src/routeTree.gen.ts (2)
e2e/react-router/js-only-file-based/src/routeTree.gen.js (3)
  • IndexRoute (30-34)
  • PostsIndexRoute (35-39)
  • PostsPostIdRoute (40-44)
e2e/react-start/basic-prerendering/src/router.tsx (1)
  • getRouter (6-16)
🪛 Biome (2.1.2)
e2e/react-start/basic-prerendering/src/routes/stream.tsx

[error] 51-51: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/basic-prerendering/src/routes/deferred.tsx

[error] 43-43: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 53-53: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~12-~12: There might be a mistake here.
Context: ... parameters (e.g., /about, /contact) - Routes with search parameters that have ...

(QB_NEW_EN)


[grammar] ~13-~13: There might be a mistake here.
Context: ...arch parameters that have default values - Routes that don't require dynamic data f...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

🧹 Nitpick comments (31)
e2e/react-start/basic-prerendering/.prettierignore (1)

1-4: Looks good. Optional: ignore Playwright artifacts too.

Current ignores are sensible for build and generated files. Consider also ignoring Playwright outputs to avoid formatting large reports.

 **/build
 **/public
 pnpm-lock.yaml
 routeTree.gen.ts
+playwright-report
+test-results
e2e/react-start/basic-prerendering/src/server.ts (1)

7-11: Add explicit return type for improved type safety.

The fetch method lacks an explicit return type annotation. To align with the strict TypeScript coding guidelines, add the return type.

As per coding guidelines.

Apply this diff to add the return type:

 export default {
-  fetch(request: Request) {
+  fetch(request: Request): ReturnType<typeof handler.fetch> {
     return handler.fetch(request)
   },
 }
e2e/react-start/basic-prerendering/.gitignore (3)

1-4: Include pnpm lockfile (repo uses pnpm).

To keep lockfile policy consistent, ignore pnpm-lock.yaml alongside npm/yarn locks (or decide to commit locks). Given pnpm usage in this PR, add:

 node_modules
 package-lock.json
 yarn.lock
+pnpm-lock.yaml

6-8: Broaden env ignores to prevent secret leaks.

Ignore all dotenv variants and keep an allowlisted example file.

 .DS_Store
 .cache
-.env
+.env
+.env.*
+!.env.example
 .vercel
 .output
@@
 # Sentry Config File
 .env.sentry-build-plugin

Also applies to: 16-16


11-20: Add common logs/caches and coverage artifacts.

These are frequently generated during builds/tests and can clutter diffs.

 /build/
 /api/
 /server/build
 /public/build
 # Sentry Config File
 .env.sentry-build-plugin
 /test-results/
 /playwright-report/
 /blob-report/
 /playwright/.cache/
+coverage/
+.nyc_output/
+*.tsbuildinfo
+npm-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+node_modules/.vite/
+.vite/
+.vscode/
+.idea/
+Thumbs.db
e2e/react-start/basic-prerendering/src/routes/api.users.ts (1)

7-11: Prefer const and validate VITE_EXTERNAL_PORT in test mode.

The queryURL variable is declared with let but could be const with a conditional expression. Additionally, VITE_EXTERNAL_PORT is used without validation, which could result in an invalid URL if the environment variable is undefined or malformed in test mode.

Apply this diff to use const and add validation:

-let queryURL = 'https://jsonplaceholder.typicode.com'
-
-if (import.meta.env.VITE_NODE_ENV === 'test') {
-  queryURL = `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT}`
-}
+const queryURL =
+  import.meta.env.VITE_NODE_ENV === 'test'
+    ? `http://localhost:${import.meta.env.VITE_EXTERNAL_PORT || '3000'}`
+    : 'https://jsonplaceholder.typicode.com'
packages/start-plugin-core/src/schema.ts (1)

189-189: Consider adding a minimum constraint to redirectCount.

The redirectCount field accepts any number, including negative values. To prevent unexpected behavior, consider adding a minimum constraint.

Apply this diff to add a minimum constraint:

-        redirectCount: z.number().optional(),
+        redirectCount: z.number().min(0).optional(),
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)

1-9: Consider renaming to .ts extension.

The file contains only type definitions and constants, with no JSX. Renaming from .tsx to .ts would align with the actual content.

e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx (1)

3-8: Consider removing the redundant type assertion.

The type assertion 'world' as string is unnecessary since 'world' is already inferred as a string literal type.

Apply this diff:

-    return { hello: 'world' as string }
+    return { hello: 'world' }
e2e/react-start/basic-prerendering/tsconfig.json (1)

3-21: Add Vite types for import.meta.env

You reference import.meta.env in TS (e.g., API routes). Add vite/client to types so TS knows ImportMetaEnv.

Apply this diff:

   "compilerOptions": {
     "strict": true,
     "esModuleInterop": true,
     "jsx": "react-jsx",
     "module": "ESNext",
     "moduleResolution": "Bundler",
     "lib": ["DOM", "DOM.Iterable", "ES2022"],
+    "types": ["vite/client"],
     "isolatedModules": true,
     "resolveJsonModule": true,
     "skipLibCheck": true,
     "target": "ES2022",
     "allowJs": true,
     "forceConsistentCasingInFileNames": true,
     "baseUrl": ".",
     "paths": {
       "~/*": ["./src/*"]
     },
     "noEmit": true
   }
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1)

15-28: Harden the handler (param validation + timeout)

Optional but useful for tests and robustness:

  • Validate params.id is a positive integer; return 400 on invalid.
  • Add a request timeout to avoid hanging on slow upstreams.

Example diff:

-      GET: async ({ request, params }) => {
+      GET: async ({ request, params }) => {
         console.info(`Fetching users by id=${params.id}... @`, request.url)
         try {
-          const res = await axios.get<User>(`${queryURL}/users/` + params.id)
+          const idNum = Number(params.id)
+          if (!Number.isInteger(idNum) || idNum <= 0) {
+            return json({ error: 'Invalid id' }, { status: 400 })
+          }
+          const res = await axios.get<User>(`${queryURL}/users/${idNum}`, {
+            timeout: 5000,
+          })
e2e/react-start/basic-prerendering/src/routes/deferred.tsx (1)

40-55: Refactor to use JSX children pattern instead of children prop.

The Await components use the children prop, which is not the canonical React pattern and is flagged by the linter.

Apply this diff to use JSX children:

       <Suspense fallback={<div>Loading person...</div>}>
         <Await
           promise={deferredPerson}
-          children={(data) => (
+        >
+          {(data) => (
             <div data-testid="deferred-person">
               {data.name} - {data.randomNumber}
             </div>
-          )}
+          )}
-        />
+        </Await>
       </Suspense>
       <Suspense fallback={<div>Loading stuff...</div>}>
         <Await
           promise={deferredStuff}
-          children={(data) => <h3 data-testid="deferred-stuff">{data}</h3>}
+        >
+          {(data) => <h3 data-testid="deferred-stuff">{data}</h3>}
-        />
+        </Await>
       </Suspense>
e2e/react-start/basic-prerendering/src/routes/stream.tsx (2)

30-45: Add stream to useEffect dependency array.

The stream object is referenced inside the effect but not included in the dependency array. While the stream reference is likely stable from the loader, explicitly including it follows React Hook best practices and avoids potential stale closure issues.

Apply this diff:

     fetchStream()
-  }, [])
+  }, [stream])

47-63: Refactor to use JSX children pattern instead of children prop.

The Await component uses the children prop, which is not the canonical React pattern and is flagged by the linter.

Apply this diff:

       <Await
         promise={promise}
-        children={(promiseData) => (
+      >
+        {(promiseData) => (
           <div className="p-2" data-testid="promise-data">
             {promiseData}
             <div data-testid="stream-data">
               {streamData.map((d) => (
                 <div key={d}>{d}</div>
               ))}
             </div>
           </div>
-        )}
+        )}
-      />
+      </Await>
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)

3-3: Replace any type with React.ReactNode for strict type safety.

The children prop is typed as any, which bypasses TypeScript's type checking. Use React.ReactNode for proper type safety.

As per coding guidelines

Apply this diff:

-export function NotFound({ children }: { children?: any }) {
+export function NotFound({ children }: { children?: React.ReactNode }) {
e2e/react-start/basic-prerendering/tests/setup/global.setup.ts (1)

2-2: Consider using import attributes syntax.

The with { type: 'json' } syntax is from an older stage-3 proposal. Node.js v20.10+ and TypeScript 5.3+ support the import attributes form, which is now the standardized syntax.

Apply this diff if your environment supports it:

-import packageJson from '../../package.json' with { type: 'json' }
+import packageJson from '../../package.json' with { type: 'json' }

Note: If the project is targeting older Node versions, the current syntax is acceptable.

e2e/react-start/basic-prerendering/src/router.tsx (1)

6-16: Consider consistent component assignment.

The router configuration is correct. For consistency with defaultErrorComponent, you could simplify defaultNotFoundComponent.

Apply this diff for consistency:

 export function getRouter() {
   const router = createRouter({
     routeTree,
     scrollRestoration: true,
     defaultPreload: 'intent',
     defaultErrorComponent: DefaultCatchBoundary,
-    defaultNotFoundComponent: () => <NotFound />,
+    defaultNotFoundComponent: NotFound,
   })

   return router
 }

Note: Both forms are valid; this is purely a stylistic suggestion.

e2e/react-start/basic-prerendering/stop-dummy-server.mjs (2)

4-4: Consider using import attributes syntax.

The with { type: 'json' } syntax is from an older stage-3 proposal. The standardized import attributes form is now supported in Node.js v20.10+ and TypeScript 5.3+.

Apply this diff if targeting modern Node versions:

-import packageJson from './package.json' with { type: 'json' }
+import packageJson from './package.json' with { type: 'json' }

Note: The current syntax works but may be deprecated in future Node versions.


6-8: Add error handling for robustness.

The script lacks error handling. If e2eStopDummyServer fails, users won't see a clear error message, and the exit code may not reflect the failure.

Apply this diff to add proper error handling:

 console.log('Stopping dummy server...')
-await e2eStopDummyServer(packageJson.name)
-console.log('Dummy server stopped successfully!')
+try {
+  await e2eStopDummyServer(packageJson.name)
+  console.log('Dummy server stopped successfully!')
+} catch (error) {
+  console.error('Failed to stop dummy server:', error)
+  process.exit(1)
+}
e2e/react-start/basic-prerendering/src/routes/users.tsx (1)

7-14: Simplify loader for clarity

Avoid mixing await with .then/.catch. Use try/catch with a single await.

-  loader: async () => {
-    return await axios
-      .get<Array<User>>('/api/users')
-      .then((r) => r.data)
-      .catch(() => {
-        throw new Error('Failed to fetch users')
-      })
-  },
+  loader: async () => {
+    try {
+      const r = await axios.get<Array<User>>('/api/users')
+      return r.data
+    } catch {
+      throw new Error('Failed to fetch users')
+    }
+  },
e2e/react-start/basic-prerendering/playwright.config.ts (1)

12-12: Improve error handling and cross-platform compatibility in the command chain.

The ssrModeCommand has two concerns:

  1. No error propagation: If pnpm build fails, the subsequent commands still execute. The chain should abort on failure.
  2. Unix-only syntax: The && operator works on Unix/macOS but may behave differently on Windows in some shells.

Consider using a dedicated script (e.g., scripts/e2e-setup.sh or .mjs) that provides explicit error handling and cross-platform compatibility:

// scripts/e2e-setup.mjs
import { execSync } from 'child_process'

const commands = [
  'pnpm test:e2e:dummyServer',
  'pnpm build',
  'pnpm test:e2e:stopDummyServer',
  'pnpm start'
]

for (const cmd of commands) {
  try {
    execSync(cmd, { stdio: 'inherit' })
  } catch (error) {
    console.error(`Command failed: ${cmd}`)
    process.exit(1)
  }
}

Then update the config:

-const ssrModeCommand = `pnpm test:e2e:dummyServer && pnpm build && pnpm test:e2e:stopDummyServer && pnpm start`
+const ssrModeCommand = 'node scripts/e2e-setup.mjs'
e2e/react-start/basic-prerendering/package.json (1)

12-14: Replace Unix-specific commands for cross-platform compatibility.

The test scripts use Unix-specific syntax (& for backgrounding and rm -rf) that won't work on Windows without WSL or Git Bash.

Consider using cross-platform alternatives:

  1. For backgrounding processes, use a Node.js script or a tool like concurrently
  2. For file deletion, use a cross-platform utility like rimraf or del-cli

Example refactor using Node scripts:

   "scripts": {
-    "test:e2e:dummyServer": "node start-dummy-server.mjs &",
+    "test:e2e:dummyServer": "node start-dummy-server.mjs",
     "test:e2e:stopDummyServer": "node stop-dummy-server.mjs",
-    "test:e2e": "rm -rf port*.txt;  pnpm run test:e2e:ssrMode"
+    "test:e2e": "node -e \"require('fs').readdirSync('.').filter(f=>f.match(/^port.*\\.txt$/)).forEach(f=>require('fs').unlinkSync(f))\" && pnpm run test:e2e:ssrMode"
   },

Or add rimraf as a devDependency and use:

-    "test:e2e": "rm -rf port*.txt;  pnpm run test:e2e:ssrMode"
+    "test:e2e": "rimraf port*.txt && pnpm run test:e2e:ssrMode"
e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx (1)

13-17: Consider making externalHost conditionally required based on the redirect target.

The externalHost search parameter is optional, but child routes (e.g., via-beforeLoad.tsx) require it when target is 'external'. This creates a validation gap where the route can be accessed with invalid combinations.

Zod doesn't natively support conditional validation across separate schema fields in this context, but you can add runtime validation in child route beforeLoad hooks or document the requirement clearly. Alternatively, split into separate routes:

// Option 1: Document the requirement
export const Route = createFileRoute('/redirect/$target')({
  // ... existing code
  validateSearch: z.object({
    reloadDocument: z.boolean().optional(),
    preload: z.literal(false).optional(),
    // externalHost is required when target is 'external'
    externalHost: z.string().optional(),
  }),

Or implement validation in each child route as suggested for via-beforeLoad.tsx.

docs/start/framework/react/guide/static-prerendering.md (1)

18-21: Remove redundant explanation.

Line 20 duplicates the automatic discovery explanation already provided in lines 10-16. The Manual Page Configuration section should focus on when and how to manually configure pages.

Consider this revision:

 ## Manual Page Configuration
 
-TanStack Start can prerender your application to static HTML files, which can then be served to users without having to generate them on the fly. When prerendering is enabled without specifying pages, TanStack Start will automatically discover and prerender all static routes in your application.
+You can manually specify which pages to prerender using the `pages` array. This is useful when you need custom output paths or want to override the automatic discovery behavior.
 
 To prerender your application, you can add the `prerender` option to your tanstackStart configuration in `vite.config.ts` file:
e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx (1)

17-21: Avoid unhandled promise from useServerFn; explicitly ignore or handle it

onClick returns a Promise that isn’t awaited/caught. Use void to signal intentional fire-and-forget (prevents no-floating-promises).

-      onClick={() =>
-        execute({ data: { target, reloadDocument, externalHost } })
-      }
+      onClick={() =>
+        void execute({ data: { target, reloadDocument, externalHost } })
+      }
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx (1)

6-8: Optional: improve readability by hoisting params

Slightly clearer to read and avoids calling the hook inline in JSX.

-        redirect test with server functions (target {Route.useParams().target})
+        {(() => {
+          const { target } = Route.useParams()
+          return <>redirect test with server functions (target {target})</>
+        })()}
e2e/react-start/basic-prerendering/server.js (2)

16-16: Prefer absolute paths for static assets

Avoid CWD sensitivity by resolving to absolute paths.

-  app.use(express.static('./dist/client'))
+  app.use(express.static(path.resolve('./dist/client')))

Repeat the same change for the other static middleware.

Also applies to: 48-48


57-61: Add startup error handling to avoid unhandled rejections

If dynamic import or binding fails, the promise rejects and nothing logs.

-createSpaServer().then(async ({ app }) =>
-  app.listen(port, () => {
-    console.info(`Client Server: http://localhost:${port}`)
-  }),
-)
+createSpaServer()
+  .then(({ app }) => {
+    app.listen(port, () => {
+      console.info(`Client Server: http://localhost:${port}`)
+    })
+  })
+  .catch((err) => {
+    console.error('Failed to start SPA server', err)
+    process.exitCode = 1
+  })
 
-createStartServer().then(async ({ app }) =>
-  app.listen(startPort, () => {
-    console.info(`Start Server: http://localhost:${startPort}`)
-  }),
-)
+createStartServer()
+  .then(({ app }) => {
+    app.listen(startPort, () => {
+      console.info(`Start Server: http://localhost:${startPort}`)
+    })
+  })
+  .catch((err) => {
+    console.error('Failed to start Start server', err)
+    process.exitCode = 1
+  })

Also applies to: 63-67

e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1)

3-15: Route always notFound — consider removing unused component

Since loader throws notFound unconditionally, the route component won’t render. If not needed for tests, drop the component to reduce dead code; otherwise, add a short comment clarifying the intent.

e2e/react-start/basic-prerendering/src/routes/__root.tsx (2)

95-101: Add html lang attribute for accessibility/SEO

Recommend (or appropriate locale).

-  return (
-    <html>
+  return (
+    <html lang="en">

26-31: Use standard theme color meta; remove non-standard 'color' on manifest link

'color' isn’t a standard attribute for rel="manifest". Prefer a meta tag for theme color.

       ...seo({
         title:
           'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework',
         description: `TanStack Start is a type-safe, client-first, full-stack React framework. `,
       }),
+      { name: 'theme-color', content: '#ffffff' },
     ],
-      { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' },
+      { rel: 'manifest', href: '/site.webmanifest' },

Also applies to: 51-53

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 294b82a and 773c94b.

⛔ Files ignored due to path filters (8)
  • e2e/react-start/basic-prerendering/public/android-chrome-192x192.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/android-chrome-512x512.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/apple-touch-icon.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/favicon-16x16.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/favicon-32x32.png is excluded by !**/*.png
  • e2e/react-start/basic-prerendering/public/favicon.ico is excluded by !**/*.ico
  • e2e/react-start/basic-prerendering/public/favicon.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (74)
  • docs/start/framework/react/guide/static-prerendering.md (2 hunks)
  • e2e/react-start/basic-prerendering/.gitignore (1 hunks)
  • e2e/react-start/basic-prerendering/.prettierignore (1 hunks)
  • e2e/react-start/basic-prerendering/package.json (1 hunks)
  • e2e/react-start/basic-prerendering/playwright.config.ts (1 hunks)
  • e2e/react-start/basic-prerendering/postcss.config.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/public/script.js (1 hunks)
  • e2e/react-start/basic-prerendering/public/script2.js (1 hunks)
  • e2e/react-start/basic-prerendering/public/site.webmanifest (1 hunks)
  • e2e/react-start/basic-prerendering/server.js (1 hunks)
  • e2e/react-start/basic-prerendering/src/client.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/components/throwRedirect.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/routeTree.gen.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/router.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/links.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/users.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/server.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/styles/app.css (1 hunks)
  • e2e/react-start/basic-prerendering/src/utils/posts.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/src/utils/seo.ts (1 hunks)
  • e2e/react-start/basic-prerendering/src/utils/users.tsx (1 hunks)
  • e2e/react-start/basic-prerendering/start-dummy-server.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/stop-dummy-server.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/tailwind.config.mjs (1 hunks)
  • e2e/react-start/basic-prerendering/tests/prerendering.spec.ts (1 hunks)
  • e2e/react-start/basic-prerendering/tests/setup/global.setup.ts (1 hunks)
  • e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts (1 hunks)
  • e2e/react-start/basic-prerendering/tsconfig.json (1 hunks)
  • e2e/react-start/basic-prerendering/vite.config.ts (1 hunks)
  • packages/start-plugin-core/src/prerender.ts (8 hunks)
  • packages/start-plugin-core/src/schema.ts (1 hunks)
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx
  • packages/start-plugin-core/src/prerender.ts
  • e2e/react-start/basic-prerendering/src/client.tsx
  • e2e/react-start/basic-prerendering/src/server.ts
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • e2e/react-start/basic-prerendering/tests/setup/global.setup.ts
  • e2e/react-start/basic-prerendering/src/components/NotFound.tsx
  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx
  • e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx
  • packages/start-plugin-core/src/schema.ts
  • e2e/react-start/basic-prerendering/src/utils/users.tsx
  • e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx
  • e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx
  • e2e/react-start/basic-prerendering/src/utils/posts.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/components/throwRedirect.ts
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx
  • e2e/react-start/basic-prerendering/tests/prerendering.spec.ts
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-prerendering/src/routes/links.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-prerendering/vite.config.ts
  • e2e/react-start/basic-prerendering/src/utils/seo.ts
  • e2e/react-start/basic-prerendering/src/routes/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routeTree.gen.ts
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx
  • e2e/react-start/basic-prerendering/src/router.tsx
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx
  • e2e/react-start/basic-prerendering/playwright.config.ts
  • e2e/react-start/basic-prerendering/src/routes/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts
**/src/routes/**

📄 CodeRabbit inference engine (AGENTS.md)

Place file-based routes under src/routes/ directories

Files:

  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-prerendering/src/routes/links.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-prerendering/src/routes/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx
  • e2e/react-start/basic-prerendering/start-dummy-server.mjs
  • e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx
  • e2e/react-start/basic-prerendering/src/client.tsx
  • e2e/react-start/basic-prerendering/src/server.ts
  • e2e/react-start/basic-prerendering/public/site.webmanifest
  • e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx
  • e2e/react-start/basic-prerendering/stop-dummy-server.mjs
  • e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx
  • e2e/react-start/basic-prerendering/package.json
  • e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx
  • e2e/react-start/basic-prerendering/tailwind.config.mjs
  • e2e/react-start/basic-prerendering/src/routes/search-params/default.tsx
  • e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx
  • e2e/react-start/basic-prerendering/tests/setup/global.setup.ts
  • e2e/react-start/basic-prerendering/src/components/NotFound.tsx
  • e2e/react-start/basic-prerendering/public/script2.js
  • e2e/react-start/basic-prerendering/src/routes/대한민국.tsx
  • e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.tsx
  • e2e/react-start/basic-prerendering/src/utils/users.tsx
  • e2e/react-start/basic-prerendering/src/styles/app.css
  • e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx
  • e2e/react-start/basic-prerendering/src/routes/deferred.tsx
  • e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout.tsx
  • e2e/react-start/basic-prerendering/src/utils/posts.tsx
  • e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx
  • e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx
  • e2e/react-start/basic-prerendering/server.js
  • e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts
  • e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/components/throwRedirect.ts
  • e2e/react-start/basic-prerendering/src/routes/__root.tsx
  • e2e/react-start/basic-prerendering/postcss.config.mjs
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts
  • e2e/react-start/basic-prerendering/src/routes/stream.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx
  • e2e/react-start/basic-prerendering/public/script.js
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-loader.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.index.tsx
  • e2e/react-start/basic-prerendering/tests/prerendering.spec.ts
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx
  • e2e/react-start/basic-prerendering/src/routes/links.tsx
  • e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx
  • e2e/react-start/basic-prerendering/vite.config.ts
  • e2e/react-start/basic-prerendering/tsconfig.json
  • e2e/react-start/basic-prerendering/src/utils/seo.ts
  • e2e/react-start/basic-prerendering/src/routes/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx
  • e2e/react-start/basic-prerendering/src/routeTree.gen.ts
  • e2e/react-start/basic-prerendering/src/routes/posts.index.tsx
  • e2e/react-start/basic-prerendering/src/router.tsx
  • e2e/react-start/basic-prerendering/src/routes/scripts.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx
  • e2e/react-start/basic-prerendering/src/routes/redirect/$target.tsx
  • e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx
  • e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx
  • e2e/react-start/basic-prerendering/playwright.config.ts
  • e2e/react-start/basic-prerendering/src/routes/users.tsx
  • e2e/react-start/basic-prerendering/src/routes/api.users.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/prerender.ts
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/src/schema.ts
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

Use workspace:* protocol for internal dependencies in package.json files

Files:

  • e2e/react-start/basic-prerendering/package.json
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/static-prerendering.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/static-prerendering.md
🧠 Learnings (2)
📚 Learning: 2025-09-23T17:36:12.598Z
Learnt from: CR
PR: TanStack/router#0
File: AGENTS.md:0-0
Timestamp: 2025-09-23T17:36:12.598Z
Learning: Applies to **/*.{ts,tsx} : Use TypeScript in strict mode with extensive type safety across the codebase

Applied to files:

  • e2e/react-start/basic-prerendering/tsconfig.json
📚 Learning: 2025-10-01T18:31:35.420Z
Learnt from: schiller-manuel
PR: TanStack/router#5330
File: e2e/react-start/custom-basepath/src/routeTree.gen.ts:58-61
Timestamp: 2025-10-01T18:31:35.420Z
Learning: Do not review files named `routeTree.gen.ts` in TanStack Router repositories, as these are autogenerated files that should not be manually modified.

Applied to files:

  • e2e/react-start/basic-prerendering/src/routeTree.gen.ts
🧬 Code graph analysis (48)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-beforeLoad.tsx (1)
e2e/react-start/basic-prerendering/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/index.tsx (2)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic/src/routes/redirect/$target/index.tsx (1)
  • preload (4-75)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (3)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (3)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/index.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/start-dummy-server.mjs (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx (2)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
  • Route (16-75)
packages/router-core/src/ssr/tsrScript.ts (1)
  • p (7-9)
packages/start-plugin-core/src/prerender.ts (1)
packages/router-generator/src/generator.ts (1)
  • getCrawlingResult (1183-1186)
e2e/react-start/basic-prerendering/src/routes/search-params/loader-throws-redirect.tsx (2)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/stop-dummy-server.mjs (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (3)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/redirect/index.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (3)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx (1)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/tests/setup/global.setup.ts (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (6)
e2e/react-start/basic-cloudflare/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/server-routes/src/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-basic-cloudflare/src/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-basic-cloudflare/src/routes/__root.tsx (1)
  • NotFound (61-61)
e2e/react-start/basic-cloudflare/src/routes/__root.tsx (1)
  • NotFound (55-55)
examples/react/start-basic-cloudflare/src/router.tsx (1)
  • NotFound (11-11)
e2e/react-start/basic-prerendering/src/routes/대한민국.tsx (1)
e2e/react-router/basic-file-based/src/routes/대한민국.tsx (1)
  • RouteComponent (7-9)
e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx (1)
e2e/react-start/basic-prerendering/src/components/throwRedirect.ts (1)
  • throwRedirect (4-23)
e2e/react-start/basic-prerendering/src/routes/posts.tsx (1)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
  • fetchPosts (35-42)
e2e/react-start/basic-prerendering/src/routes/posts.$postId.tsx (2)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
  • fetchPost (17-33)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-prerendering/src/routes/deferred.tsx (2)
examples/solid/start-basic/src/routes/deferred.tsx (1)
  • Deferred (31-62)
e2e/react-start/basic/src/routes/deferred.tsx (1)
  • deferredStuff (19-27)
e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx (1)
e2e/react-start/server-routes/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (2)
e2e/react-router/basic-esbuild-file-based/src/routes/_layout.tsx (1)
  • LayoutComponent (7-16)
e2e/react-router/basic-file-based-code-splitting/src/routes/_layout.tsx (1)
  • LayoutComponent (7-16)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
e2e/react-router/js-only-file-based/src/posts.js (1)
  • queryURL (5-5)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here.tsx (1)
e2e/react-start/basic-prerendering/src/routes/foo/$bar/$qux/_here/index.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/posts_.$postId.deep.tsx (1)
e2e/react-start/basic-prerendering/src/utils/posts.tsx (1)
  • fetchPost (17-33)
e2e/react-start/basic-prerendering/tests/setup/global.teardown.ts (2)
e2e/solid-router/basic-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/solid-router/basic-virtual-file-based/tests/setup/global.teardown.ts (1)
  • teardown (4-6)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (3)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-loader.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
e2e/react-start/server-routes/src/routes/__root.tsx (1)
  • RootDocument (57-82)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-loader.tsx (3)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (3)
e2e/react-router/js-only-file-based/src/posts.js (1)
  • queryURL (5-5)
e2e/react-start/basic-prerendering/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
e2e/react-start/basic-prerendering/src/routes/stream.tsx (1)
e2e/react-start/basic/src/routes/stream.tsx (1)
  • Home (26-64)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/serverFn/via-useServerFn.tsx (1)
e2e/react-start/basic-prerendering/src/components/RedirectOnClick.tsx (1)
  • RedirectOnClick (10-26)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (4)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-b.tsx (3)
e2e/react-start/basic-prerendering/src/routes/_layout.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/src/routes/_layout/_layout-2/layout-a.tsx (1)
  • Route (3-5)
e2e/react-start/basic-prerendering/vite.config.ts (1)
packages/router-core/src/ssr/tsrScript.ts (1)
  • p (7-9)
e2e/react-start/basic-prerendering/src/routes/index.tsx (1)
e2e/react-start/basic-prerendering/src/components/CustomMessage.tsx (1)
  • CustomMessage (3-10)
e2e/react-start/basic-prerendering/src/routes/redirect/$target/via-beforeLoad.tsx (2)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/not-found/index.tsx (1)
  • Route (3-31)
e2e/react-start/basic-prerendering/src/routeTree.gen.ts (2)
e2e/react-router/js-only-file-based/src/routeTree.gen.js (4)
  • IndexRoute (30-34)
  • PostsIndexRoute (35-39)
  • PostsPostIdRoute (40-44)
  • rootRouteChildren (90-94)
e2e/react-start/basic-prerendering/src/router.tsx (1)
  • getRouter (6-16)
e2e/react-start/basic-prerendering/src/routes/posts.index.tsx (1)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/router.tsx (2)
e2e/react-start/basic-prerendering/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-prerendering/src/routes/scripts.tsx (2)
e2e/react-start/basic-prerendering/src/routes/__root.tsx (1)
  • Route (16-75)
e2e/react-start/basic-prerendering/src/routes/inline-scripts.tsx (1)
  • Route (3-18)
e2e/react-start/basic-prerendering/src/routes/search-params/route.tsx (2)
e2e/react-start/basic-prerendering/src/routes/not-found/route.tsx (1)
  • Route (4-8)
e2e/react-start/basic-prerendering/src/routes/not-found/via-beforeLoad.tsx (1)
  • Route (3-15)
e2e/react-start/basic-prerendering/src/routes/search-params/index.tsx (2)
e2e/react-start/basic-prerendering/src/routes/index.tsx (1)
  • Route (4-6)
e2e/react-start/basic-prerendering/src/routes/links.tsx (1)
  • Route (3-47)
e2e/react-start/basic-prerendering/src/routes/users.$userId.tsx (4)
e2e/react-start/basic-prerendering/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
e2e/react-start/basic-prerendering/src/components/NotFound.tsx (1)
  • NotFound (3-25)
e2e/react-start/basic-prerendering/playwright.config.ts (1)
scripts/set-ts-version.js (1)
  • packageJson (33-33)
e2e/react-start/basic-prerendering/src/routes/users.tsx (3)
e2e/react-start/basic-prerendering/src/routes/api.users.ts (1)
  • Route (13-28)
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
e2e/react-start/basic-prerendering/src/routes/api.users.ts (2)
e2e/react-start/basic-prerendering/src/routes/api/users.$id.ts (1)
  • Route (12-32)
e2e/react-start/basic-prerendering/src/utils/users.tsx (1)
  • User (1-5)
🪛 Biome (2.1.2)
e2e/react-start/basic-prerendering/src/routes/deferred.tsx

[error] 43-43: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)


[error] 53-53: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

e2e/react-start/basic-prerendering/src/routes/stream.tsx

[error] 51-51: Avoid passing children using a prop

The canonical way to pass children in React is to use JSX elements

(lint/correctness/noChildrenProp)

🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~12-~12: There might be a mistake here.
Context: ... parameters (e.g., /about, /contact) - Routes with search parameters that have ...

(QB_NEW_EN)


[grammar] ~13-~13: There might be a mistake here.
Context: ...arch parameters that have default values - Routes that don't require dynamic data f...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Preview
  • GitHub Check: Test

@schiller-manuel
Copy link
Contributor

schiller-manuel commented Oct 14, 2025

i dont think we need a separate e2e test for this, can we please just add this to the existing start-basic e2e test?

@FatahChan
Copy link
Contributor Author

FatahChan commented Oct 14, 2025

i dont think we need a separate e2e test for this, can we please just add this to the existing start-basic e2e test?

I will check, but i didn't want to change the existing testing by turning on prerender, I might be able to create a different command to build for prerender, so test dont conflict.

WDYT?

if (!route.routePath) continue

// filter routes that are layout
if (route.isNonPath === true) continue
Copy link
Contributor

Choose a reason for hiding this comment

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

wouldnt we just want to render leaf routes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not sure I understand, but this code just check that it's not a layout, and nodes that doesn't have routePath

what are leaf routes? do they not have routePath?

Copy link
Contributor

Choose a reason for hiding this comment

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

if a route does not have children it's a leaf

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah, so you I should just check if the route has children?

how would it work for

users.tsx
users/details.tsx

user.tsx has children and a leaf node at the same time right?

Copy link
Contributor Author

@FatahChan FatahChan Oct 15, 2025

Choose a reason for hiding this comment

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

yeah, after testing. we still need to render non leaf children, i.e routes that have

let me know if you have other conrences regarding this point, otherwise, please resolve this conversation

Copy link
Contributor

Choose a reason for hiding this comment

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

what are "non leaf children"?

in your example

users.tsx << non leaf
users/index.tsx << leaf
users/details.tsx << leaf

@FatahChan
Copy link
Contributor Author

@schiller-manuel thanks for the review🙏, I will have to address your comments tomorrow, it's getting late here.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (2)
docs/start/framework/react/guide/static-prerendering.md (2)

12-14: Revise the automatic discovery criteria to match implementation.

The documentation incorrectly states that automatic discovery only includes routes that "don't require dynamic data fetching." However, based on the e2e tests (e2e/react-start/basic-prerendering/tests/prerendering.spec.ts), routes with loaders and API calls (e.g., /users, /posts) are successfully prerendered. Additionally, there's no evidence in the code that search parameters with default values are checked during discovery.

Apply this diff to accurately describe the discovery behavior:

-- Routes with no path parameters (e.g., `/about`, `/contact`)
-- Routes with search parameters that have default values
-- Routes that don't require dynamic data fetching
+- Routes with no path parameters (e.g., `/about`, `/contact`)
+- Routes that may fetch data via loaders or API calls (these are prerendered with their initial data)

20-20: Remove duplicate text.

This sentence repeats information already stated in the "Automatic Static Route Discovery" section above.

Apply this diff:

-TanStack Start can prerender your application to static HTML files, which can then be served to users without having to generate them on the fly. When prerendering is enabled without specifying pages, TanStack Start will automatically discover and prerender all static routes in your application.
+TanStack Start can prerender your application to static HTML files, which can then be served to users without having to generate them on the fly.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 773c94b and 6400b71.

📒 Files selected for processing (7)
  • docs/start/framework/react/guide/static-prerendering.md (2 hunks)
  • packages/router-generator/src/index.ts (1 hunks)
  • packages/start-plugin-core/src/global.d.ts (1 hunks)
  • packages/start-plugin-core/src/prerender.ts (6 hunks)
  • packages/start-plugin-core/src/schema.ts (1 hunks)
  • packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts (1 hunks)
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-plugin-core/src/global.d.ts
  • packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts
  • packages/router-generator/src/index.ts
  • packages/start-plugin-core/src/prerender.ts
  • packages/start-plugin-core/src/schema.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/global.d.ts
  • packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts
  • packages/start-plugin-core/src/prerender.ts
  • packages/start-plugin-core/src/schema.ts
packages/{router-cli,router-generator,router-plugin,virtual-file-routes}/**

📄 CodeRabbit inference engine (AGENTS.md)

Keep CLI, generators, bundler plugins, and virtual file routing utilities in their dedicated tooling package directories

Files:

  • packages/router-generator/src/index.ts
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/static-prerendering.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/static-prerendering.md
🧬 Code graph analysis (1)
packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts (2)
packages/router-generator/src/index.ts (3)
  • GeneratorPlugin (12-12)
  • RouteNode (34-34)
  • inferFullPath (30-30)
packages/router-core/src/route.ts (1)
  • path (1551-1553)
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~12-~12: There might be a mistake here.
Context: ... parameters (e.g., /about, /contact) - Routes with search parameters that have ...

(QB_NEW_EN)


[grammar] ~13-~13: There might be a mistake here.
Context: ...arch parameters that have default values - Routes that don't require dynamic data f...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (8)
packages/router-generator/src/index.ts (1)

30-30: LGTM! Export addition follows existing patterns.

The addition of inferFullPath to the public exports is appropriate for enabling automatic prerenderable path discovery. The change is backward compatible and follows the established re-export pattern in this index file.

docs/start/framework/react/guide/static-prerendering.md (1)

55-56: LGTM! Clear documentation of the new option.

The maxRedirect option is well documented with its default value, making it easy for users to understand and configure redirect limits during prerendering.

packages/start-plugin-core/src/global.d.ts (1)

6-6: LGTM! Clean global type declaration.

The new TSS_PRERENDABLE_PATHS global follows the existing pattern and provides appropriate typing for the automatic route discovery feature.

packages/start-plugin-core/src/schema.ts (1)

189-189: LGTM! Schema properly validates the new option.

The maxRedirect field is correctly added as an optional number, aligning with its usage in prerender.ts where it defaults to 5.

packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts (2)

23-32: LGTM! Comprehensive filtering logic.

The filtering logic appropriately excludes:

  • Routes without a path
  • Layout routes (non-path routes)
  • Dynamic routes (containing $)
  • API routes (without a component)

The comment on Line 32 helpfully clarifies why routes without components are filtered.


17-38: Avoid adding '/' unconditionally

The function seeds paths with '/', so if routeNodes lacks a root index route you’ll still return { path: '/' }. Only include '/' when a corresponding routeNode.routePath === '/' exists.

packages/start-plugin-core/src/prerender.ts (2)

73-95: LGTM! Robust redirect handling with proper limits.

The redirect logic correctly:

  • Follows internal redirects up to the configured limit
  • Warns and skips external redirects
  • Returns the last response when the limit is reached

The subsequent check on Lines 182-184 appropriately warns users when maxRedirect is exhausted, addressing the maintainer's concern.


126-137: LGTM! Accurate tracking of successfully prerendered pages.

Introducing a separate prerendered Set (Line 126) and returning it (Line 137) instead of seen ensures the function reports only successfully written pages, not just attempted ones. This is a meaningful improvement for observability.

@FatahChan
Copy link
Contributor Author

as I am re-writing the docs, I realize that static path discover should be a config

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6400b71 and 8ef548d.

📒 Files selected for processing (1)
  • packages/start-plugin-core/src/prerender.ts (6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-plugin-core/src/prerender.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/prerender.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (5)
packages/start-plugin-core/src/prerender.ts (5)

73-95: LGTM! Redirect handling logic is sound.

The redirect following logic correctly:

  • Limits recursion via maxRedirect to prevent infinite loops
  • Restricts following to internal redirects only (localhost or relative paths)
  • Warns about external redirects
  • Returns the redirect response when max redirects are exhausted (properly handled downstream at lines 182-184)

Note: The Request constructor at line 82 preserves the original options (including headers) when following redirects, which is correct.


126-126: LGTM! Better tracking of successful prerenders.

Introducing the prerendered Set separates "attempted" (tracked by seen) from "successfully prerendered" paths, and returning only successful paths provides accurate reporting to the caller.

Also applies to: 137-137


182-184: LGTM! Helpful logging for redirect exhaustion.

The warning clearly indicates when a page fails due to exceeding maxRedirect, helping users diagnose redirect loops.

Based on learnings (past review comments): This addresses the requested logging when max redirects are reached.


224-224: LGTM! Correctly tracks successful prerender.

The page is added to prerendered only after the HTML is successfully written to disk, ensuring accurate success tracking.


171-179: Naming consistency verified for maxRedirect. The schema, default parameter, and localFetch invocation all use the singular maxRedirect with a default of 5—no changes required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/start-plugin-core/src/prerender.ts (1)

26-31: Add type guard before assigning discovered paths.

The code assigns globalThis.TSS_PRERENDABLE_PATHS directly without validating it's an array. If the global is undefined, null, or a non-array value, this will cause a runtime error when checking .length.

Apply this diff to add proper validation:

   if (startConfig.prerender?.enabled && !startConfig.pages.length) {
     try {
-      startConfig.pages = globalThis.TSS_PRERENDABLE_PATHS
-      if (!startConfig.pages.length) {
+      const discovered = globalThis.TSS_PRERENDABLE_PATHS
+      if (!Array.isArray(discovered) || discovered.length === 0) {
         logger.warn('No static paths discovered. Falling back to root "/"')
         startConfig.pages = [{ path: '/' }]
+      } else {
+        startConfig.pages = discovered
       }
     } catch (error) {

Based on learnings

🧹 Nitpick comments (1)
packages/start-plugin-core/src/prerender.ts (1)

88-96: Consider URL parsing for more robust redirect handling.

The current string manipulation (location.replace('http://localhost', '')) works but has edge cases:

  • If location is exactly 'http://localhost' (no path), newUrl becomes an empty string
  • Only replaces the first occurrence if multiple 'http://localhost' substrings exist

Apply this diff to use URL parsing:

     if (isRedirectResponse(response) && maxRedirect > 0) {
       const location = response.headers.get('location')!
-      if (location.startsWith('http://localhost') || location.startsWith('/')) {
-        const newUrl = location.replace('http://localhost', '')
-        return localFetch(newUrl, options, maxRedirect - 1)
+      try {
+        const redirectUrl = new URL(location, 'http://localhost')
+        if (redirectUrl.hostname === 'localhost' || location.startsWith('/')) {
+          return localFetch(redirectUrl.pathname + redirectUrl.search + redirectUrl.hash, options, maxRedirect - 1)
+        }
+      } catch {
+        // Malformed URL, skip redirect
+        logger.warn(`Malformed redirect location: ${location}`)
+      }
-      } else {
-        logger.warn(`Skipping redirect to external location: ${location}`)
-      }
+      logger.warn(`Skipping redirect to external location: ${location}`)
     }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ef548d and 839208b.

📒 Files selected for processing (1)
  • packages/start-plugin-core/src/prerender.ts (6 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-plugin-core/src/prerender.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/prerender.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (3)
packages/start-plugin-core/src/prerender.ts (3)

130-130: Good semantic improvement.

Tracking prerendered separately from seen accurately reflects which pages were successfully written to disk versus merely encountered during crawling.

Also applies to: 141-141


186-188: Good diagnostic logging for redirect limits.

The warning helps users identify infinite redirect loops or pages requiring more than the configured redirect limit.


228-228: LGTM!

Correctly tracks successfully prerendered pages after writing to disk.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
docs/start/framework/react/guide/static-prerendering.md (2)

74-75: Clarify the automatic discovery description.

The sentence is ungrammatical and misses the key takeaway that discovered paths are merged with any explicitly configured pages. Suggest rephrasing:

-## Automatic Static Route Discovery
-
-All static paths will be prerendered, and seamlessly merge them with specified `pages` config
+## Automatic Static Route Discovery
+
+When enabled, TanStack Start prerenders every static path it finds and merges those results with any routes you explicitly list in `pages`.

80-82: Polish the crawling description.

The heading and example need better wording:

-## Crawling Links
-
-Prerender the pages link the prerendered pages
-
-if `/` --has link to--> `/posts`, `/posts` will also be prerendered
+## Crawling Links
+
+TanStack Start can also follow links found in prerendered HTML.
+
+For example, if `/` links to `/posts`, the `/posts` page will be prerendered too.
packages/start-plugin-core/src/prerender.ts (1)

84-106: Boolean redirect check and robust redirect resolution (handle https, ports, and relative locations).

Make the helper boolean and resolve target using URL(base) instead of string replace.

-  const isRedirectResponse = (res: Response) => {
-    return res.status >= 300 && res.status < 400 && res.headers.get('location')
-  }
+  const isRedirectResponse = (res: Response): boolean => {
+    return res.status >= 300 && res.status < 400 && !!res.headers.get('location')
+  }
   async function localFetch(
     path: string,
     options?: RequestInit,
     maxRedirect: number = 5,
   ): Promise<Response> {
-    const url = new URL(`http://localhost${path}`)
-    const response = await serverEntrypoint.fetch(new Request(url, options))
+    const requestUrl = new URL(path, 'http://localhost')
+    const response = await serverEntrypoint.fetch(new Request(requestUrl, options))
 
-    if (isRedirectResponse(response) && maxRedirect > 0) {
-      const location = response.headers.get('location')!
-      if (location.startsWith('http://localhost') || location.startsWith('/')) {
-        const newUrl = location.replace('http://localhost', '')
-        return localFetch(newUrl, options, maxRedirect - 1)
-      } else {
-        logger.warn(`Skipping redirect to external location: ${location}`)
-      }
-    }
+    if (isRedirectResponse(response)) {
+      if (maxRedirect <= 0) {
+        return response
+      }
+      const location = response.headers.get('location')!
+      const targetUrl = new URL(location, requestUrl)
+      if (targetUrl.origin === requestUrl.origin) {
+        const nextPath = `${targetUrl.pathname}${targetUrl.search}`
+        return localFetch(nextPath, options, maxRedirect - 1)
+      } else {
+        logger.warn(`Skipping redirect to external origin: ${targetUrl.href}`)
+      }
+    }
 
     return response
   }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 839208b and e5f1772.

📒 Files selected for processing (3)
  • docs/start/framework/react/guide/static-prerendering.md (4 hunks)
  • packages/start-plugin-core/src/prerender.ts (6 hunks)
  • packages/start-plugin-core/src/schema.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/start-plugin-core/src/schema.ts
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-plugin-core/src/prerender.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/prerender.ts
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/static-prerendering.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/static-prerendering.md
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~74-~74: There might be a mistake here.
Context: ...ute Discovery All static paths will be prerendered, and seamlessly merge them with specifie...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Test
🔇 Additional comments (2)
packages/start-plugin-core/src/prerender.ts (2)

135-149: Return only successfully prerendered pages — LGTM.

Switching to track and return actually written pages improves reporting accuracy.


235-236: Record prerendered path after write — LGTM.

Accurately marking success post-write is correct.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (2)
docs/start/framework/react/guide/static-prerendering.md (2)

28-30: Example contradicts the narrative; also fix “pre-rerendered”.

The example disables auto discovery while the text says it’s automatic when pages aren’t specified. Align the sample and fix the hyphenation.

-        // If disabled only root or the paths defined in pages config will be pre-rerendered
-        autoStaticPathsDiscovery: false,
+        // Enable automatic discovery of static routes when pages aren’t specified
+        autoStaticPathsDiscovery: true,

57-60: Remove redundancy.

This note repeats what’s explained above. Trim to keep the section focused.

-      // Optional configuration for specific pages
-      // Note: When pages are not specified, TanStack Start will automatically
-      // discover and prerender all static routes in your application
+      // Optional configuration for specific pages
🧹 Nitpick comments (3)
docs/start/framework/react/guide/static-prerendering.md (3)

10-10: Grammar and clarity fix.

Tighten the sentence.

-Set the `prerender.enabled` to `true`, others fields are optional
+Set `prerender.enabled` to `true`; other fields are optional.

Also, your snippet uses defineConfig but doesn’t import it. Add this near the other imports:

import { defineConfig } from 'vite'

74-75: Clarify and fix grammar in discovery section.

Make the sentence clear and correct.

-All static paths will be prerendered, and seamlessly merge them with specified `pages` config
+When enabled, all static routes are automatically discovered and prerendered. Any explicitly configured `pages` are merged with the discovered set.

80-82: Improve wording and mention crawlLinks behavior.

Clear grammar and tie to the option.

-Prerender the pages link the prerendered pages
-
-if `/` --has link to--> `/posts`, `/posts` will also be prerendered
+Prerender pages linked from prerendered pages (controlled by `crawlLinks`).
+
+For example, if `/` links to `/posts`, then `/posts` will also be prerendered.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9f6947 and d02748f.

📒 Files selected for processing (1)
  • docs/start/framework/react/guide/static-prerendering.md (4 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/static-prerendering.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/static-prerendering.md
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~74-~74: There might be a mistake here.
Context: ...ute Discovery All static paths will be prerendered, and seamlessly merge them with specifie...

(QB_NEW_EN)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
docs/start/framework/react/guide/static-prerendering.md (1)

28-30: Example contradicts the documented default behavior.

Setting autoStaticPathsDiscovery: false disables the automatic discovery feature that the surrounding documentation promotes. Since the schema default is true and the docs state that discovery happens automatically, either:

  1. Remove this line to show the default behavior, or
  2. Change it to true with a comment explaining it's the default

Apply this diff to align the example with the documented behavior:

-        // If disabled only root or the paths defined in pages config will be pre-rerendered
-        autoStaticPathsDiscovery: false,
+        // Automatically discover static routes and merge with pages config (default: true)
+        autoStaticPathsDiscovery: true,

Or remove it entirely:

-        // If disabled only root or the paths defined in pages config will be pre-rerendered
-        autoStaticPathsDiscovery: false,
-
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d02748f and ffbed76.

📒 Files selected for processing (2)
  • docs/start/framework/react/guide/static-prerendering.md (4 hunks)
  • packages/start-plugin-core/src/prerender.ts (6 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • packages/start-plugin-core/src/prerender.ts
packages/{*-start,start-*}/**

📄 CodeRabbit inference engine (AGENTS.md)

Name and place Start framework packages under packages/-start/ or packages/start-/

Files:

  • packages/start-plugin-core/src/prerender.ts
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Use internal docs links relative to the docs/ folder (e.g., ./guide/data-loading)

Files:

  • docs/start/framework/react/guide/static-prerendering.md
docs/{router,start}/**

📄 CodeRabbit inference engine (AGENTS.md)

Place router docs under docs/router/ and start framework docs under docs/start/

Files:

  • docs/start/framework/react/guide/static-prerendering.md
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~74-~74: There might be a mistake here.
Context: ...ute Discovery All static paths will be prerendered, and seamlessly merge them with specifie...

(QB_NEW_EN)

🔇 Additional comments (4)
packages/start-plugin-core/src/prerender.ts (4)

24-44: Clarify the auto-discovery merging behavior.

The current implementation merges discovered static paths with user-defined pages regardless of whether pages were explicitly specified. This differs from the typical interpretation of "automatic discovery when pages are not specified" (as stated in PR objectives and documentation).

However, past comments suggest this merging behavior is intentional. Please confirm:

  1. Should auto-discovery merge with explicit pages (current behavior)?
  2. Or should it only activate when startConfig.pages is empty (as PR objectives imply)?

If merging is the intended design, update the documentation to clearly state that autoStaticPathsDiscovery supplements rather than replaces user-specified pages.

The implementation looks correct for the merge approach. The || [] fallback at line 32 properly handles undefined/missing global paths. However, consider adding validation to ensure discovered pages are valid:

       const pagesMap = new Map(pages.map((item) => [item.path, item]))
       const discoveredPages = globalThis.TSS_PRERENDABLE_PATHS || []
 
       for (const page of discoveredPages) {
-        if (!pagesMap.has(page.path)) {
+        if (page?.path && !pagesMap.has(page.path)) {
           pagesMap.set(page.path, page)
         }
       }

85-107: LGTM: Solid redirect handling implementation.

The redirect-following logic is well-implemented:

  • Correctly identifies redirect responses (300-399 with Location header)
  • Follows internal redirects (localhost or root-relative) recursively
  • Respects the maxRedirect limit to prevent infinite loops
  • Warns about external redirects instead of following them
  • Preserves request options across redirects

One minor suggestion for improved observability when the redirect limit is reached:

     if (isRedirectResponse(response) && maxRedirect > 0) {
       const location = response.headers.get('location')!
       if (location.startsWith('http://localhost') || location.startsWith('/')) {
         const newUrl = location.replace('http://localhost', '')
         return localFetch(newUrl, options, maxRedirect - 1)
       } else {
         logger.warn(`Skipping redirect to external location: ${location}`)
       }
+    } else if (isRedirectResponse(response) && maxRedirect === 0) {
+      logger.warn(`Redirect limit reached at ${path}`)
     }

This helps distinguish between "max redirects reached" vs "redirect succeeded" in the calling code.


138-149: Verify the tracking set change aligns with intended behavior.

The return value changed from seen to prerendered. This means:

  • Previously: returned all pages that were attempted (including failures)
  • Now: returns only successfully prerendered pages

This is likely the correct behavior (only report success), but verify that:

  1. Calling code expects only successful pages
  2. Failed pages don't need to be reported separately
  3. The log message "Prerendered X pages" accurately reflects successful count

The implementation correctly tracks successful prerenders by adding to the set only after writeFile succeeds (line 236).


183-196: LGTM: Proper integration of redirect handling.

The localFetch call correctly:

  • Passes the maxRedirect parameter from merged options
  • Guards against undefined headers with ?? {}
  • Checks for redirect responses when !res.ok
  • Logs a warning when max redirects are reached

The warning at line 195 provides useful feedback to users when they hit the redirect limit. Consider whether the error thrown at line 197 should include more context about the redirect chain for debugging.

FatahChan and others added 25 commits October 26, 2025 01:02
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
e2e/react-start/basic/tests/prerendering.spec.ts (3)

27-30: Minor: Merge standalone comment with assertion.

The comment on line 27 is separated from its related assertion. Consider moving it to line 28 or removing the blank line for better readability.

Apply this diff:

-      // API routes should NOT be prerendered
-
       expect(existsSync(join(distDir, 'api', 'users', 'index.html'))).toBe(
         false,
-      ) // /api/users
+      ) // API routes should NOT be prerendered (/api/users)

12-12: Extract repeated distDir construction.

The distDir path is constructed identically in three places. Extract it to a shared constant in a beforeAll hook or module-level variable to reduce duplication and improve maintainability.

Example refactor:

+const distDir = join(process.cwd(), 'dist', 'client')
+
 test.describe('Prerender Static Path Discovery', () => {
   test.skip(!isPrerender, 'Skipping since not in prerender mode')
   test.describe('Build Output Verification', () => {
     test('should automatically discover and prerender static routes', () => {
-      const distDir = join(process.cwd(), 'dist', 'client')
       // ... rest of test

Also applies to: 36-36, 45-45


7-52: Verify alignment with reviewer feedback and consider expanded test coverage.

  1. Reviewer feedback alignment: According to PR objectives, schiller-manuel suggested adding coverage to the existing start-basic e2e test rather than creating a separate test file. Please confirm this approach was discussed and approved.

  2. Limited test coverage: The current tests provide basic smoke testing for static route discovery but don't exercise several features introduced in this PR:

    • autoStaticPathsDiscovery configuration option
    • maxRedirects and redirect following behavior
    • failOnError configuration
    • Dynamic routes discovered via crawlLinks
    • Error handling when discovery fails

Consider adding tests for these scenarios to provide more comprehensive coverage of the prerendering functionality.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 67888a6 and 00a811c.

📒 Files selected for processing (1)
  • e2e/react-start/basic/tests/prerendering.spec.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript in strict mode with extensive type safety across the codebase

Files:

  • e2e/react-start/basic/tests/prerendering.spec.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

Store end-to-end tests under the e2e/ directory

Files:

  • e2e/react-start/basic/tests/prerendering.spec.ts
🧬 Code graph analysis (1)
e2e/react-start/basic/tests/prerendering.spec.ts (1)
e2e/react-start/basic/tests/utils/isPrerender.ts (1)
  • isPrerender (1-1)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Test
  • GitHub Check: Preview
🔇 Additional comments (1)
e2e/react-start/basic/tests/prerendering.spec.ts (1)

1-8: LGTM! Test suite setup is correct.

The imports are appropriate, and the conditional skip logic correctly gates the tests to run only in prerender mode.

@schiller-manuel schiller-manuel merged commit 475707e into TanStack:main Oct 25, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

static pre-rendering does not pre-render pages that are not linked

2 participants