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 enhancements: automatic static route discovery (optional), configurable max redirects, fail-on-error option, and improved redirect handling with tracking of prerendered pages.
    • Added a public routing utility to infer full paths.
  • Documentation

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

    • Added prerender test mode, build/prerender scripts, dummy-server lifecycle scripts, and updated E2E Playwright tests.

Copy link
Contributor

coderabbitai bot commented Oct 14, 2025

Walkthrough

Adds automatic static-route discovery via a new 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 / if empty; merge globalThis.TSS_PRERENDABLE_PATHS when autoStaticPathsDiscovery enabled; add prerendered tracking; enhance localFetch to follow internal redirects up to maxRedirects and warn on external/exhausted redirects; write HTML and return prerendered paths.
Schema / types
packages/start-plugin-core/src/schema.ts
Add optional autoStaticPathsDiscovery?: boolean and maxRedirects?: number to prerender schema/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, componented) paths and assigns globalThis.TSS_PRERENDABLE_PATHS; declare global type.
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/*, 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.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Generator as prerenderRoutesPlugin
  participant Global as globalThis
  participant Core as prerender engine
  participant App as Local SSR server
  participant FS as File System

  Generator->>Generator: compute prerenderable paths (non-dynamic, has component)
  Generator->>Global: set TSS_PRERENDABLE_PATHS

  Core->>Core: init pages ("/" if none)
  alt autoStaticPathsDiscovery = true
    Core->>Global: read TSS_PRERENDABLE_PATHS
    Core->>Core: merge into pages set (dedupe)
  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

Possibly related PRs

Suggested reviewers

  • schiller-manuel
  • birkskyum

Poem

A rabbit hops along the route,
Sniffs out static pages, cute and stout,
Follows gentle redirects, skips the rest,
Writes them down and takes a rest,
Leaves a tiny index — all checked out. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "fix: not all pages are pre-rendered" directly and clearly captures the main objective of this changeset. The PR implements automatic static path discovery to address the reported issue where only root and linked pages were being prerendered when prerender was enabled without explicit page configuration. The title is specific enough that a teammate reviewing the history would immediately understand this fixes incomplete prerendering behavior, and it avoids vague terminology or extraneous details.
Linked Issues Check ✅ Passed The PR comprehensively addresses all key requirements from issue #5455. It implements automatic static path discovery that discovers and prerenders all static routes when no pages are explicitly specified, correctly excludes layout-only routes (underscore-prefixed), dynamic routes (containing $), and routes without components, and preserves existing crawlLinks behavior for linked dynamic routes. The implementation includes the new configuration option autoStaticPathsDiscovery (defaulting to true), improved redirect handling with maxRedirects, and includes E2E tests that verify prerendering of static routes while confirming exclusion of layout and API routes. The solution falls back to root path initialization when discovery is disabled, maintaining backward compatibility.
Out of Scope Changes Check ✅ Passed All code changes in this PR are directly aligned with the stated objectives of fixing automatic prerendering of static routes. The changes span core prerendering logic (prerender.ts), configuration schema (schema.ts), route discovery plugin (prerender-routes-plugin.ts), plugin integration, documentation updates, and comprehensive E2E testing infrastructure. No unrelated modifications such as refactorings, dependency updates, or feature additions outside of the prerendering scope are present. Even supplementary changes like the inferFullPath export and isPrerender utility are directly necessary for implementing and testing the new functionality.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ec71c9 and 4ad8062.

📒 Files selected for processing (1)
  • 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
⏰ 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

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.

Copy link

nx-cloud bot commented Oct 14, 2025

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit 4ad8062

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ❌ Failed 27m 36s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 14s View ↗

☁️ Nx Cloud last updated this comment at 2025-10-18 20:57:41 UTC

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-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: 4ad8062

@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.

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: 3

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

28-30: Example contradicts the demonstrated feature.

The example shows autoStaticPathsDiscovery: false, which disables automatic discovery—the primary feature being documented. Since the default is true (as seen in prerender.ts line 29) and this section demonstrates "Enabling Prerendering," consider:

Option 1 (recommended): Remove the line entirely to use the default:

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

Option 2: Show it enabled with an updated comment:

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

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (12)
  • docs/start/framework/react/guide/static-prerendering.md (4 hunks)
  • e2e/react-start/basic/package.json (1 hunks)
  • e2e/react-start/basic/playwright.config.ts (3 hunks)
  • e2e/react-start/basic/tests/search-params.spec.ts (4 hunks)
  • e2e/react-start/basic/tests/utils/isPrerender.ts (1 hunks)
  • e2e/react-start/basic/vite.config.ts (3 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 (3)
  • packages/start-plugin-core/src/start-router-plugin/plugin.ts
  • packages/start-plugin-core/src/schema.ts
  • packages/start-plugin-core/src/start-router-plugin/generator-plugins/prerender-routes-plugin.ts
🧰 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/tests/search-params.spec.ts
  • e2e/react-start/basic/tests/utils/isPrerender.ts
  • e2e/react-start/basic/playwright.config.ts
  • packages/start-plugin-core/src/prerender.ts
  • packages/router-generator/src/index.ts
  • e2e/react-start/basic/vite.config.ts
  • packages/start-plugin-core/src/global.d.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • e2e/react-start/basic/tests/search-params.spec.ts
  • e2e/react-start/basic/tests/utils/isPrerender.ts
  • e2e/react-start/basic/playwright.config.ts
  • e2e/react-start/basic/package.json
  • e2e/react-start/basic/vite.config.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
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/global.d.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
**/package.json

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • e2e/react-start/basic/package.json
🧬 Code graph analysis (3)
e2e/react-start/basic/tests/search-params.spec.ts (2)
e2e/react-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/react-start/basic/tests/utils/isPrerender.ts (1)
  • isPrerender (1-1)
