Next.js Server Actions are async functions that run on the server and handle mutations. You attach one to a form or an event handler, and Next.js calls it over a POST request. The function updates data on the server, then the UI refreshes in a single roundtrip.
A Server Action is a Server Function used for mutations. The broader Server Function term covers any async function the server exposes, while Server Actions are the form and mutation side you reach for most often.
// app/actions.ts
'use server'
export async function createPost(formData: FormData) {
const title = formData.get('title')?.toString()
return { title }
}The use server directive at the top of this file marks every exported function as server-only code. The browser can call these functions, but their code never ships in the client bundle.
Now call it from a form. This page is a Server Component, and the form passes the function straight to the action attribute.
// app/page.tsx
import { createPost } from './actions'
export default function Page() {
return (
<form action={createPost}>
<label>Title <input name="title" required /></label>
<button type="submit">Create post</button>
</form>
)
}When someone submits the form, the browser sends the fields as FormData and calls createPost on the server. The form also works before JavaScript loads, because React submits it with progressive enhancement.
Two ways to mark server code
The use server directive has two placements. At the top of a file it marks every exported function. Inside a function body it marks only that one function.
| Placement | Effect |
|---|---|
| Top of a file | Every export in the file runs on the server |
| Inside a function body | Only that function runs on the server |
The file form is the common one, because it keeps related actions together in one module. The inline form is useful when one page needs an action that closes over its props. See the use server directive for the full placement rules.
Ways to invoke a Server Action
The form action above is the primary path, but two more exist. A button can use formAction, and a Client Component can call the function from an event handler.
// app/like-button.tsx
'use client'
import { incrementLike } from './actions'
export function LikeButton({ likes }: { likes: number }) {
return (
<button onClick={() => incrementLike()}>Like ({likes})</button>
)
}This component needs use client because event handlers run in the browser. incrementLike is another export from app/actions.ts, so it stays a server function, and the click sends it over a POST just like the form does.
The count on screen does not change on its own. The click reaches the server, but the button keeps rendering the likes prop it was given until the page revalidates or you hold the value in client state.
What happens in a roundtrip
When a Server Action runs, Next.js sends a POST to the server with the serialized arguments. The server runs the function and can respond with updated UI and new data together, so a mutation and its result arrive in one response. Actions use the POST method, and it is the only method that can invoke one.
The returned value is serialized and sent back, which is why createPost can return the title object. On the server, the function can reach databases, cookies, and other server-only APIs that never touch the browser.
Two limits are worth knowing early. The client dispatches actions one at a time, so triggering three in a row queues them rather than running them in parallel. Action request bodies are also capped at 1MB by default, which you raise through the serverActions option in the Next.js config when a form carries larger uploads.
Server Actions run on the server, not the client
The function body always executes on the server, even when a Client Component calls it. That separation is what lets an action read secrets or write to a database without exposing those details to visitors.
The trade-off is that every action is reachable over HTTP, not just through your UI. Anyone who can construct a POST can attempt to call it, so an action must verify the caller itself rather than trusting the button that triggered it.
Next.js does add framework-level protection. It compares the request Origin against the Host and rejects mismatches, and it encrypts action references at build time so unused actions leave no public endpoint behind.
Those checks stop a cross-site request. They say nothing about whether this particular user may change this particular record, which is the part your code has to answer.
When to use a Server Action
Use a Server Action for mutations: create, update, delete, or any state change.
Do not use one to fetch read-only data. A Server Component already fetches directly on the server without a roundtrip, and because actions dispatch one at a time, a list of reads runs in sequence instead of in parallel. Fetch in the component, mutate in the action.
If an outside system needs to call your code, an action is also the wrong shape, since it has no stable URL to hand out. That job belongs to a Route Handler.
See how to mutate data with a Server Action for the working pattern, and securing Server Actions for the auth checks to add before shipping.
Rune AI
Key Insights
- A Server Action is an async function that runs on the server and handles mutations.
- Mark server code with the use server directive at the top of a file or inside a function.
- Invoke an action through a form action, a button formAction, or a client event handler.
- Actions use POST only, and the server can return updated UI and data in one roundtrip.
- Verify authentication inside every action before mutating data.
Frequently Asked Questions
Are Server Actions and Server Functions the same thing?
Do Server Actions work without JavaScript?
Can a Server Action run on the client?
Conclusion
Server Actions give you a server-only function you can call from a form or event handler over a POST request. Mark code with use server, attach it to a form action, and the mutation runs on the server in a single roundtrip.
More in this topic
`generateMetadata` Explained with Real Examples
What generateMetadata does, when it runs, and how to use it for real routes: awaited params, deduplicated data fetching, extending parent metadata, and returning a 404 from metadata.
Canonical URLs in Next.js: `metadataBase`, `alternates.canonical`, and Dynamic Pages
How canonical URLs work in the Next.js App Router: setting metadataBase once, writing alternates.canonical per route, handling dynamic segments, and what happens when the base URL is missing.
Open Graph and Twitter Card Metadata in Next.js
How to write Open Graph and Twitter card metadata in the Next.js App Router: the openGraph and twitter fields, automatic card defaults, article tags, and image merge rules.