| 1 | # RSC Boundaries |
| 2 | |
| 3 | Detect and prevent invalid patterns when crossing Server/Client component boundaries. |
| 4 | |
| 5 | ## Detection Rules |
| 6 | |
| 7 | ### 1. Async Client Components Are Invalid |
| 8 | |
| 9 | Client components **cannot** be async functions. Only Server Components can be async. |
| 10 | |
| 11 | **Detect:** File has `'use client' |
| 12 | |
| 13 | ```tsx |
| 14 | // Bad: async client component |
| 15 | "use client"; |
| 16 | export default async function |
| 17 | const user = |
| 18 | return <div>{ |
| 19 | } |
| 20 | |
| 21 | // Good: Remove async, fetch data in parent server component |
| 22 | // page.tsx (server component - no 'use client') |
| 23 | export default async function |
| 24 | const user = |
| 25 | return <UserProfile |
| 26 | } |
| 27 | |
| 28 | // UserProfile.tsx (client component) |
| 29 | ("use client"); |
| 30 | export function UserProfile({ |
| 31 | return <div>{ |
| 32 | } |
| 33 | ``` |
| 34 | |
| 35 | ```tsx |
| 36 | // Bad: async arrow function client component |
| 37 | "use client"; |
| 38 | const Dashboard = async |
| 39 | const data = |
| 40 | return <div>{ |
| 41 | }; |
| 42 | |
| 43 | // Good: Fetch in server component, pass data down |
| 44 | ``` |
| 45 | |
| 46 | ### 2. Non-Serializable Props to Client Components |
| 47 | |
| 48 | Props passed from Server → Client must be JSON-serializable. |
| 49 | |
| 50 | **Detect:** Server component passes these to a client component: |
| 51 | |
| 52 | - Functions (except Server Actions with `'use server'`) |
| 53 | - `Date` objects |
| 54 | - `Map`, `Set`, |
| 55 | - Class instances |
| 56 | - `Symbol` (unless globally registered) |
| 57 | - Circular references |
| 58 | |
| 59 | ```tsx |
| 60 | // Bad: Function prop |
| 61 | // page.tsx (server) |
| 62 | export default function Page |
| 63 | const handleClick = |
| 64 | return <ClientButton |
| 65 | } |
| 66 | |
| 67 | // Good: Define function inside client component |
| 68 | // ClientButton.tsx |
| 69 | ("use client"); |
| 70 | export function ClientButton() |
| 71 | const handleClick = |
| 72 | return <button onClick |
| 73 | } |
| 74 | ``` |
| 75 | |
| 76 | ```tsx |
| 77 | // Bad: Date object (silently becomes string, then crashes) |
| 78 | // page.tsx (server) |
| 79 | export default async function |
| 80 | const post = |
| 81 | return <PostCard |
| 82 | } |
| 83 | |
| 84 | // PostCard.tsx (client) - will crash on .getFullYear() |
| 85 | ("use client"); |
| 86 | export function PostCard({ |
| 87 | return <span>{ |
| 88 | } |
| 89 | |
| 90 | // Good: Serialize to string on server |
| 91 | // page.tsx (server) |
| 92 | export default async function |
| 93 | const post = |
| 94 | return <PostCard |
| 95 | } |
| 96 | |
| 97 | // PostCard.tsx (client) |
| 98 | ("use client"); |
| 99 | export function PostCard({ |
| 100 | const date = |
| 101 | return <span>{ |
| 102 | } |
| 103 | ``` |
| 104 | |
| 105 | ```tsx |
| 106 | // Bad: Class instance |
| 107 | const user = new |
| 108 | <ClientProfile user={ |
| 109 | |
| 110 | // Good: Pass plain object |
| 111 | const user = await |
| 112 | <ClientProfile user={ |
| 113 | ``` |
| 114 | |
| 115 | ```tsx |
| 116 | // Bad: Map/Set |
| 117 | <ClientComponent items={ |
| 118 | |
| 119 | // Good: Convert to array/object |
| 120 | <ClientComponent items={ |
| 121 | <ClientComponent items={ |
| 122 | ``` |
| 123 | |
| 124 | ### 3. Server Actions Are the Exception |
| 125 | |
| 126 | Functions marked with `'use server'` CAN be passed to client components. |
| 127 | |
| 128 | ```tsx |
| 129 | // Valid: Server Action can be passed |
| 130 | // actions.ts |
| 131 | "use server"; |
| 132 | export async function submitForm |
| 133 | // server-side logic |
| 134 | } |
| 135 | |
| 136 | // page.tsx (server) |
| 137 | import { submitForm } |
| 138 | export default function Page |
| 139 | return <ClientForm |
| 140 | } |
| 141 | |
| 142 | // ClientForm.tsx (client) |
| 143 | ("use client"); |
| 144 | export function ClientForm({ |
| 145 | return <form action |
| 146 | } |
| 147 | ``` |
| 148 | |
| 149 | ## Quick Reference |
| 150 | |
| 151 | | Pattern | Valid? | Fix | |
| 152 | | --------------------------------- | ------ | |
| 153 | | `'use client'` + `async function` |
| 154 | | Pass `() => {}` to client | No | Define in client or use server action |
| 155 | | Pass `new Date()` to client | No | Use |
| 156 | | Pass `new Map()` to client | No | Convert to object/array |
| 157 | | Pass class instance to client | No | Pass plain object | |
| 158 | | Pass server action to client | Yes | - | |
| 159 | | Pass `string/number/boolean` | Yes | |
| 160 | | Pass plain object/array | Yes | - | |