|
| 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