e2e/react-start/basic/playwright.config.ts (2)
e2e/react-start/basic/tests/utils/isSpaMode.ts (1)
  • isSpaMode (1-1)
e2e/react-start/basic/tests/utils/isPrerender.ts (1)
  • isPrerender (1-1)
e2e/react-start/basic/vite.config.ts (1)
e2e/react-start/basic/tests/utils/isPrerender.ts (1)
  • isPrerender (1-1)
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g API route...

(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
🔇 Additional comments (14)
packages/router-generator/src/index.ts (1)

30-30: LGTM – inferFullPath is exported and has JSDoc documentation.

e2e/react-start/basic/playwright.config.ts (2)

22-26: LGTM!

The getCommand() helper provides clean mode selection logic with appropriate fallback to SSR mode.


20-20: Dummy server scripts are defined in package.json.

The test:e2e:startDummyServer and test:e2e:stopDummyServer scripts are confirmed to exist. The prerenderModeCommand is properly configured.

The 152+ character command string is readable as-is, though breaking it into multiple lines or storing commands in separate variables would improve maintainability if this grows further.

e2e/react-start/basic/tests/utils/isPrerender.ts (1)

1-1: LGTM!

The implementation is clean and follows the same pattern as isSpaMode.ts. The explicit boolean type annotation ensures type safety.

e2e/react-start/basic/tests/search-params.spec.ts (2)

31-33: Verify that skipping redirect expectations aligns with prerender behavior.

The test now skips redirect expectations when isPrerender is true. This suggests that during prerendering, redirects are followed server-side and baked into the static HTML, so the browser won't observe the redirect.

Confirm this matches the intended prerender behavior described in the PR (redirect-following logic during prerendering).


56-58: Consistent with the redirect handling above.

This check mirrors the logic at line 31, ensuring redirect expectations are skipped consistently across prerender mode tests.

e2e/react-start/basic/vite.config.ts (2)

15-26: Verify the maxRedirect value and document the filter logic.

The prerenderConfiguration looks well-structured, but:

  1. maxRedirect: 100 — This value seems arbitrary. Verify if it aligns with the "redirect count limit" mentioned in the PR objectives or if it should be documented/made configurable.

  2. Filter paths — The hardcoded exclusion list is appropriate for e2e tests (excluding 404s, redirects, etc.), but consider adding a brief inline comment explaining why these specific paths are filtered out for future maintainability.


39-39: LGTM!

The conditional prerender configuration follows the same pattern as the SPA configuration, maintaining consistency in the codebase.

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

6-6: LGTM! Clean global type declaration.

The type definition correctly declares the global as optional with an appropriate structure that matches the Page type's minimal shape. The optional nature aligns with the fallback handling in prerender.ts (|| []).

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

24-44: Clarify merge behavior in documentation.

The implementation merges discovered static paths with user-specified pages (as confirmed in your "scope changed" comment). However, the PR objectives state: "When prerender.enabled: true and no pages specified, automatically prerender all static routes."

This creates an inconsistency between the stated objectives and the actual behavior. While the documentation at line 74 correctly states paths are "merged with the specified pages config," the comment at lines 57-60 in the docs says "When pages are not specified" which contradicts the merging behavior.

Please clarify the intended behavior and update either:

  1. The PR objectives/description to reflect that discovery merges with specified pages, OR
  2. The implementation to only auto-discover when startConfig.pages is empty

Additionally, the documentation example at line 29 shows autoStaticPathsDiscovery: false, which disables the feature being demonstrated. Consider setting it to true or removing it to use the default.


85-107: LGTM! Solid redirect handling logic.

The redirect-following logic is well-implemented:

  • Correctly identifies redirect responses (3xx status with Location header)
  • Limits redirect depth with configurable maxRedirect parameter
  • Only follows internal redirects (localhost or root-relative paths) for security
  • Warns about external redirects appropriately
  • Previous concerns about parameter naming and header spreading have been addressed

138-138: LGTM! Accurate tracking of prerendered pages.

The change from tracking "seen" pages to tracking actually "prerendered" pages improves accuracy. Returning the prerendered set (line 149) ensures the reported count reflects pages that were successfully written to disk, not just attempted.

Also applies to: 149-149, 236-236


183-191: All previous concerns addressed.

The localFetch invocation correctly:

  • Passes maxRedirect parameter matching the schema field
  • Guards header spreading with nullish coalescing (?? {})
  • Includes redirect warning at lines 194-196 as requested in past reviews
docs/start/framework/react/guide/static-prerendering.md (1)

72-82: LGTM! Accurate description of auto-discovery behavior.

The section correctly describes:

  • Merging of discovered paths with specified pages config (line 74)
  • Exclusions from automatic discovery (layout routes, dynamic routes, routes without components)
  • Ability to prerender dynamic routes via crawlLinks

Note: LanguageTool warnings at lines 78-79 appear to be false positives—the markdown list syntax 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: 3

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

28-30: Align the example with the documented default behavior.

Setting autoStaticPathsDiscovery to false disables the very feature this section is introducing, and the comment still uses the incorrect “pre-rerendered” wording. Please show it enabled (or drop the override) and clarify the comment.

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

57-59: Update the note to reflect path merging.

Discovered static paths are merged with the pages array when auto discovery is on; the current note still claims the opposite. Please reword it to describe the merge behavior controlled by autoStaticPathsDiscovery.


88-88: Remove the stray + marker.

The leading plus sign looks like a diff artifact; drop it so the sentence renders properly.

🧹 Nitpick comments (1)
docs/start/framework/solid/guide/static-prerendering.md (1)

80-80: Grammar nit: add the comma in “e.g., API routes”.

Tiny readability tweak: “Routes without components (e.g., API routes)”.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adabf0f and 3b7ba80.

📒 Files selected for processing (2)
  • docs/start/framework/react/guide/static-prerendering.md (3 hunks)
  • docs/start/framework/solid/guide/static-prerendering.md (3 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/start/framework/solid/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
  • docs/start/framework/solid/guide/static-prerendering.md
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g API route...

(QB_NEW_EN)

docs/start/framework/solid/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g API route...

(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

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

Caution

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

⚠️ Outside diff range comments (1)
docs/start/framework/solid/guide/static-prerendering.md (1)

15-68: Restore the Solid-specific Vite plugins.

This Solid guide now imports the React Start and Vite React plugins, which won’t work in a Solid project. Please switch back to the Solid equivalents (e.g., @tanstack/solid-start/plugin/vite and vite-plugin-solid) and update the plugin invocation accordingly.

-import { tanstackStart } from '@tanstack/react-start/plugin/vite'
-import viteReact from '@vitejs/plugin-react'
+import { tanstackStart } from '@tanstack/solid-start/plugin/vite'
+import solid from 'vite-plugin-solid'
@@
-    }),
-    viteReact(),
+    }),
+    solid(),
♻️ Duplicate comments (2)
docs/start/framework/solid/guide/static-prerendering.md (1)

