Skip to content
rshono
Documentation menu

Core concepts

Server actions

'use server' functions, callable from the client, with progressive enhancement.

'use server' modules export functions callable from client components, with typed arguments and result:

'use server';

export async function createUser(data: { name: string; email: string }) {
  // runs on the server, always
}

Call them directly, or wire them to <form action> / useActionState. There is no route handler in between.

Progressive enhancement

A <form action={createUser}> posts before hydration and with JavaScript disabled. The client runtime upgrades it to a fetch once loaded; until then the browser's own form post does the job.

Every action response carries a fresh page payload, so server-rendered UI updates after a mutation with no refetch and no cache invalidation call.

Every action is a public endpoint

That is the RSC model, not an rshono choice: the client is handed an id for each action and can call it with whatever arguments it likes. The CSRF check proves a request came from your own site; it says nothing about who sent it. Authenticate, authorize and validate arguments inside the action, exactly as in a route handler.

Errors

Thrown action errors are logged server-side and redacted in the production payload — React sends no message and no digest. Return values, not throws, for anything the user should see:

'use server';

export async function createUser(data: FormData) {
  const email = String(data.get('email') ?? '');
  if (!email.includes('@')) return { ok: false, error: 'That email looks wrong.' };
  return { ok: true };
}

Errors that do escape reach onServerError like everything else the framework catches.

Secrets

Actions compile to server references and run only on the server, so they read the real process.env. See Environment & secrets.