Skip to content
1 change: 1 addition & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MIT License - Copyright (c) 2026 Ian Alloway
8 changes: 4 additions & 4 deletions src/content/learn/extracting-state-logic-into-a-reducer.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,11 +183,11 @@ Each of its event handlers calls `setTasks` in order to update the state. As thi

Reducers are a different way to handle state. You can migrate from `useState` to `useReducer` in three steps:

1. **Move** from setting state to dispatching actions.
1. **Move** from setting state to dispatching Actions.
2. **Write** a reducer function.
3. **Use** the reducer from your component.

### Step 1: Move from setting state to dispatching actions {/*step-1-move-from-setting-state-to-dispatching-actions*/}
### Step 1: Move from setting state to dispatching Actions {/*step-1-move-from-setting-state-to-dispatching-actions*/}

Your event handlers currently specify _what to do_ by setting state:

Expand Down Expand Up @@ -226,7 +226,7 @@ Remove all the state setting logic. What you are left with are three event handl
- `handleChangeTask(task)` is called when the user toggles a task or presses "Save".
- `handleDeleteTask(taskId)` is called when the user presses "Delete".

Managing state with reducers is slightly different from directly setting state. Instead of telling React "what to do" by setting state, you specify "what the user just did" by dispatching "actions" from your event handlers. (The state update logic will live elsewhere!) So instead of "setting `tasks`" via an event handler, you're dispatching an "added/changed/deleted a task" action. This is more descriptive of the user's intent.
Managing state with reducers is slightly different from directly setting state. Instead of telling React "what to do" by setting state, you specify "what the user just did" by dispatching "Actions" from your event handlers. (The state update logic will live elsewhere!) So instead of "setting `tasks`" via an event handler, you're dispatching an "added/changed/deleted a task" action. This is more descriptive of the user's intent.

```js
function handleAddTask(text) {
Expand Down Expand Up @@ -862,7 +862,7 @@ li {

</Sandpack>

Component logic can be easier to read when you separate concerns like this. Now the event handlers only specify _what happened_ by dispatching actions, and the reducer function determines _how the state updates_ in response to them.
Component logic can be easier to read when you separate concerns like this. Now the event handlers only specify _what happened_ by dispatching Actions, and the reducer function determines _how the state updates_ in response to them.

## Comparing `useState` and `useReducer` {/*comparing-usestate-and-usereducer*/}

Expand Down
2 changes: 1 addition & 1 deletion src/content/learn/managing-state.md
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ Read **[Preserving and Resetting State](/learn/preserving-and-resetting-state)**

## Extracting state logic into a reducer {/*extracting-state-logic-into-a-reducer*/}

Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called "reducer". Your event handlers become concise because they only specify the user "actions". At the bottom of the file, the reducer function specifies how the state should update in response to each action!
Components with many state updates spread across many event handlers can get overwhelming. For these cases, you can consolidate all the state update logic outside your component in a single function, called "reducer". Your event handlers become concise because they only specify the user "Actions". At the bottom of the file, the reducer function specifies how the state should update in response to each action!

<Sandpack>

Expand Down
2 changes: 1 addition & 1 deletion src/content/learn/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ export default App = AppTSX;

This technique works when you have a default value which makes sense - but there are occasionally cases when you do not, and in those cases `null` can feel reasonable as a default value. However, to allow the type-system to understand your code, you need to explicitly set `ContextShape | null` on the `createContext`.

This causes the issue that you need to eliminate the `| null` in the type for context consumers. Our recommendation is to have the Hook do a runtime check for it's existence and throw an error when not present:
This causes the issue that you need to eliminate the `| null` in the type for context consumers. Our recommendation is to have the Hook do a runtime check for its existence and throw an error when not present:

```js {5, 16-20}
import { createContext, useContext, useState, useMemo } from 'react';
Expand Down
94 changes: 93 additions & 1 deletion src/content/reference/rsc/use-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,4 +376,96 @@ These libraries may rely on component Hooks or client APIs. Third-party componen

If these libraries have been updated to be compatible with React Server Components, then they will already include `'use client'` markers of their own, allowing you to use them directly from your Server Components. If a library hasn't been updated, or if a component needs props like event handlers that can only be specified on the client, you may need to add your own Client Component file in between the third-party Client Component and your Server Component where you'd like to use it.