28-30: Keep auto discovery enabled in the example.

Explicitly setting autoStaticPathsDiscovery: false disables static route discovery and contradicts the text. Either remove the override or show it enabled.

-        // If disabled only root or the paths defined in pages config will be pre-rerendered
-        autoStaticPathsDiscovery: false,
+        // Disable only if you want to prerender just the root or the paths defined in pages config
+        // autoStaticPathsDiscovery: false,
docs/start/framework/react/guide/static-prerendering.md (1)

28-30: Keep auto discovery enabled in the example.

Setting autoStaticPathsDiscovery to false here disables the very behavior this section explains and misleads readers following the sample. Either drop the override (to inherit the default true) or explicitly set it to true and adjust the comment copy.

-        // If disabled only root or the paths defined in pages config will be pre-rerendered
-        autoStaticPathsDiscovery: false,
+        // Disable only if you want to prerender just the root or the paths defined in pages config
+        // 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 3b7ba80 and efa967d.

📒 Files selected for processing (3)
  • docs/start/framework/react/guide/static-prerendering.md (3 hunks)
  • docs/start/framework/solid/guide/static-prerendering.md (3 hunks)
  • e2e/react-start/basic/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • e2e/react-start/basic/package.json
🧰 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/start/framework/solid/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
  • docs/start/framework/solid/guide/static-prerendering.md
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g API route...

