next-safe-action
Guides

Executing Actions

There are three ways to execute a safe action from a Client Component. Each approach serves different needs:

Three execution methods: Direct Call, useAction, Form Action

Comparison

Given the same action:

src/app/actions.ts
"use server";

import { z } from "zod";
import { actionClient } from "@/lib/safe-action";

export const greetUser = actionClient
	.inputSchema(z.object({ name: z.string() }))
	.action(async ({ parsedInput }) => {
		return { greeting: `Hello, ${parsedInput.name}!` };
	});

Here's how each method calls it:

src/app/page.tsx
"use client";

import { greetUser } from "./actions";

export default function Page() {
	const handleClick = async () => {
		const result = await greetUser({ name: "Alice" });

		if (result?.data) {
			alert(result.data.greeting);
		}
	};

	return <button onClick={handleClick}>Greet</button>;
}

Use when: You need a simple one-off call, or you're calling from a Server Component or event handler where you don't need reactive UI updates.

src/app/page.tsx
"use client";

import { useAction } from "next-safe-action/hooks";
import { greetUser } from "./actions";

export default function Page() {
	const { execute, result, isExecuting } = useAction(greetUser, {
		onSuccess: ({ data }) => alert(data.greeting),
	});

	return (
		<button onClick={() => execute({ name: "Alice" })} disabled={isExecuting}>
			{isExecuting ? "Loading..." : "Greet"}
		</button>
	);
}

Use when: You need loading states, status tracking, lifecycle callbacks, or any reactive UI behavior. This is the most common approach for interactive Client Components.

src/app/page.tsx
"use client";

import { useActionState } from "react";
import { greetUser } from "./actions";

export default function Page() {
	const [result, dispatch] = useActionState(greetUser, {});

	return (
		<form action={dispatch}>
			<input name="name" defaultValue="Alice" />
			<button type="submit">Greet</button>
			{result?.data && <p>{result.data.greeting}</p>}
		</form>
	);
}

Use when: You need progressive enhancement (form works without JavaScript), or you want to use HTML form patterns with FormData input. See the Form Actions guide for details.

Which method should I use?

NeedMethod
Simple call, no loading UIDirect call
Loading states, callbacks, reactive resultuseAction hook
Optimistic UI updatesuseOptimisticAction hook
Progressive enhancement / no-JS formsForm action
Server Component calling an actionDirect call

What's next?

On this page