Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Getting Started Docs: Add Mutating Data page #74018

Merged
merged 22 commits into from
Jan 6, 2025
Merged
Changes from 16 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
308 changes: 308 additions & 0 deletions docs/01-app/01-getting-started/08-mutating-data.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
---
title: How to mutate data
nav_title: Mutating Data
description: Learn how to mutate data in your Next.js application.
related:
title: API Reference
description: Learn more about the features mentioned in this page by reading the API Reference.
links:
- app/api-reference/functions/revalidatePath
- app/api-reference/functions/revalidateTag
- app/api-reference/functions/redirect
---

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.

> **Good to know:** When used in a mutation context, Server Functions are also known as _Server Actions_.

## Creating Server Functions

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 Functions, or at the top of a separate file to mark all exports of that file.

```ts filename="app/lib/actions.ts" switcher
'use server'

export async function createPost(formData: FormData) {}

export async function deletePost(formData: FormData) {}
```

```js filename="app/lib/actions.js" switcher
'use server'

export async function createPost(formData) {}

export async function deletePost(formData) {}
```

### Server Components

Server Functions can be inlined in Server Components by adding the `"use server"` directive to the top of the function body:

```tsx filename="app/page.tsx" switcher
export async default function Page() {
// Server Action
async function createPost() {
'use server'
// Mutate data
// ...

return <></>
}
```

```jsx filename="app/page.js" switcher
export default function Page() {
// Server Action
async function createPost() {
'use server'
// Mutate data
// ...
}

return <></>
}
```

### Client Components

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:

```tsx filename="app/actions.ts" switcher
'use server'

export async function createPost() {}
```

```js filename="app/actions.js" switcher
'use server'

export async function createPost() {}
```

```tsx filename="app/ui/button.tsx" switcher
'use client'

import { createPost } from '@/app/actions'

export function Button() {
return <button onClick={() => createPost()}>Create</button>
}
```

```jsx filename="app/ui/button.js" switcher
'use client'

import { createPost } from '@/app/actions'

export function Button() {
return <button onClick={() => createPost()}>Create</button>
}
```

## Invoking Server Functions

There are two mains ways you can invoke a Server Function:

1. [Forms](#forms) in Server and Client Components
2. [Event Handlers](#event-handlers) in Client Components

### Forms

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.

When invoked in a form, the action 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):

```tsx filename="app/ui/form.tsx" switcher
import { createPost } from '@/app/actions'

export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<input type="text" name="content" />
<button type="submit">Create</button>
</form>
)
}
```

```jsx filename="app/ui/form.js" switcher
import { createPost } from '@/app/actions'

export function Form() {
return (
<form action={createPost}>
<input type="text" name="title" />
<input type="text" name="content" />
<button type="submit">Create</button>
</form>
)
}
```

```tsx filename="app/actions.ts" switcher
'use server'

export async function createPost(formData: FormData) {
const title = formData.get('title')
const content = formData.get('content')

// mutate data
// revalidate cache
}
```

```jsx filename="app/actions.js" switcher
'use server'

export async function createPost(formData) {
const title = formData.get('title')
const content = formData.get('content')

// mutate data
// revalidate cache
}
```

### Event Handlers

You can invoke a Server Function in a Client Component by using event handlers such as `onClick`.

```tsx filename="app/like-button.tsx" switcher
'use client'

import { incrementLike } from './actions'
import { useState } from 'react'

export default function LikeButton({ initialLikes }: { initialLikes: number }) {
const [likes, setLikes] = useState(initialLikes)

return (
<>
<p>Total Likes: {likes}</p>
<button
onClick={async () => {
const updatedLikes = await incrementLike()
setLikes(updatedLikes)
}}
>
Like
</button>
```

```jsx filename="app/like-button.js" switcher
'use client'

import { incrementLike } from './actions'
import { useState } from 'react'

export default function LikeButton({ initialLikes }) {
const [likes, setLikes] = useState(initialLikes)

return (
<>
<p>Total Likes: {likes}</p>
<button
onClick={async () => {
const updatedLikes = await incrementLike()
setLikes(updatedLikes)
}}
>
Like
</button>
</>
)
}
```

### Showing a pending state

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:

```tsx filename="app/ui/button.tsx" switcher
'use client'

import { useActionState } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'

export function Button() {
const [state, action, pending] = useActionState(createPost, false)

return (
<button onClick={async () => action()}>
{pending ? <LoadingSpinner /> : 'Create Post'}
</button>
)
}
```

```jsx filename="app/ui/button.js" switcher
'use client'

import { useActionState } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'

export function Button() {
const [state, action, pending] = useActionState(createPost, false)

return (
<button onClick={async () => action()}>
{pending ? <LoadingSpinner /> : 'Create Post'}
</button>
)
}
```

### Revalidating the cache

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:

```ts filename="app/lib/actions.ts" switcher
'use server'

export async function createPost(formData: FormData) {
// Mutate data
// ...

revalidatePath('/posts')
}
```

```js filename="app/actions.js" switcher
'use server'

export async function createPost(formData) {
// Mutate data
// ...
revalidatePath('/posts')
}
```

### Redirecting

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:

```ts filename="app/lib/actions.ts" switcher
'use server'

export async function createPost(formData: FormData) {
// Mutate data
// ...

redirect('/posts')
}
```

```js filename="app/actions.js" switcher
'use server'

export async function createPost(formData) {
// Mutate data
// ...

redirect('/posts')
}
```
Loading