<html lang="en">
<head></head>
<body>

<form id="mainForm" method="post" action="https://stackblitz.com/run" target="_self">
<input type="hidden" name="project[files][.gitignore]" value="node_modules
.DS_Store
dist
dist-ssr
*.local
">
<input type="hidden" name="project[files][README.md]" value="# TanStack Router - SSR Streaming File-Based Example

An example demonstrating server-side rendering with streaming and file-based routing.

- [TanStack Router Docs](https://tanstack.com/router)

## Start a new project based on this example

To start a new project based on this example, run:

```sh
npx gitpick TanStack/router/tree/main/examples/react/basic-ssr-streaming-file-based basic-ssr-streaming-file-based
```

## Getting Started

Install dependencies:

```sh
pnpm install
```

Start the development server:

```sh
pnpm dev
```

## Build

Build for production:

```sh
pnpm build
```

## About This Example

This example demonstrates:

- Server-side rendering with streaming
- File-based routing
- Progressive rendering
- Suspense boundaries
- Optimized time-to-first-byte
">
<input type="hidden" name="project[files][package.json]" value="{&quot;name&quot;:&quot;tanstack-router-react-example-basic-ssr-streaming-file-based&quot;,&quot;private&quot;:true,&quot;type&quot;:&quot;module&quot;,&quot;scripts&quot;:{&quot;dev&quot;:&quot;node server&quot;,&quot;build&quot;:&quot;npm run build:client &amp;&amp; npm run build:server&quot;,&quot;build:client&quot;:&quot;vite build&quot;,&quot;build:server&quot;:&quot;vite build --ssr&quot;,&quot;serve&quot;:&quot;NODE_ENV=production node server&quot;,&quot;debug&quot;:&quot;node --inspect-brk server&quot;},&quot;dependencies&quot;:{&quot;@tanstack/react-router&quot;:&quot;https://pkg.pr.new/TanStack/router/@tanstack/react-router@e1c34ffdf31d70311e79f524a1c35a5fa644ea03&quot;,&quot;@tanstack/router-plugin&quot;:&quot;https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@e1c34ffdf31d70311e79f524a1c35a5fa644ea03&quot;,&quot;compression&quot;:&quot;^1.8.0&quot;,&quot;express&quot;:&quot;^4.21.2&quot;,&quot;get-port&quot;:&quot;^7.1.0&quot;,&quot;node-fetch&quot;:&quot;^3.3.2&quot;,&quot;react&quot;:&quot;^19.0.0&quot;,&quot;react-dom&quot;:&quot;^19.0.0&quot;},&quot;devDependencies&quot;:{&quot;@tanstack/react-router-devtools&quot;:&quot;https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@e1c34ffdf31d70311e79f524a1c35a5fa644ea03&quot;,&quot;@types/express&quot;:&quot;^4.17.23&quot;,&quot;@types/react&quot;:&quot;^19.0.8&quot;,&quot;@types/react-dom&quot;:&quot;^19.0.1&quot;,&quot;@vitejs/plugin-react&quot;:&quot;^4.3.4&quot;,&quot;typescript&quot;:&quot;^5.7.2&quot;,&quot;vite&quot;:&quot;^7.3.1&quot;}}">
<input type="hidden" name="project[files][server.js]" value="import path from &#39;node:path&#39;
import express from &#39;express&#39;
import getPort, { portNumbers } from &#39;get-port&#39;
import * as zlib from &#39;node:zlib&#39;

const isTest = process.env.NODE_ENV === &#39;test&#39; || !!process.env.VITE_TEST_BUILD

export async function createServer(
  root = process.cwd(),
  isProd = process.env.NODE_ENV === &#39;production&#39;,
  hmrPort,
) {
  const app = express()

  /**
   * @type {import(&#39;vite&#39;).ViteDevServer}
   */
  let vite
  if (!isProd) {
    vite = await (
      await import(&#39;vite&#39;)
    ).createServer({
      root,
      logLevel: isTest ? &#39;error&#39; : &#39;info&#39;,
      server: {
        middlewareMode: true,
        watch: {
          // During tests we edit the files too fast and sometimes chokidar
          // misses change events, so enforce polling for consistency
          usePolling: true,
          interval: 100,
        },
        hmr: {
          port: hmrPort,
        },
      },
      appType: &#39;custom&#39;,
    })
    // use vite&#39;s connect instance as middleware
    app.use(vite.middlewares)
  } else {
    app.use(
      (await import(&#39;compression&#39;)).default({
        brotli: {
          flush: zlib.constants.BROTLI_OPERATION_FLUSH,
        },
        flush: zlib.constants.Z_SYNC_FLUSH,
      }),
    )
  }

  if (isProd) app.use(express.static(&#39;./dist/client&#39;))

  app.use(&#39;*&#39;, async (req, res) =&gt; {
    try {
      const url = req.originalUrl

      if (path.extname(url) !== &#39;&#39;) {
        console.warn(`${url} is not valid router path`)
        res.status(404)
        res.end(`${url} is not valid router path`)
        return
      }

      // Best effort extraction of the head from vite&#39;s index transformation hook
      let viteHead = !isProd
        ? await vite.transformIndexHtml(
            url,
            `&lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;`,
          )
        : &#39;&#39;

      viteHead = viteHead.substring(
        viteHead.indexOf(&#39;&lt;head&gt;&#39;) + 6,
        viteHead.indexOf(&#39;&lt;/head&gt;&#39;),
      )

      const entry = await (async () =&gt; {
        if (!isProd) {
          return vite.ssrLoadModule(&#39;/src/entry-server.tsx&#39;)
        } else {
          return import(&#39;./dist/server/entry-server.js&#39;)
        }
      })()

      console.info(&#39;Rendering: &#39;, url, &#39;...&#39;)
      entry.render({ req, res, head: viteHead })
    } catch (e) {
      !isProd &amp;&amp; vite.ssrFixStacktrace(e)
      console.info(e.stack)
      res.status(500).end(e.stack)
    }
  })

  return { app, vite }
}

if (!isTest) {
  createServer().then(async ({ app }) =&gt;
    app.listen(await getPort({ port: portNumbers(3000, 3100) }), () =&gt; {
      console.info(&#39;Client Server: http://localhost:3000&#39;)
    }),
  )
}
">
<input type="hidden" name="project[files][tsconfig.json]" value="{
  &quot;compilerOptions&quot;: {
    &quot;strict&quot;: true,
    &quot;esModuleInterop&quot;: true,
    &quot;jsx&quot;: &quot;react-jsx&quot;,
    &quot;target&quot;: &quot;esnext&quot;,
    &quot;module&quot;: &quot;esnext&quot;,
    &quot;types&quot;: [&quot;vite/client&quot;],
    &quot;moduleResolution&quot;: &quot;bundler&quot;,
    &quot;lib&quot;: [&quot;DOM&quot;, &quot;DOM.Iterable&quot;, &quot;ES2022&quot;],
    &quot;skipLibCheck&quot;: true
  },
  &quot;include&quot;: [&quot;src&quot;, &quot;vite.config.ts&quot;]
}
">
<input type="hidden" name="project[files][vite.config.ts]" value="import path from &#39;node:path&#39;
import url from &#39;node:url&#39;
import { defineConfig } from &#39;vite&#39;
import react from &#39;@vitejs/plugin-react&#39;
import { tanstackRouter } from &#39;@tanstack/router-plugin/vite&#39;
import type { BuildEnvironmentOptions } from &#39;vite&#39;

const __filename = url.fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

// SSR configuration
const ssrBuildConfig: BuildEnvironmentOptions = {
  ssr: true,
  outDir: &#39;dist/server&#39;,
  ssrEmitAssets: true,
  copyPublicDir: false,
  emptyOutDir: true,
  rollupOptions: {
    input: path.resolve(__dirname, &#39;src/entry-server.tsx&#39;),
    output: {
      entryFileNames: &#39;[name].js&#39;,
      chunkFileNames: &#39;assets/[name]-[hash].js&#39;,
      assetFileNames: &#39;assets/[name]-[hash][extname]&#39;,
    },
  },
}

// Client-specific configuration
const clientBuildConfig: BuildEnvironmentOptions = {
  outDir: &#39;dist/client&#39;,
  emitAssets: true,
  copyPublicDir: true,
  emptyOutDir: true,
  rollupOptions: {
    input: path.resolve(__dirname, &#39;src/entry-client.tsx&#39;),
    output: {
      entryFileNames: &#39;static/[name].js&#39;,
      chunkFileNames: &#39;static/assets/[name]-[hash].js&#39;,
      assetFileNames: &#39;static/assets/[name]-[hash][extname]&#39;,
    },
  },
}

// https://vitejs.dev/config/
export default defineConfig((configEnv) =&gt; {
  return {
    plugins: [
      tanstackRouter({ target: &#39;react&#39;, autoCodeSplitting: true }),
      react(),
    ],
    build: configEnv.isSsrBuild ? ssrBuildConfig : clientBuildConfig,
  }
})
">
<input type="hidden" name="project[files][.vscode/settings.json]" value="{
  &quot;files.watcherExclude&quot;: {
    &quot;**/routeTree.gen.ts&quot;: true
  },
  &quot;search.exclude&quot;: {
    &quot;**/routeTree.gen.ts&quot;: true
  },
  &quot;files.readonlyInclude&quot;: {
    &quot;**/routeTree.gen.ts&quot;: true
  }
}
">
<input type="hidden" name="project[files][src/entry-client.tsx]" value="import { hydrateRoot } from &#39;react-dom/client&#39;
import { RouterClient } from &#39;@tanstack/react-router/ssr/client&#39;
import { createRouter } from &#39;./router&#39;

const router = createRouter()

hydrateRoot(document, &lt;RouterClient router={router} /&gt;)
">
<input type="hidden" name="project[files][src/entry-server.tsx]" value="import { pipeline } from &#39;node:stream/promises&#39;
import {
  RouterServer,
  createRequestHandler,
  renderRouterToStream,
} from &#39;@tanstack/react-router/ssr/server&#39;
import { createRouter } from &#39;./router&#39;
import type express from &#39;express&#39;
import &#39;./fetch-polyfill&#39;

export async function render({
  req,
  res,
  head,
}: {
  head: string
  req: express.Request
  res: express.Response
}) {
  // Convert the express request to a fetch request
  const url = new URL(req.originalUrl || req.url, &#39;http://localhost:3000&#39;).href

  const request = new Request(url, {
    method: req.method,
    headers: (() =&gt; {
      const headers = new Headers()
      for (const [key, value] of Object.entries(req.headers)) {
        headers.set(key, value as any)
      }
      return headers
    })(),
  })

  // Create a request handler
  const handler = createRequestHandler({
    request,
    createRouter: () =&gt; {
      const router = createRouter()

      // Update each router instance with the head info from vite
      router.update({
        context: {
          ...router.options.context,
          head: head,
        },
      })
      return router
    },
  })

  // Let&#39;s use the default stream handler to create the response
  const response = await handler(({ request, responseHeaders, router }) =&gt;
    renderRouterToStream({
      request,
      responseHeaders,
      router,
      children: &lt;RouterServer router={router} /&gt;,
    }),
  )

  // Convert the fetch response back to an express response
  res.statusMessage = response.statusText
  res.status(response.status)

  response.headers.forEach((value, name) =&gt; {
    res.setHeader(name, value)
  })

  // Stream the response body
  return pipeline(response.body as any, res)
}
">
<input type="hidden" name="project[files][src/fetch-polyfill.js]" value="// fetch-polyfill.js
import fetch, {
  Blob,
  blobFrom,
  blobFromSync,
  File,
  fileFrom,
  fileFromSync,
  FormData,
  Headers,
  Request,
  Response,
} from &#39;node-fetch&#39;

if (!globalThis.fetch) {
  globalThis.fetch = fetch
  globalThis.Headers = Headers
  globalThis.Request = Request
  globalThis.Response = Response
}
">
<input type="hidden" name="project[files][src/routeTree.gen.ts]" value="/* eslint-disable */

// @ts-nocheck

// noinspection JSUnusedGlobalSymbols

// This file was automatically generated by TanStack Router.
// You should NOT make any changes in this file as it will be overwritten.
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.

import { Route as rootRouteImport } from &#39;./routes/__root&#39;
import { Route as ErrorRouteImport } from &#39;./routes/error&#39;
import { Route as PostsRouteRouteImport } from &#39;./routes/posts/route&#39;
import { Route as IndexRouteImport } from &#39;./routes/index&#39;
import { Route as PostsIndexRouteImport } from &#39;./routes/posts/index&#39;
import { Route as PostsPostIdRouteImport } from &#39;./routes/posts/$postId&#39;

const ErrorRoute = ErrorRouteImport.update({
  id: &#39;/error&#39;,
  path: &#39;/error&#39;,
  getParentRoute: () =&gt; rootRouteImport,
} as any)
const PostsRouteRoute = PostsRouteRouteImport.update({
  id: &#39;/posts&#39;,
  path: &#39;/posts&#39;,
  getParentRoute: () =&gt; rootRouteImport,
} as any)
const IndexRoute = IndexRouteImport.update({
  id: &#39;/&#39;,
  path: &#39;/&#39;,
  getParentRoute: () =&gt; rootRouteImport,
} as any)
const PostsIndexRoute = PostsIndexRouteImport.update({
  id: &#39;/&#39;,
  path: &#39;/&#39;,
  getParentRoute: () =&gt; PostsRouteRoute,
} as any)
const PostsPostIdRoute = PostsPostIdRouteImport.update({
  id: &#39;/$postId&#39;,
  path: &#39;/$postId&#39;,
  getParentRoute: () =&gt; PostsRouteRoute,
} as any)

export interface FileRoutesByFullPath {
  &#39;/&#39;: typeof IndexRoute
  &#39;/posts&#39;: typeof PostsRouteRouteWithChildren
  &#39;/error&#39;: typeof ErrorRoute
  &#39;/posts/$postId&#39;: typeof PostsPostIdRoute
  &#39;/posts/&#39;: typeof PostsIndexRoute
}
export interface FileRoutesByTo {
  &#39;/&#39;: typeof IndexRoute
  &#39;/error&#39;: typeof ErrorRoute
  &#39;/posts/$postId&#39;: typeof PostsPostIdRoute
  &#39;/posts&#39;: typeof PostsIndexRoute
}
export interface FileRoutesById {
  __root__: typeof rootRouteImport
  &#39;/&#39;: typeof IndexRoute
  &#39;/posts&#39;: typeof PostsRouteRouteWithChildren
  &#39;/error&#39;: typeof ErrorRoute
  &#39;/posts/$postId&#39;: typeof PostsPostIdRoute
  &#39;/posts/&#39;: typeof PostsIndexRoute
}
export interface FileRouteTypes {
  fileRoutesByFullPath: FileRoutesByFullPath
  fullPaths: &#39;/&#39; | &#39;/posts&#39; | &#39;/error&#39; | &#39;/posts/$postId&#39; | &#39;/posts/&#39;
  fileRoutesByTo: FileRoutesByTo
  to: &#39;/&#39; | &#39;/error&#39; | &#39;/posts/$postId&#39; | &#39;/posts&#39;
  id: &#39;__root__&#39; | &#39;/&#39; | &#39;/posts&#39; | &#39;/error&#39; | &#39;/posts/$postId&#39; | &#39;/posts/&#39;
  fileRoutesById: FileRoutesById
}
export interface RootRouteChildren {
  IndexRoute: typeof IndexRoute
  PostsRouteRoute: typeof PostsRouteRouteWithChildren
  ErrorRoute: typeof ErrorRoute
}

declare module &#39;@tanstack/react-router&#39; {
  interface FileRoutesByPath {
    &#39;/error&#39;: {
      id: &#39;/error&#39;
      path: &#39;/error&#39;
      fullPath: &#39;/error&#39;
      preLoaderRoute: typeof ErrorRouteImport
      parentRoute: typeof rootRouteImport
    }
    &#39;/posts&#39;: {
      id: &#39;/posts&#39;
      path: &#39;/posts&#39;
      fullPath: &#39;/posts&#39;
      preLoaderRoute: typeof PostsRouteRouteImport
      parentRoute: typeof rootRouteImport
    }
    &#39;/&#39;: {
      id: &#39;/&#39;
      path: &#39;/&#39;
      fullPath: &#39;/&#39;
      preLoaderRoute: typeof IndexRouteImport
      parentRoute: typeof rootRouteImport
    }
    &#39;/posts/&#39;: {
      id: &#39;/posts/&#39;
      path: &#39;/&#39;
      fullPath: &#39;/posts/&#39;
      preLoaderRoute: typeof PostsIndexRouteImport
      parentRoute: typeof PostsRouteRoute
    }
    &#39;/posts/$postId&#39;: {
      id: &#39;/posts/$postId&#39;
      path: &#39;/$postId&#39;
      fullPath: &#39;/posts/$postId&#39;
      preLoaderRoute: typeof PostsPostIdRouteImport
      parentRoute: typeof PostsRouteRoute
    }
  }
}

interface PostsRouteRouteChildren {
  PostsPostIdRoute: typeof PostsPostIdRoute
  PostsIndexRoute: typeof PostsIndexRoute
}

const PostsRouteRouteChildren: PostsRouteRouteChildren = {
  PostsPostIdRoute: PostsPostIdRoute,
  PostsIndexRoute: PostsIndexRoute,
}

const PostsRouteRouteWithChildren = PostsRouteRoute._addFileChildren(
  PostsRouteRouteChildren,
)

const rootRouteChildren: RootRouteChildren = {
  IndexRoute: IndexRoute,
  PostsRouteRoute: PostsRouteRouteWithChildren,
  ErrorRoute: ErrorRoute,
}
export const routeTree = rootRouteImport
  ._addFileChildren(rootRouteChildren)
  ._addFileTypes&lt;FileRouteTypes&gt;()
">
<input type="hidden" name="project[files][src/router.tsx]" value="import { createRouter as createReactRouter } from &#39;@tanstack/react-router&#39;

import { routeTree } from &#39;./routeTree.gen&#39;

export function createRouter() {
  return createReactRouter({
    routeTree,
    context: {
      head: &#39;&#39;,
    },
    defaultPreload: &#39;intent&#39;,
    scrollRestoration: true,
  })
}

declare module &#39;@tanstack/react-router&#39; {
  interface Register {
    router: ReturnType&lt;typeof createRouter&gt;
  }
}
">
<input type="hidden" name="project[files][src/routerContext.tsx]" value="export type RouterContext = {
  head: string
}
">
<input type="hidden" name="project[files][public/images/favicon.ico]" value="https://pkg.pr.new/template/32277fcd-b1b9-4cf7-ba2e-bc5e70118ac8">
<input type="hidden" name="project[files][src/routes/__root.tsx]" value="import { TanStackRouterDevtools } from &#39;@tanstack/react-router-devtools&#39;
import * as React from &#39;react&#39;
import {
  HeadContent,
  Link,
  Outlet,
  Scripts,
  createRootRouteWithContext,
} from &#39;@tanstack/react-router&#39;
import type { RouterContext } from &#39;../routerContext&#39;

export const Route = createRootRouteWithContext&lt;RouterContext&gt;()({
  head: () =&gt; ({
    links: [{ rel: &#39;icon&#39;, href: &#39;/images/favicon.ico&#39; }],
    meta: [
      {
        title: &#39;TanStack Router SSR Basic File Based Streaming&#39;,
      },
      {
        charSet: &#39;UTF-8&#39;,
      },
      {
        name: &#39;viewport&#39;,
        content: &#39;width=device-width, initial-scale=1.0&#39;,
      },
    ],
    scripts: [
      {
        src: &#39;https://unpkg.com/@tailwindcss/browser@4&#39;,
      },
      ...(!import.meta.env.PROD
        ? [
            {
              type: &#39;module&#39;,
              children: `import RefreshRuntime from &quot;/@react-refresh&quot;
  RefreshRuntime.injectIntoGlobalHook(window)
  window.$RefreshReg$ = () =&gt; {}
  window.$RefreshSig$ = () =&gt; (type) =&gt; type
  window.__vite_plugin_react_preamble_installed__ = true`,
            },
            {
              type: &#39;module&#39;,
              src: &#39;/@vite/client&#39;,
            },
          ]
        : []),
      {
        type: &#39;module&#39;,
        src: import.meta.env.PROD
          ? &#39;/static/entry-client.js&#39;
          : &#39;/src/entry-client.tsx&#39;,
      },
    ],
  }),
  component: RootComponent,
})

function RootComponent() {
  return (
    &lt;html lang=&quot;en&quot;&gt;
      &lt;head&gt;
        &lt;HeadContent /&gt;
      &lt;/head&gt;
      &lt;body&gt;
        &lt;div className=&quot;p-2 flex gap-2 text-lg&quot;&gt;
          &lt;Link
            to=&quot;/&quot;
            activeProps={{
              className: &#39;font-bold&#39;,
            }}
            activeOptions={{ exact: true }}
          &gt;
            Home
          &lt;/Link&gt;{&#39; &#39;}
          &lt;Link
            to=&quot;/posts&quot;
            activeProps={{
              className: &#39;font-bold&#39;,
            }}
          &gt;
            Posts
          &lt;/Link&gt;{&#39; &#39;}
          &lt;Link
            to=&quot;/error&quot;
            activeProps={{
              className: &#39;font-bold&#39;,
            }}
          &gt;
            Error
          &lt;/Link&gt;
        &lt;/div&gt;
        &lt;hr /&gt;
        &lt;Outlet /&gt; {/* Start rendering router matches */}
        &lt;TanStackRouterDevtools position=&quot;bottom-right&quot; /&gt;
        &lt;Scripts /&gt;
      &lt;/body&gt;
    &lt;/html&gt;
  )
}
">
<input type="hidden" name="project[files][src/routes/error.tsx]" value="import { Await, createFileRoute } from &#39;@tanstack/react-router&#39;
import * as React from &#39;react&#39;

async function loadData() {
  await new Promise((r) =&gt; setTimeout(r, 2000))
  if (Math.random() &gt; 0.5) throw new Error(&#39;Random error in streaming!&#39;)
  return &#39;Hello!&#39;
}

export const Route = createFileRoute(&#39;/error&#39;)({
  component: ErrorComponent,
  loader: () =&gt; {
    if (Math.random() &gt; 0.5) throw new Error(&#39;Random error!&#39;)
    return {
      deferredData: loadData(),
    }
  },
  pendingComponent: () =&gt; &lt;p&gt;Loading..&lt;/p&gt;,
  wrapInSuspense: true,
  errorComponent: ({ error }) =&gt; {
    return (
      &lt;div className=&quot;p-2&quot;&gt;
        &lt;h3&gt;Caught: {error.message}&lt;/h3&gt;
        &lt;p&gt;(This page has a 75% chance of throwing an error)&lt;/p&gt;
      &lt;/div&gt;
    )
  },
})

function ErrorComponent() {
  return (
    &lt;div className=&quot;p-2&quot;&gt;
      &lt;h3&gt;
        The loader of this page will have a 75% chance of throwing an error!
      &lt;/h3&gt;

      &lt;Await promise={Route.useLoaderData().deferredData}&gt;
        {(data) =&gt; &lt;p&gt;Streaming data loaded successfully: {data}&lt;/p&gt;}
      &lt;/Await&gt;
    &lt;/div&gt;
  )
}
">
<input type="hidden" name="project[files][src/routes/index.tsx]" value="import { Await, createFileRoute } from &#39;@tanstack/react-router&#39;
import * as React from &#39;react&#39;

export const Route = createFileRoute(&#39;/&#39;)({
  loader: () =&gt; ({
    date: new Date(),
    deferred: new Promise&lt;{ date: Date }&gt;((r) =&gt;
      setTimeout(() =&gt; r({ date: new Date() }), 1000),
    ),
  }),
  component: IndexComponent,
})

function IndexComponent() {
  const data = Route.useLoaderData()

  return (
    &lt;div className=&quot;p-2&quot;&gt;
      &lt;h3&gt;Welcome Home!&lt;/h3&gt;
      &lt;p&gt;Data: {data.date.getDate()}&lt;/p&gt;
      &lt;Await promise={data.deferred} fallback=&quot;Loading...&quot;&gt;
        {(data) =&gt; &lt;p&gt;Deferred: {new Date(data.date).getDate()}&lt;/p&gt;}
      &lt;/Await&gt;
    &lt;/div&gt;
  )
}
">
<input type="hidden" name="project[files][src/routes/posts/$postId.tsx]" value="import { Await, createFileRoute, notFound } from &#39;@tanstack/react-router&#39;
import * as React from &#39;react&#39;
import type { PostType } from &#39;./route&#39;

async function fetchPostById(postId: string) {
  console.info(`Fetching post with id ${postId}...`)

  await new Promise((r) =&gt; setTimeout(r, 100 + Math.round(Math.random() * 100)))

  const res = await fetch(
    `https://jsonplaceholder.typicode.com/posts/${postId}`,
  )

  if (res.status === 404) throw notFound()

  return res.json() as Promise&lt;PostType&gt;
}

export type CommentType = {
  id: string
  postId: string
  name: string
  email: string
  body: string
}

async function fetchComments(postId: string) {
  await new Promise((r) =&gt; setTimeout(r, 2000))

  return fetch(
    `https://jsonplaceholder.typicode.com/comments?postId=${postId}`,
  ).then((r) =&gt; r.json() as Promise&lt;Array&lt;CommentType&gt;&gt;)
}

export const Route = createFileRoute(&#39;/posts/$postId&#39;)({
  loader: async ({ params: { postId } }) =&gt; {
    const commentsPromise = fetchComments(postId)
    const post = await fetchPostById(postId)

    return {
      post,
      commentsPromise: commentsPromise,
    }
  },
  wrapInSuspense: true,
  errorComponent: ({ error }) =&gt; {
    return &lt;div&gt;Failed to load post: {(error as any).message}&lt;/div&gt;
  },
  notFoundComponent: () =&gt; {
    return &lt;div&gt;Post not found&lt;/div&gt;
  },
  component: PostComponent,
})

function PostComponent() {
  const { post, commentsPromise } = Route.useLoaderData()

  return (
    &lt;div className=&quot;space-y-2&quot;&gt;
      &lt;h4 className=&quot;text-xl font-bold underline&quot;&gt;{post.title}&lt;/h4&gt;
      &lt;div className=&quot;text-sm&quot;&gt;{post.body}&lt;/div&gt;
      &lt;Await
        promise={commentsPromise}
        fallback={&lt;div&gt;Loading comments...&lt;/div&gt;}
        key={post.id}
      &gt;
        {(comments) =&gt; {
          return (
            &lt;div className=&quot;space-y-2&quot;&gt;
              &lt;h5 className=&quot;text-lg font-bold underline&quot;&gt;Comments&lt;/h5&gt;
              {comments.map((comment) =&gt; {
                return (
                  &lt;div key={comment.id}&gt;
                    &lt;h6 className=&quot;text-md font-bold&quot;&gt;{comment.name}&lt;/h6&gt;
                    &lt;div className=&quot;text-sm italic opacity-50&quot;&gt;
                      {comment.email}
                    &lt;/div&gt;
                    &lt;div className=&quot;text-sm&quot;&gt;{comment.body}&lt;/div&gt;
                  &lt;/div&gt;
                )
              })}
            &lt;/div&gt;
          )
        }}
      &lt;/Await&gt;
    &lt;/div&gt;
  )
}
">
<input type="hidden" name="project[files][src/routes/posts/index.tsx]" value="import { createFileRoute } from &#39;@tanstack/react-router&#39;
import * as React from &#39;react&#39;

export const Route = createFileRoute(&#39;/posts/&#39;)({
  component: PostsIndexComponent,
  wrapInSuspense: true,
  errorComponent: ({ error }) =&gt; {
    return (
      &lt;div className=&quot;text-red-500&quot;&gt;Failed to load post: {error.message}&lt;/div&gt;
    )
  },
})

function PostsIndexComponent() {
  return &lt;div&gt;Select a post.&lt;/div&gt;
}
">
<input type="hidden" name="project[files][src/routes/posts/route.tsx]" value="import * as React from &#39;react&#39;
import { Link, Outlet, createFileRoute } from &#39;@tanstack/react-router&#39;

export type PostType = {
  id: string
  title: string
  body: string
}

export const Route = createFileRoute(&#39;/posts&#39;)({
  loader: async () =&gt; {
    console.info(&#39;Fetching posts...&#39;)
    await new Promise((r) =&gt;
      setTimeout(r, 300 + Math.round(Math.random() * 300)),
    )
    return fetch(&#39;https://jsonplaceholder.typicode.com/posts&#39;)
      .then((d) =&gt; d.json() as Promise&lt;Array&lt;PostType&gt;&gt;)
      .then((d) =&gt; d.slice(0, 10))
  },
  component: PostsComponent,
})

function PostsComponent() {
  const posts = Route.useLoaderData()

  return (
    &lt;div className=&quot;p-2 flex gap-2&quot;&gt;
      &lt;ul className=&quot;list-disc pl-4&quot;&gt;
        {posts.map((post) =&gt; {
          return (
            &lt;li key={post.id} className=&quot;whitespace-nowrap&quot;&gt;
              &lt;Link
                to=&quot;/posts/$postId&quot;
                params={{
                  postId: post.id,
                }}
                className=&quot;block py-1 text-blue-800 hover:text-blue-600&quot;
                activeProps={{ className: &#39;text-black font-bold&#39; }}
              &gt;
                &lt;div&gt;{post.title.substring(0, 20)}&lt;/div&gt;
              &lt;/Link&gt;
            &lt;/li&gt;
          )
        })}
        &lt;li className=&quot;whitespace-nowrap&quot;&gt;
          &lt;Link
            to=&quot;/posts/$postId&quot;
            params={{
              postId: &#39;does-not-exist&#39;,
            }}
            className=&quot;block py-1 text-blue-800 hover:text-blue-600&quot;
            activeProps={{ className: &#39;text-black font-bold&#39; }}
          &gt;
            &lt;div&gt;This post does not exist&lt;/div&gt;
          &lt;/Link&gt;
        &lt;/li&gt;
      &lt;/ul&gt;
      &lt;hr /&gt;
      &lt;Outlet /&gt;
    &lt;/div&gt;
  )
}
">
<input type="hidden" name="project[description]" value="generated by https://pkg.pr.new">
<input type="hidden" name="project[template]" value="node">
<input type="hidden" name="project[title]" value="tanstack-router-react-example-basic-ssr-streaming-file-based">
</form>
<script>document.getElementById("mainForm").submit();</script>

</body></html>