Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Apply Next.js best practices for RSC boundaries, async APIs, routing, metadata, and optimization.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
rsc-boundaries.md
1# RSC Boundaries23Detect and prevent invalid patterns when crossing Server/Client component boundaries.45## Detection Rules67### 1. Async Client Components Are Invalid89Client components **cannot** be async functions. Only Server Components can be async.1011**Detect:** File has `'use client'` AND component is `async function` or returns `Promise`1213```tsx14// Bad: async client component15'use client'16export default async function UserProfile() {17const user = await getUser() // Cannot await in client component18return <div>{user.name}</div>19}2021// Good: Remove async, fetch data in parent server component22// page.tsx (server component - no 'use client')23export default async function Page() {24const user = await getUser()25return <UserProfile user={user} />26}2728// UserProfile.tsx (client component)29'use client'30export function UserProfile({ user }: { user: User }) {31return <div>{user.name}</div>32}33```3435```tsx36// Bad: async arrow function client component37'use client'38const Dashboard = async () => {39const data = await fetchDashboard()40return <div>{data}</div>41}4243// Good: Fetch in server component, pass data down44```4546### 2. Non-Serializable Props to Client Components4748Props passed from Server → Client must be JSON-serializable.4950**Detect:** Server component passes these to a client component:51- Functions (except Server Actions with `'use server'`)52- `Date` objects53- `Map`, `Set`, `WeakMap`, `WeakSet`54- Class instances55- `Symbol` (unless globally registered)56- Circular references5758```tsx59// Bad: Function prop60// page.tsx (server)61export default function Page() {62const handleClick = () => console.log('clicked')63return <ClientButton onClick={handleClick} />64}6566// Good: Define function inside client component67// ClientButton.tsx68'use client'69export function ClientButton() {70const handleClick = () => console.log('clicked')71return <button onClick={handleClick}>Click</button>72}73```7475```tsx76// Bad: Date object (silently becomes string, then crashes)77// page.tsx (server)78export default async function Page() {79const post = await getPost()80return <PostCard createdAt={post.createdAt} /> // Date object81}8283// PostCard.tsx (client) - will crash on .getFullYear()84'use client'85export function PostCard({ createdAt }: { createdAt: Date }) {86return <span>{createdAt.getFullYear()}</span> // Runtime error!87}8889// Good: Serialize to string on server90// page.tsx (server)91export default async function Page() {92const post = await getPost()93return <PostCard createdAt={post.createdAt.toISOString()} />94}9596// PostCard.tsx (client)97'use client'98export function PostCard({ createdAt }: { createdAt: string }) {99const date = new Date(createdAt)100return <span>{date.getFullYear()}</span>101}102```103104```tsx105// Bad: Class instance106const user = new UserModel(data)107<ClientProfile user={user} /> // Methods will be stripped108109// Good: Pass plain object110const user = await getUser()111<ClientProfile user={{ id: user.id, name: user.name }} />112```113114```tsx115// Bad: Map/Set116<ClientComponent items={new Map([['a', 1]])} />117118// Good: Convert to array/object119<ClientComponent items={Object.fromEntries(map)} />120<ClientComponent items={Array.from(set)} />121```122123### 3. Server Actions Are the Exception124125Functions marked with `'use server'` CAN be passed to client components.126127```tsx128// Valid: Server Action can be passed129// actions.ts130'use server'131export async function submitForm(formData: FormData) {132// server-side logic133}134135// page.tsx (server)136import { submitForm } from './actions'137export default function Page() {138return <ClientForm onSubmit={submitForm} /> // OK!139}140141// ClientForm.tsx (client)142'use client'143export function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) {144return <form action={onSubmit}>...</form>145}146```147148## Quick Reference149150| Pattern | Valid? | Fix |151|---------|--------|-----|152| `'use client'` + `async function` | No | Fetch in server parent, pass data |153| Pass `() => {}` to client | No | Define in client or use server action |154| Pass `new Date()` to client | No | Use `.toISOString()` |155| Pass `new Map()` to client | No | Convert to object/array |156| Pass class instance to client | No | Pass plain object |157| Pass server action to client | Yes | - |158| Pass `string/number/boolean` | Yes | - |159| Pass plain object/array | Yes | - |160