[TODO]: <> (Troubleshooting - need use-cases)
## Troubleshooting {/*troubleshooting*/}

### "Functions cannot be passed directly to Client Components" error {/*functions-cannot-be-passed-directly*/}

This error occurs when you try to pass a function from a Server Component to a Client Component.

```js
// Server Component
import ClientButton from './ClientButton';

export default function Page() {
function handleClick() {
console.log('clicked');
}
// This will error!
return <ClientButton onClick={handleClick} />;
}
```

Functions are not serializable, so they cannot be passed from server to client. To fix this, either move the function to the Client Component, or use a [Server Function](/reference/rsc/server-functions) with `'use server'`.

```js
// Solution 1: Move the handler to the Client Component
'use client';

export default function ClientButton() {
function handleClick() {
console.log('clicked');
}
return <button onClick={handleClick}>Click me</button>;
}
```

```js
// Solution 2: Use a Server Function
'use server';

export async function handleClick() {
console.log('clicked on server');
}
```

### Component uses hooks but doesn't have `'use client'` {/*component-uses-hooks-without-use-client*/}

If you're using hooks like `useState`, `useEffect`, or `useContext` in a component, you need to mark it as a Client Component.

```js
// This will error - hooks require 'use client'
import { useState } from 'react';

export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
```

Add `'use client'` at the top of the file:

```js
'use client';

import { useState } from 'react';

export default function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
```

### Third-party library doesn't work in Server Components {/*third-party-library-server-component*/}

Many third-party libraries were written before Server Components existed and may use client-only features. If you get an error when importing a library in a Server Component, create a wrapper Client Component:

```js
// components/ChartWrapper.js
'use client';

import { Chart } from 'third-party-chart-library';

export default function ChartWrapper(props) {
return <Chart {...props} />;
}
```

Then import the wrapper in your Server Component:

```js
// page.js (Server Component)
import ChartWrapper from './components/ChartWrapper';

export default function Page() {
return <ChartWrapper data={data} />;
}
94 changes: 94 additions & 0 deletions src/content/reference/rsc/use-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,97 @@ export default async function incrementLike() {
```

To read a Server Function return value, you'll need to `await` the promise returned.

## Troubleshooting {/*troubleshooting*/}

### "Server Functions cannot be called during render" error {/*server-functions-cannot-be-called-during-render*/}

Server Functions should only be called in response to user actions (like form submissions or button clicks), not during component rendering.

```js
// This will error - calling during render
function BadComponent() {
const data = await serverFunction(); // Don't do this!
return <div>{data}</div>;
}

// Correct - call in response to user action
function GoodComponent() {
const [data, setData] = useState(null);

const handleClick = async () => {
const result = await serverFunction();
setData(result);
};

return <button onClick={handleClick}>Load Data</button>;
}
```

For data fetching during render, use a Server Component instead of a Server Function.

### "Arguments must be serializable" error {/*arguments-must-be-serializable*/}

Server Functions can only receive serializable arguments. You cannot pass functions, class instances, or React elements.

```js
// This will error - functions aren't serializable
async function saveData(callback) {
'use server';
// ...
}

// Correct - pass serializable data
async function saveData(userId, formData) {
'use server';
// ...
}
```

### Server Function not updating the UI {/*server-function-not-updating-ui*/}

If your Server Function runs but the UI doesn't update, make sure you're calling it inside a Transition when outside a form:

```js
import { useTransition } from 'react';

function MyComponent() {
const [isPending, startTransition] = useTransition();

const handleClick = () => {
// Wrap in startTransition for proper UI updates
startTransition(async () => {
await myServerFunction();
});
};

return <button onClick={handleClick} disabled={isPending}>Submit</button>;
}
```

### "use server" directive not working {/*use-server-directive-not-working*/}

The `'use server'` directive must be:
1. At the very beginning of the function body (not before the function)
2. Written with single or double quotes (not backticks)
3. Used only in async functions

```js
// Wrong - directive before function
'use server';
async function myFunction() {
// ...
}

// Correct - directive inside function body
async function myFunction() {
'use server';
// ...
}

// Also correct - at top of file to mark all exports
'use server';

export async function functionA() { /* ... */ }
export async function functionB() { /* ... */ }
```
Loading