Skip to main content
Web & Performance

Server Actions in production: what we learned

After a year of shipping Next.js Server Actions in client work, here is where they earn their keep and where a real API endpoint is still the right answer.

2 min readBy Tom Whitfield
React logo beside component code in a dark editor window
Cover image for Server Actions in production: what we learned

Key takeaways

  • Server Actions replace form-handling glue, not the API layer.
  • Anything a third party calls, or that needs a versioned contract, still wants an endpoint.
  • Progressive enhancement and pending states coming free is the real productivity win.

When should you use a Server Action instead of an API route?

Use a Server Action when a mutation belongs to one piece of your own UI and nothing outside the application needs to call it. Use an API route when the operation needs a stable versioned contract, is called by a third party, or is invoked from several unrelated places.

What they removed from our codebases

Server Actions removed an entire category of code from our applications: the API route that exists only to be called by one form, along with its request schema, its fetch wrapper and its two error paths.

Where they shine is mutations tied to a specific piece of UI. Progressive enhancement comes free, the pending state is built in, and revalidation is one call rather than a cache-invalidation dance.

Where they do not fit

Anything a third party needs to call. Anything that needs a stable versioned contract. Anything where the same operation is invoked from several unrelated places. Those still want a real API endpoint, and pretending otherwise produces a duplicated action per caller.

The mistake we made early was treating them as a replacement for the API layer rather than a replacement for form-handling glue. They are the latter.

Authorise inside the action

An action is reachable from the browser like any other endpoint. The component that renders the form is not a security boundary.

Our house rule is that every action begins with the same three lines: resolve the session, assert the permission, parse the input with a schema. If those are not the first statements in the function, the review does not pass.

Server Actions are a better form handler, not a smaller API. Teams that adopt them as the latter end up rebuilding the API by accident.

Keep actions thin and testable

The pattern that has held up best for us is an action that does four things and nothing else: authorise, validate, call a domain function, revalidate. The business logic lives in the domain function, which is a plain module with no framework dependency.

That split matters for testing. A domain function can be unit tested without a request, a session or a rendering context, which is where most of the value in testing this layer sits. Actions themselves get a small number of integration tests covering authorisation and validation failures.

It also makes the eventual API endpoint cheap. When an operation does turn out to need a public contract — and some always do — the endpoint wraps the same domain function, and the action stays as the UI path. Nothing is rewritten and there is only one implementation of the rule.

FAQFAQ

Frequently asked questions

About the author

TW

Staff Frontend Engineer

Core Web Vitals and accessibility specialist

Tom builds the front end: rendering strategy, performance budgets and accessible interfaces that hold up under real network conditions. He writes about treating Core Web Vitals as a revenue metric rather than a lint rule.

  • Core Web Vitals
  • Next.js and React
  • Web accessibility
  • Frontend architecture
All articles by Tom

Read next

More on the same problem, from the same team.

Want this built, not just read about?

Tell us the outcome you need. We reply within one business day with a plan, a timeline and a price.

ExploreKeep exploring

Related pages

Guides

Subscribe Newsletter

Practical playbooks on AI, product engineering, growth marketing and creator campaigns. One email a month, no filler.