How shadcn/ui Supports React Server Components (RSC) and Server-Side Rendering (SSR)

shadcn/ui enables React Server Components and SSR through a single rsc configuration flag that controls source-code transformation and a generated router registration that activates server-side rendering.

The shadcn/ui toolkit treats server rendering as a first-class concern, allowing developers to opt into React Server Components during project initialization and automatically handling the "use client" directives that determine component boundaries. This architecture ensures that components can be rendered entirely on the server where appropriate, while still supporting client-side interactivity when needed.

The RSC Configuration Flag

shadcn/ui introduces RSC support at the schema level through a boolean rsc property defined in packages/shadcn/src/schema.ts. This property defaults to false but can be enabled during project setup:

// packages/shadcn/src/schema.ts
export const rawConfigSchema = z.object({
  // ... other fields
  rsc: z.coerce.boolean().default(false),
})

When you run npx shadcn-ui@latest init, the CLI prompts for RSC support and persists the answer in components.json:

? Are you using React Server Components? (yes/no)  (Y/n) y

This configuration is subsequently loaded by getConfig in packages/shadcn/src/utils/get-config.ts, making config.rsc available throughout the CLI's transformation pipeline.

Source Code Transformation for RSC

The core mechanism for RSC support resides in packages/shadcn/src/utils/transformers/transform-rsc.ts. This transformer conditionally removes "use client" directives based on the rsc configuration:

// packages/shadcn/src/utils/transformers/transform-rsc.ts
export const transformRsc: Transformer = async ({ sourceFile, config }) => {
  // If RSC is enabled, preserve the directive (component stays client-side capable)
  if (config.rsc) {
    return sourceFile
  }

  // Otherwise, strip "use client" to make the component server-compatible
  const first = sourceFile.getFirstChildByKind(SyntaxKind.ExpressionStatement)
  if (first && /^["']use client["']$/.test(first.getText())) {
    first.remove()
  }
  return sourceFile
}

When config.rsc is true, the transformer becomes a no-op, preserving any "use client" directives that mark interactive components. When false, it strips these directives, effectively converting components to server-compatible modules.

This transformer integrates into the file update pipeline in packages/shadcn/src/utils/updaters/update-files.ts:

// packages/shadcn/src/utils/updaters/update-files.ts
const content = await transform(
  { filename: file.path, raw: file.content, config },
  [
    transformImport,
    transformRsc,  // RSC handling
    transformCssVars,
    // ... other transformers
  ]
)

SSR Support in the TanStack Router

For applications using the start-app template, shadcn/ui enables SSR through TanStack Router's registration system. The generated route tree in templates/start-app/src/routeTree.gen.ts declares SSR capability:

// templates/start-app/src/routeTree.gen.ts
declare module '@tanstack/react-start' {
  interface Register {
    ssr: true  // Enables server-side rendering
    router: Awaited<ReturnType<typeof getRouter>>
  }
}

This ssr: true declaration instructs TanStack's runtime to pre-render routes on the server, delivering fully rendered HTML to the client before hydration occurs.

How RSC and SSR Work Together in shadcn/ui

While RSC and SSR are distinct concepts, shadcn/ui coordinates them through configuration:

Feature Configuration Implementation Effect
RSC rsc: true in components.json transform-rsc.ts preserves "use client" Components can run on server; client components marked explicitly
SSR ssr: true in route registration routeTree.gen.ts interface TanStack Router pre-renders pages on server

The documentation site itself demonstrates this pattern in apps/v4/lib/rehype.ts, where a display configuration explicitly sets rsc: true for component previews:

// apps/v4/lib/rehype.ts
const buildDisplayConfig = (styleName: string) => ({
  $schema: "https://ui.shadcn.com/schema.json",
  style: styleName,
  rsc: true,  // Preview as server component
  tsx: true,
  // ...
})

Summary

Frequently Asked Questions

What happens if I set rsc: false in my shadcn/ui configuration?

When rsc is set to false, the CLI's transformer (transform-rsc.ts) automatically removes all "use client" directives from component files during installation. This converts the components into standard server-compatible modules, though you lose the ability to use client-side React features (like useState or useEffect) in those specific files unless you manually add the directive back.

How does shadcn/ui detect if my project supports React Server Components?

The CLI includes project detection logic in packages/shadcn/src/utils/get-project-info.ts that checks for the presence of a Next.js app directory. If the app directory exists (indicating Next.js 13+), the CLI sets isRSC to true, which can influence default recommendations during initialization, though the explicit rsc flag in components.json remains the authoritative configuration.

Can I use shadcn/ui with SSR frameworks other than Next.js?

Yes. While the RSC configuration works universally by controlling "use client" directives, the explicit SSR registration shown in templates/start-app/src/routeTree.gen.ts demonstrates TanStack Router integration. For other frameworks (Remix, Astro, etc.), the rsc flag still ensures components are generated without client directives when server rendering is desired, though you must configure the framework's own SSR pipeline separately.

What is the difference between the rsc flag and the ssr flag in shadcn/ui?

The rsc flag controls component-level behavior—specifically whether "use client" directives are preserved or removed from component files, determining if they can run as React Server Components. The ssr flag appears in the TanStack Router template and controls application-level server-side rendering, instructing the router to pre-render pages on the server before sending HTML to the client.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →