Skip to content

Commit 67ac8bf

Browse files
Getting Started Docs: Add Mutating Data page (vercel#74018)
Closes: https://linear.app/vercel/issue/DOC-3906/page-mutating-data --------- Co-authored-by: Lee Robinson <me@leerob.io>
1 parent 827fb90 commit 67ac8bf

File tree

1 file changed

+316
-0
lines changed

1 file changed

+316
-0
lines changed
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
---
2+
title: How to mutate data
3+
nav_title: Mutating Data
4+
description: Learn how to mutate data in your Next.js application.
5+
related:
6+
title: API Reference
7+
description: Learn more about the features mentioned in this page by reading the API Reference.
8+
links:
9+
- app/api-reference/functions/revalidatePath
10+
- app/api-reference/functions/revalidateTag
11+
- app/api-reference/functions/redirect
12+
---
13+
14+
You can mutate data in Next.js using React's [Server Functions](https://react.dev/reference/rsc/server-functions). This page will go through how you can [create](#creating-server-functions) and [invoke](#invoking-server-functions) Server Functions.
15+
16+
## Creating Server Functions
17+
18+
A Server Function can be defined by using the [`use server`](https://react.dev/reference/rsc/use-server) directive. You can place the directive at the top of an **asynchronous** function to mark the function as a Server Function, or at the top of a separate file to mark all exports of that file. We recommend using a separate file in most instances.
19+
20+
```ts filename="app/lib/actions.ts" switcher
21+
'use server'
22+
23+
export async function createPost(formData: FormData) {}
24+
25+
export async function deletePost(formData: FormData) {}
26+
```
27+
28+
```js filename="app/lib/actions.js" switcher
29+
'use server'
30+
31+
export async function createPost(formData) {}
32+
33+
export async function deletePost(formData) {}
34+
```
35+
36+
### Server Components
37+
38+
Server Functions can be inlined in Server Components by adding the `"use server"` directive to the top of the function body:
39+
40+
```tsx filename="app/page.tsx" switcher
41+
export async default function Page() {
42+
// Server Action
43+
async function createPost() {
44+
'use server'
45+
// Mutate data
46+
// ...
47+
48+
return <></>
49+
}
50+
```
51+
52+
```jsx filename="app/page.js" switcher
53+
export default function Page() {
54+
// Server Action
55+
async function createPost() {
56+
'use server'
57+
// Mutate data
58+
// ...
59+
}
60+
61+
return <></>
62+
}
63+
```
64+
65+
### Client Components
66+
67+
It's not possible to define Server Functions in Client Components. However, you can invoke them in Client Components by importing them from a file that has the `"use server"` directive at the top of it:
68+
69+
```tsx filename="app/actions.ts" switcher
70+
'use server'
71+
72+
export async function createPost() {}
73+
```
74+
75+
```js filename="app/actions.js" switcher
76+
'use server'
77+
78+
export async function createPost() {}
79+
```
80+
81+
```tsx filename="app/ui/button.tsx" switcher
82+
'use client'
83+
84+
import { createPost } from '@/app/actions'
85+
86+
export function Button() {
87+
return <button formAction={createPost}>Create</button>
88+
}
89+
```
90+
91+
```jsx filename="app/ui/button.js" switcher
92+
'use client'
93+
94+
import { createPost } from '@/app/actions'
95+
96+
export function Button() {
97+
return <button formAction={createPost}>Create</button>
98+
}
99+
```
100+
101+
## Invoking Server Functions
102+
103+
There are two mains ways you can invoke a Server Function:
104+
105+
1. [Forms](#forms) in Server and Client Components
106+
2. [Event Handlers](#event-handlers) in Client Components
107+
108+
### Forms
109+
110+
React extends the HTML [`<form>`](https://react.dev/reference/react-dom/components/form) element to allow Server Function to be invoked with the HTML `action` prop.
111+
112+
When invoked in a form, the function automatically receives the [`FormData`](https://developer.mozilla.org/docs/Web/API/FormData/FormData) object. You can extract the data using the native [`FormData` methods](https://developer.mozilla.org/en-US/docs/Web/API/FormData#instance_methods):
113+
114+
```tsx filename="app/ui/form.tsx" switcher
115+
import { createPost } from '@/app/actions'
116+
117+
export function Form() {
118+
return (
119+
<form action={createPost}>
120+
<input type="text" name="title" />
121+
<input type="text" name="content" />
122+
<button type="submit">Create</button>
123+
</form>
124+
)
125+
}
126+
```
127+
128+
```jsx filename="app/ui/form.js" switcher
129+
import { createPost } from '@/app/actions'
130+
131+
export function Form() {
132+
return (
133+
<form action={createPost}>
134+
<input type="text" name="title" />
135+
<input type="text" name="content" />
136+
<button type="submit">Create</button>
137+
</form>
138+
)
139+
}
140+
```
141+
142+
```tsx filename="app/actions.ts" switcher
143+
'use server'
144+
145+
export async function createPost(formData: FormData) {
146+
const title = formData.get('title')
147+
const content = formData.get('content')
148+
149+
// Mutate data
150+
// Revalidate cache
151+
}
152+
```
153+
154+
```jsx filename="app/actions.js" switcher
155+
'use server'
156+
157+
export async function createPost(formData) {
158+
const title = formData.get('title')
159+
const content = formData.get('content')
160+
161+
// Mutate data
162+
// Revalidate cache
163+
}
164+
```
165+
166+
> **Good to know:** When passed to the `action` prop, Server Functions are also known as _Server Actions_.
167+
168+
### Event Handlers
169+
170+
You can invoke a Server Function in a Client Component by using event handlers such as `onClick`.
171+
172+
```tsx filename="app/like-button.tsx" switcher
173+
'use client'
174+
175+
import { incrementLike } from './actions'
176+
import { useState } from 'react'
177+
178+
export default function LikeButton({ initialLikes }: { initialLikes: number }) {
179+
const [likes, setLikes] = useState(initialLikes)
180+
181+
return (
182+
<>
183+
<p>Total Likes: {likes}</p>
184+
<button
185+
onClick={async () => {
186+
const updatedLikes = await incrementLike()
187+
setLikes(updatedLikes)
188+
}}
189+
>
190+
Like
191+
</button>
192+
```
193+
194+
```jsx filename="app/like-button.js" switcher
195+
'use client'
196+
197+
import { incrementLike } from './actions'
198+
import { useState } from 'react'
199+
200+
export default function LikeButton({ initialLikes }) {
201+
const [likes, setLikes] = useState(initialLikes)
202+
203+
return (
204+
<>
205+
<p>Total Likes: {likes}</p>
206+
<button
207+
onClick={async () => {
208+
const updatedLikes = await incrementLike()
209+
setLikes(updatedLikes)
210+
}}
211+
>
212+
Like
213+
</button>
214+
</>
215+
)
216+
}
217+
```
218+
219+
### Showing a pending state
220+
221+
While executing a Server Function, you can show a loading indicator with React's [`useActionState`](https://react.dev/reference/react/useActionState) hook. This hook returns a `pending` boolean:
222+
223+
```tsx filename="app/ui/button.tsx" switcher
224+
'use client'
225+
226+
import { useActionState } from 'react'
227+
import { createPost } from '@/app/actions'
228+
import { LoadingSpinner } from '@/app/ui/loading-spinner'
229+
230+
export function Button() {
231+
const [state, action, pending] = useActionState(createPost, false)
232+
233+
return (
234+
<button onClick={async () => action()}>
235+
{pending ? <LoadingSpinner /> : 'Create Post'}
236+
</button>
237+
)
238+
}
239+
```
240+
241+
```jsx filename="app/ui/button.js" switcher
242+
'use client'
243+
244+
import { useActionState } from 'react'
245+
import { createPost } from '@/app/actions'
246+
import { LoadingSpinner } from '@/app/ui/loading-spinner'
247+
248+
export function Button() {
249+
const [state, action, pending] = useActionState(createPost, false)
250+
251+
return (
252+
<button onClick={async () => action()}>
253+
{pending ? <LoadingSpinner /> : 'Create Post'}
254+
</button>
255+
)
256+
}
257+
```
258+
259+
### Revalidating the cache
260+
261+
After performing a mutation, you can revalidate the Next.js cache and show the updated data by calling [`revalidatePath`](/docs/app/api-reference/functions/revalidatePath) or [`revalidateTag`](/docs/app/api-reference/functions/revalidateTag) within the Server Function:
262+
263+
```ts filename="app/lib/actions.ts" switcher
264+
'use server'
265+
266+
import { revalidatePath } from 'next/cache'
267+
268+
export async function createPost(formData: FormData) {
269+
// Mutate data
270+
// ...
271+
272+
revalidatePath('/posts')
273+
}
274+
```
275+
276+
```js filename="app/actions.js" switcher
277+
'use server'
278+
279+
import { revalidatePath } from 'next/cache'
280+
281+
export async function createPost(formData) {
282+
// Mutate data
283+
// ...
284+
revalidatePath('/posts')
285+
}
286+
```
287+
288+
### Redirecting
289+
290+
You may want to redirect the user to a different page after performing a mutation. You can do this by calling [`redirect`](/docs/app/api-reference/functions/redirect) within the Server Function:
291+
292+
```ts filename="app/lib/actions.ts" switcher
293+
'use server'
294+
295+
import { redirect } from 'next/navigation'
296+
297+
export async function createPost(formData: FormData) {
298+
// Mutate data
299+
// ...
300+
301+
redirect('/posts')
302+
}
303+
```
304+
305+
```js filename="app/actions.js" switcher
306+
'use server'
307+
308+
import { redirect } from 'next/navigation'
309+
310+
export async function createPost(formData) {
311+
// Mutate data
312+
// ...
313+
314+
redirect('/posts')
315+
}
316+
```

0 commit comments

Comments
 (0)