# RSC Boundaries

Detect and prevent invalid patterns when crossing Server/Client component boundaries.

## Detection Rules

### 1. Async Client Components Are Invalid

Client components **cannot** be async functions. Only Server Components can be async.

**Detect:** File has `'use client'` AND component is `async function` or returns `Promise`

```tsx
// Bad: async client component
'use client';
export default async function UserProfile() {
  const user = await getUser(); // Cannot await in client component
  return <div>{user.name}</div>;
}

// Good: Remove async, fetch data in parent server component
// page.tsx (server component - no 'use client')
export default async function Page() {
  const user = await getUser();
  return <UserProfile user={user} />;
}

// UserProfile.tsx (client component)
('use client');
export function UserProfile({ user }: { user: User }) {
  return <div>{user.name}</div>;
}
```

```tsx
// Bad: async arrow function client component
'use client';
const Dashboard = async () => {
  const data = await fetchDashboard();
  return <div>{data}</div>;
};

// Good: Fetch in server component, pass data down
```

### 2. Non-Serializable Props to Client Components

Props passed from Server → Client must be JSON-serializable.

**Detect:** Server component passes these to a client component:

- Functions (except Server Actions with `'use server'`)
- `Date` objects
- `Map`, `Set`, `WeakMap`, `WeakSet`
- Class instances
- `Symbol` (unless globally registered)
- Circular references

```tsx
// Bad: Function prop
// page.tsx (server)
export default function Page() {
  const handleClick = () => console.log('clicked');
  return <ClientButton onClick={handleClick} />;
}

// Good: Define function inside client component
// ClientButton.tsx
('use client');
export function ClientButton() {
  const handleClick = () => console.log('clicked');
  return <button onClick={handleClick}>Click</button>;
}
```

```tsx
// Bad: Date object (silently becomes string, then crashes)
// page.tsx (server)
export default async function Page() {
  const post = await getPost();
  return <PostCard createdAt={post.createdAt} />; // Date object
}

// PostCard.tsx (client) - will crash on .getFullYear()
('use client');
export function PostCard({ createdAt }: { createdAt: Date }) {
  return <span>{createdAt.getFullYear()}</span>; // Runtime error!
}

// Good: Serialize to string on server
// page.tsx (server)
export default async function Page() {
  const post = await getPost();
  return <PostCard createdAt={post.createdAt.toISOString()} />;
}

// PostCard.tsx (client)
('use client');
export function PostCard({ createdAt }: { createdAt: string }) {
  const date = new Date(createdAt);
  return <span>{date.getFullYear()}</span>;
}
```

```tsx
// Bad: Class instance
const user = new UserModel(data)
<ClientProfile user={user} /> // Methods will be stripped

// Good: Pass plain object
const user = await getUser()
<ClientProfile user={{ id: user.id, name: user.name }} />
```

```tsx
// Bad: Map/Set
<ClientComponent items={new Map([['a', 1]])} />

// Good: Convert to array/object
<ClientComponent items={Object.fromEntries(map)} />
<ClientComponent items={Array.from(set)} />
```

### 3. Server Actions Are the Exception

Functions marked with `'use server'` CAN be passed to client components.

```tsx
// Valid: Server Action can be passed
// actions.ts
'use server';
export async function submitForm(formData: FormData) {
  // server-side logic
}

// page.tsx (server)
import { submitForm } from './actions';
export default function Page() {
  return <ClientForm onSubmit={submitForm} />; // OK!
}

// ClientForm.tsx (client)
('use client');
export function ClientForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) {
  return <form action={onSubmit}>...</form>;
}
```

## Quick Reference

| Pattern                           | Valid? | Fix                                   |
| --------------------------------- | ------ | ------------------------------------- |
| `'use client'` + `async function` | No     | Fetch in server parent, pass data     |
| Pass `() => {}` to client         | No     | Define in client or use server action |
| Pass `new Date()` to client       | No     | Use `.toISOString()`                  |
| Pass `new Map()` to client        | No     | Convert to object/array               |
| Pass class instance to client     | No     | Pass plain object                     |
| Pass server action to client      | Yes    | -                                     |
| Pass `string/number/boolean`      | Yes    | -                                     |
| Pass plain object/array           | Yes    | -                                     |
