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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/components/StartExampleOverview.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { CodeBlock } from '~/components/markdown'
import { getStartExamplePage } from '~/utils/start-example-pages'

export function StartExampleOverview({
params,
}: {
params: {
libraryId: string
version: string
framework: string
_splat?: string
}
}) {
const page = getStartExamplePage(params)
if (!page) return null
const source = `https://github.com/TanStack/router/blob/main/examples/react/${page.slug}`
return (
<section className="prose dark:prose-invert max-w-3xl px-4 pb-6 lg:px-6">
<p>{page.description}</p>
<p>{page.details}</p>
<h2>Run locally</h2>
<p>
Use Node.js 22.12 or newer and pnpm 11. These examples fetch public
sample data from JSONPlaceholder, so an internet connection is required.
No API key or database is needed.
</p>
<CodeBlock>
<code className="language-sh">{`git clone https://github.com/TanStack/router.git
cd router
pnpm install
cd examples/react/${page.slug}
pnpm dev`}</code>
</CodeBlock>
<p>{page.note}</p>
<p>
Run <code>pnpm build</code> from the example directory to build the app
and check its types.
</p>
<h2>Files to follow</h2>
<ul>
{page.files.map((file) => (
<li key={file.path}>
<a href={`${source}/${file.path}`}>{file.path}</a>
{`: ${file.description}`}
</li>
))}
</ul>
<p>
<a href={`/start/latest/docs/framework/react/guide/${page.guide}`}>
{page.guideTitle}
</a>
</p>
</section>
)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { StartExampleOverview } from '~/components/StartExampleOverview'
import { getStartExamplePage } from '~/utils/start-example-pages'
import {
ClientOnly,
isNotFound,
Expand Down Expand Up @@ -268,8 +270,13 @@ export const Route = createFileRoute(
const library = getLibrary(params.libraryId)
const exampleName = slugToTitle(params._splat || '')
const frameworkName = capitalize(params.framework)
const ogTitle = `${frameworkName} ${library.name} ${exampleName} Example`
const ogDescription = `An example showing how to implement ${exampleName} in ${frameworkName} using ${library.name}.`
const overview = getStartExamplePage(params)
const ogTitle =
overview?.title ??
`${frameworkName} ${library.name} ${exampleName} Example`
const ogDescription =
overview?.description ??
`An example showing how to implement ${exampleName} in ${frameworkName} using ${library.name}.`

const canonicalHref = canonicalUrl(
loaderData?.canonicalPathOverride ?? buildExamplePath(params),
Expand Down Expand Up @@ -532,7 +539,9 @@ function ExternalExamplePage({
<div className="p-4 lg:p-6">
<DocTitle>
<span>
{capitalize(framework)} Example: {slugToTitle(_splat!)}
{getStartExamplePage({ libraryId, version, framework, _splat })
?.title ??
`${capitalize(framework)} Example: ${slugToTitle(_splat!)}`}
</span>
<div className="flex items-center gap-4 flex-wrap font-normal text-xs">
{orderedExampleDeployProviders.map((provider) =>
Expand Down Expand Up @@ -569,6 +578,9 @@ function ExternalExamplePage({
</div>
</DocTitle>
</div>
<StartExampleOverview
params={{ libraryId, version, framework, _splat }}
/>
<div className="flex-1 lg:px-6 flex flex-col min-h-0">
<CodeExplorer
activeTab={activeTab}
Expand Down Expand Up @@ -634,7 +646,9 @@ function ClientExamplePage({
<div className="p-4 lg:p-6">
<DocTitle>
<span>
{capitalize(framework)} Example: {slugToTitle(_splat!)}
{getStartExamplePage({ libraryId, version, framework, _splat })
?.title ??
`${capitalize(framework)} Example: ${slugToTitle(_splat!)}`}
</span>
<a
href={githubUrl}
Expand All @@ -646,6 +660,9 @@ function ClientExamplePage({
</a>
</DocTitle>
</div>
<StartExampleOverview
params={{ libraryId, version, framework, _splat }}
/>
<div className="flex min-h-0 flex-1 flex-col lg:px-6">
<ClientOnly fallback={fallback}>
<React.Suspense fallback={fallback}>
Expand Down
2 changes: 2 additions & 0 deletions src/utils/sitemap.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { getStartExampleSitemapEntries } from './start-example-pages'
import { getBranch, libraries } from '~/libraries'
import type { LibrarySlim } from '~/libraries/types'
import { getPublishedPosts } from '~/utils/blog'
Expand Down Expand Up @@ -118,6 +119,7 @@ export async function getSitemapEntries(): Promise<Array<SitemapEntry>> {
const entries = [
...HIGH_VALUE_NON_DOC_PAGES.map((path) => ({ path })),
...getLibraryEntries(),
...getStartExampleSitemapEntries(),
...docsEntries.flat(),
...getBlogEntries(),
...getPartnerSitemapEntries(),
Expand Down
102 changes: 102 additions & 0 deletions src/utils/start-example-pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
const startExamplePages = [
{
slug: 'start-basic',
title: 'TanStack Start routing and server functions example',
description:
'Explore file-based routes, nested layouts, server functions, and server-rendered pages in a runnable React application.',
details:
'Browse posts and users, open a detail URL directly, and follow links between nested layouts. The post loaders call server functions, so you can follow the request from a route to the server and back to the page.',
files: [
{
path: 'src/router.tsx',
description:
'Creates the router and configures shared loading and error behavior.',
},
{
path: 'src/routes/posts.tsx',
description: 'Loads the post list for the route.',
},
{
path: 'src/utils/posts.tsx',
description: 'Fetches sample posts inside server functions.',
},
],
guide: 'routing',
guideTitle: 'Routing guide',
note: 'Open the local URL printed by Vite. Try a post detail URL in a new tab to see a direct server-rendered request.',
},
{
slug: 'start-basic-react-query',
title: 'TanStack Start with React Query example',
description:
'Connect React Query to TanStack Start for server rendering, route preloading, hydration, and cached client navigation.',
details:
'Route loaders prepare query data before rendering. Components read the same query options, while the Router integration transfers server-fetched query data to the browser. Follow the post list into a detail page to see how route loading and query caching work together.',
files: [
{
path: 'src/router.tsx',
description:
'Creates a QueryClient for the router and connects the SSR query integration.',
},
{
path: 'src/utils/posts.tsx',
description: 'Shares query keys and server-function query options.',
},
{
path: 'src/routes/posts.$postId.tsx',
description:
'Preloads and renders a post using the shared query options.',
},
],
guide: 'tanstack-query',
guideTitle: 'React Query integration guide',
note: 'Open the local URL printed by Vite. Visit a post directly, navigate back to the list, and inspect query state with the included devtools.',
},
{
slug: 'start-basic-static',
title: 'TanStack Start static rendering example',
description:
'Explore SPA mode, prerendered routes, and static server-function results in a React application built with TanStack Start.',
details:
'This example combines SPA mode with link crawling during prerendering. Its post functions use static-function middleware to capture results for static output. Use it to understand the build configuration before adapting it to your own public content.',
files: [
{
path: 'vite.config.ts',
description:
'Configures the /test/ base path, SPA prerendering, link crawling, and sitemap host.',
},
{
path: 'src/utils/posts.tsx',
description: 'Uses static-function middleware for public sample posts.',
},
{
path: 'src/routes/posts.$postId.tsx',
description: 'Loads and displays an individual post.',
},
],
guide: 'static-prerendering',
guideTitle: 'Static prerendering guide',
note: 'Visit http://localhost:3000/test/ in development. Before publishing, replace the sample sitemap host and base path. The example sets failOnError to false, so inspect build output for failed pages. Static output is a build-time snapshot, not a live database.',
},
]

export function getStartExamplePage(params: {
libraryId: string
version: string
framework: string
_splat?: string
}) {
if (
params.libraryId !== 'start' ||
params.framework !== 'react' ||
params.version !== 'latest'
)
return undefined
return startExamplePages.find((page) => page.slug === params._splat)
}

export function getStartExampleSitemapEntries() {
return startExamplePages.map((page) => ({
path: `/start/latest/docs/framework/react/examples/${page.slug}`,
}))
}
Loading