(QB_NEW_EN)

docs/start/framework/solid/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g API route...

(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: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between efa967d and 16b3541.

📒 Files selected for processing (2)
  • docs/start/framework/react/guide/static-prerendering.md (3 hunks)
  • docs/start/framework/solid/guide/static-prerendering.md (3 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/start/framework/solid/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
  • docs/start/framework/solid/guide/static-prerendering.md
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g., API rou...

(QB_NEW_EN)

docs/start/framework/solid/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g., API rou...

(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: 0

Caution

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

⚠️ Outside diff range comments (3)
docs/start/framework/solid/guide/static-prerendering.md (1)

12-20: Add missing import for defineConfig in the Vite config snippet.

Copy-pasting will fail without importing defineConfig.

 // vite.config.ts
-
+import { defineConfig } from 'vite'
 import { tanstackStart } from '@tanstack/solid-start/plugin/vite'
 import viteSolid from 'vite-plugin-solid'
docs/start/framework/react/guide/static-prerendering.md (2)

57-66: Example config bug: pages is outside the prerender block.

In the snippet, pages is a sibling of prerender. It must be nested inside prerender for the config to work.

Replace the prerender section with this corrected block:

prerender: {
  // Enable prerendering
  enabled: true,
  // Enable if you need pages to be at `/page/index.html` instead of `/page.html`
  autoSubfolderIndex: true,
  // If disabled, only the root path or the paths defined in the pages config will be prerendered
  autoStaticPathsDiscovery: true,
  // How many prerender jobs to run at once
  concurrency: 14,
  // Whether to extract links from the HTML and prerender them also
  crawlLinks: true,
  // Filter function takes the page object and returns whether it should prerender
  filter: ({ path }) => !path.startsWith('/do-not-render-me'),
  // Number of times to retry a failed prerender job
  retryCount: 2,
  // Delay between retries in milliseconds
  retryDelay: 1000,
  // Maximum number of redirects to follow during prerendering
  maxRedirect: 5,
  // Fail if an error occurs during prerendering
  failOnError: true,
  // Callback when page is successfully rendered
  onSuccess: ({ page }) => {
    console.log(`Rendered ${page.path}!`)
  },
  // Optional configuration for specific pages
  // Note: When autoStaticPathsDiscovery is enabled (default), discovered static
  // routes will be merged with the pages specified below
  pages: [
    {
      path: '/my-page',
      prerender: { enabled: true, outputPath: '/my-page/index.html' },
    },
  ],
},

12-20: Missing import for defineConfig in the snippet.

Add the import so the example is copy‑paste runnable.

 // vite.config.ts
-
-import { tanstackStart } from '@tanstack/react-start/plugin/vite'
+import { defineConfig } from 'vite'
+import { tanstackStart } from '@tanstack/react-start/plugin/vite'
 import viteReact from '@vitejs/plugin-react'
♻️ Duplicate comments (2)
docs/start/framework/solid/guide/static-prerendering.md (1)

74-75: Reword claim to align with listed exclusions.

Current sentence over-promises (“All static paths...”) while the bullets list exclusions. Suggest:

-All static paths will be automatically discovered and seamlessly merged with the specified `pages` config
+Static routes that meet the criteria below are automatically discovered and seamlessly merged with the specified `pages` config
docs/start/framework/react/guide/static-prerendering.md (1)

74-74: Overpromise: not “All static paths.” Limit to eligible routes.

Rephrase to match the exclusion list that follows.

- All static paths will be automatically discovered and seamlessly merged with the specified `pages` config
+ Static routes that meet the criteria below are automatically discovered and seamlessly merged with the specified `pages` config
🧹 Nitpick comments (2)
docs/start/framework/solid/guide/static-prerendering.md (1)

46-51: Document defaults/behavior for new options (maxRedirect, failOnError).

State their default values and effect (e.g., does failOnError exit the build?). Helps readers rely on implied defaults without copying values.

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

28-30: Clarify disabled behavior (mention crawlLinks).

When auto discovery is disabled, root-linked pages can still be prerendered if crawlLinks is true. Add that to avoid confusion.

-        // If disabled, only the root path or the paths defined in the pages config will be prerendered
+        // If disabled, only the root path (and any pages discovered via crawlLinks) or the paths defined in the pages config will 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 16b3541 and 90cb1ee.

📒 Files selected for processing (2)
  • docs/start/framework/react/guide/static-prerendering.md (3 hunks)
  • docs/start/framework/solid/guide/static-prerendering.md (3 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/start/framework/solid/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
  • docs/start/framework/solid/guide/static-prerendering.md
🪛 LanguageTool
docs/start/framework/react/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g., API rou...

(QB_NEW_EN)

docs/start/framework/solid/guide/static-prerendering.md

[grammar] ~78-~78: There might be a mistake here.
Context: ...e they require specific parameter values - Layout routes (prefixed with _) since ...

(QB_NEW_EN)


[grammar] ~79-~79: There might be a mistake here.
Context: ...since they don't render standalone pages - Routes without components (e.g., API rou...

(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)
docs/start/framework/solid/guide/static-prerendering.md (2)

15-16: Solid Start + Vite Solid plugins correctly used.
Looks good and resolves prior feedback to avoid React plugins in Solid docs.


28-30: Correct default and guidance for autoStaticPathsDiscovery.
Good: default enabled and clear note about behavior when disabled.

@FatahChan
Copy link
Contributor Author

@schiller-manuel can you check? please.

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)

12-12: Extract repeated distDir path construction.

The distDir path is constructed identically three times across different tests. Extract it to a shared constant or beforeEach hook to reduce duplication and improve maintainability.

Apply this refactor to consolidate the path construction:

+let distDir: string
+
 test.describe('Prerender Static Path Discovery', () => {
   test.skip(!isPrerender, 'Skipping since not in prerender mode')
+  
+  test.beforeEach(() => {
+    distDir = join(process.cwd(), 'dist', 'client')
+  })
+  
   test.describe('Build Output Verification', () => {
     test('should automatically discover and prerender static routes', () => {
       // Check that static routes were automatically discovered and prerendered
-      const distDir = join(process.cwd(), 'dist', 'client')

       // These static routes should be automatically discovered and prerendered
   test.describe('Static Files Verification', () => {
     test('should contain prerendered content in posts.html', () => {
-      const distDir = join(process.cwd(), 'dist', 'client')
       expect(existsSync(join(distDir, 'posts.html'))).toBe(true)
     test('should contain prerendered content in users.html', () => {
-      const distDir = join(process.cwd(), 'dist', 'client')
       expect(existsSync(join(distDir, 'users.html'))).toBe(true)

Also applies to: 36-36, 45-45


35-51: Refactor duplicate test pattern with parameterization.

The two tests in Static Files Verification follow identical patterns, differing only in filename and expected text. Parameterize these tests to reduce duplication and improve maintainability.

Apply this refactor to parameterize the tests:

   test.describe('Static Files Verification', () => {
-    test('should contain prerendered content in posts.html', () => {
-      const distDir = join(process.cwd(), 'dist', 'client')
-      expect(existsSync(join(distDir, 'posts.html'))).toBe(true)
-
-      // "Select a post." should be in the prerendered HTML
-      const html = readFileSync(join(distDir, 'posts.html'), 'utf-8')
-      expect(html).toContain('Select a post.')
-    })
-
-    test('should contain prerendered content in users.html', () => {
-      const distDir = join(process.cwd(), 'dist', 'client')
-      expect(existsSync(join(distDir, 'users.html'))).toBe(true)
-
-      // "Select a user." should be in the prerendered HTML
-      const html = readFileSync(join(distDir, 'users.html'), 'utf-8')
-      expect(html).toContain('Select a user.')
-    })
+    const testCases = [
+      { file: 'posts.html', expectedText: 'Select a post.' },
+      { file: 'users.html', expectedText: 'Select a user.' },
+    ]
+
+    testCases.forEach(({ file, expectedText }) => {
+      test(`should contain prerendered content in ${file}`, () => {
+        const filePath = join(distDir, file)
+        expect(existsSync(filePath)).toBe(true)
+
+        const html = readFileSync(filePath, 'utf-8')
+        expect(html).toContain(expectedText)
+      })
+    })
   })

37-37: Remove redundant existsSync checks.

The existsSync checks before readFileSync are redundant. readFileSync will throw a clear error if the file doesn't exist, making the explicit existence check unnecessary. Removing these checks simplifies the code while maintaining the same failure behavior.

Apply this diff to remove the redundant checks:

     test('should contain prerendered content in posts.html', () => {
       const distDir = join(process.cwd(), 'dist', 'client')
-      expect(existsSync(join(distDir, 'posts.html'))).toBe(true)

       // "Select a post." should be in the prerendered HTML
       const html = readFileSync(join(distDir, 'posts.html'), 'utf-8')
       expect(html).toContain('Select a post.')
     })

     test('should contain prerendered content in users.html', () => {
       const distDir = join(process.cwd(), 'dist', 'client')
-      expect(existsSync(join(distDir, 'users.html'))).toBe(true)

       // "Select a user." should be in the prerendered HTML
       const html = readFileSync(join(distDir, 'users.html'), 'utf-8')
       expect(html).toContain('Select a user.')
     })

Also applies to: 46-46

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 90cb1ee and ded3104.

📒 Files selected for processing (2)
  • e2e/react-start/basic/package.json (1 hunks)
  • e2e/react-start/basic/tests/prerendering.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • e2e/react-start/basic/package.json
🧰 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: Preview
  • GitHub Check: Test
🔇 Additional comments (1)
e2e/react-start/basic/tests/prerendering.spec.ts (1)

7-53: Remove placement warning for prerendering.spec.ts
Feature-specific e2e tests in e2e/react-start/basic/tests are each in their own .spec.ts file; adding prerendering.spec.ts aligns with the existing pattern.

Likely an incorrect or invalid review comment.

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 (1)
docs/start/framework/solid/guide/static-prerendering.md (1)

80-80: Fix punctuation: add comma after "e.g."

Apply this diff:

-- Routes without components (e.g API routes)
+- Routes without components (e.g., API routes)
🧹 Nitpick comments (1)
packages/start-plugin-core/src/prerender.ts (1)

24-44: Add defensive validation for discovered pages.

While the || [] fallback handles undefined, the code doesn't validate that each item in discoveredPages has a .path property. If the global is set to an invalid array, line 35 will throw.

Apply this diff to add a safeguard:

      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)
        }
      }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ded3104 and 4ec71c9.

📒 Files selected for processing (5)
  • docs/start/framework/react/guide/static-prerendering.md (3 hunks)
  • docs/start/framework/solid/guide/static-prerendering.md (3 hunks)
  • e2e/react-start/basic/vite.config.ts (3 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)
  • docs/start/framework/react/guide/static-prerendering.md
🧰 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:

  • e2e/react-start/basic/vite.config.ts
  • packages/start-plugin-core/src/prerender.ts
  • packages/start-plugin-core/src/schema.ts
e2e/**

📄 CodeRabbit inference engine (AGENTS.md)

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

Files:

  • e2e/react-start/basic/vite.config.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/schema.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/solid/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/solid/guide/static-prerendering.md
🧬 Code graph analysis (1)
e2e/react-start/basic/vite.config.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: Preview
  • GitHub Check: Test
🔇 Additional comments (10)
docs/start/framework/solid/guide/static-prerendering.md (3)

28-30: Documentation looks good!

The comment accurately describes the behavior when autoStaticPathsDiscovery is disabled, and setting it to true (the default) is the correct example for users.


57-59: Clear documentation of merging behavior.

The note effectively communicates that discovered routes are merged with explicitly specified pages when auto-discovery is enabled.


72-88: Documentation sections are comprehensive.

The Automatic Static Route Discovery and Crawling Links sections provide clear guidance on how the feature works and what to expect. The exclusion list and the note about dynamic routes are helpful.

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

85-107: Redirect handling logic is sound.

The implementation correctly:

  • Follows internal redirects up to the configured limit
  • Decrements the counter to prevent infinite loops
  • Skips external redirects with a warning
  • Returns the final response

The edge case where maxRedirects is exhausted is properly handled by the caller at lines 194-196, which logs a warning when a redirect response is returned with !res.ok.


183-196: Fetch and error handling implemented correctly.

The code properly:

  • Guards against undefined headers with nullish coalescing
  • Passes the maxRedirects parameter consistently (plural form throughout)
  • Provides a specific warning when redirect limits are exhausted
  • Throws an appropriate error for other failure cases

136-149: Prerendered tracking improves accuracy.

The introduction of the prerendered set (separate from seen) ensures the function returns only pages that were successfully written to disk, which is the correct semantic for the return value.


236-237: Correct placement of prerendered tracking.

Adding the page to prerendered after the successful writeFile ensures that only pages that were actually written are counted, with failures properly excluded.

e2e/react-start/basic/vite.config.ts (3)

6-6: LGTM!

The import follows the established pattern and is consistent with the existing isSpaMode import.


39-39: LGTM!

The conditional configuration follows the established pattern and enables proper test isolation by only activating prerender when MODE=prerender.


15-26: Verify the maxRedirects value and the filter matching approach.

Based on examination of the codebase:

  1. High redirect limit is intentional but unclear: The default maxRedirects in the prerender implementation is 5, but this configuration sets it to 100—20 times higher. While the routes in this test app include legitimate redirects (/redirect/$target/via-beforeLoad, /redirect/$target/via-loader), there's no comment explaining why such a high limit is needed. This warrants clarification.

  2. Filter matching is fragile: The filter excludes routes matching /redirect, /not-found/via-beforeLoad, and /not-found/via-loader using .includes(). Currently, this works for the existing routes. However, this approach could accidentally exclude unintended routes if future paths contain these substrings (e.g., a hypothetical /my-redirect-page would be unintentionally excluded). Consider using exact path matching or a more explicit exclusion strategy to prevent future issues.

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