server-components

Implement, review, debug, and refactor TanStack Start React Server Components in React 19 apps. Use when tasks mention @tanstack/react-start/rsc, renderServerComponent, createCompositeComponent, CompositeComponent, renderToReadableStream, createFromReadableStream, createFromFetch, Composite Components, React Flight streams, loader or query owned RSC caching, router.invalidate, structuralSharing: false, selective SSR, stale names like renderRsc or .validator, or migration from Next App Router RSC patterns. Do not use for generic SSR or non-TanStack RSC frameworks except brief comparison.

Install
npx skills add 'https://github.com/TanStack/router/tree/main/packages/react-start/skills/react-start/server-components'
Download bundle ↓
main · 8e164d2Scanned 2026-09-15

Contributors

GitHub-linked commit authors for this SKILL.md at the saved revision. Co-authors and history before file renames are not included.

File history ↗
View on GitHub
← Back to SKILL.md
// Query-owned RSC pattern.// Assumes your router context provides a queryClient for SSR prefetch. import type { ReactNode } from 'react'import { createFileRoute } from '@tanstack/react-router'import { createServerFn } from '@tanstack/react-start'import {  CompositeComponent,  createCompositeComponent,} from '@tanstack/react-start/rsc'import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'import { z } from 'zod' // Replace with your own data layerdeclare const db: {  posts: {    findById(postId: string): Promise<{      id: string      title: string      body: string    }>    update(      postId: string,      patch: { title?: string; body?: string },    ): Promise<void>  }} const getPostRsc = createServerFn({ method: 'GET' })  .validator(z.object({ postId: z.string() }))  .handler(async ({ data }) => {    const post = await db.posts.findById(data.postId)     const src = await createCompositeComponent<{      renderActions?: (args: { postId: string }) => ReactNode    }>((props) => (      <article>        <h1>{post.title}</h1>        <p>{post.body}</p>        <footer>{props.renderActions?.({ postId: post.id })}</footer>      </article>    ))     return { src }  }) const updatePost = createServerFn({ method: 'POST' })  .validator(    z.object({      postId: z.string(),      title: z.string().optional(),      body: z.string().optional(),    }),  )  .handler(async ({ data }) => {    await db.posts.update(data.postId, {      title: data.title,      body: data.body,    })  }) const postQueryOptions = (postId: string) => ({  queryKey: ['post-rsc', postId],  structuralSharing: false,  queryFn: () => getPostRsc({ data: { postId } }),  staleTime: 5 * 60 * 1000,}) export const Route = createFileRoute('/posts/$postId')({  loader: async ({ context, params }) => {    await context.queryClient.ensureQueryData(postQueryOptions(params.postId))  },  component: PostPage,}) function PostPage() {  const { postId } = Route.useParams()  const queryClient = useQueryClient()   const { data } = useSuspenseQuery(postQueryOptions(postId))   const handleRename = async () => {    await updatePost({ data: { postId, title: 'Updated title' } })    await queryClient.invalidateQueries({ queryKey: ['post-rsc', postId] })  }   return (    <CompositeComponent      src={data.src}      renderActions={({ postId }) => (        <button type="button" onClick={handleRename}>          Refresh {postId}        </button>      )}    />  )} 
Referenced from SKILL.md