Next.js 36: Calling Actions Outside Forms
easy⏱ 5 mincoursenextjs
Actions are just functions
<form action={fn}> is one convenient way to call a Server Action, but far from the only one. Since the action is an async function, you can call it anywhere you'd call any other async function: onClick={() => deleteComment(id)}, inside useEffect for a periodic sync, or chained after another async call. Next.js handles the network call the same way regardless of where you invoke it from.
'use server';
export async function deleteComment(id: string) {
await db.comment.delete({ where: { id } });
}
// In a Client Component:
function DeleteButton({ id }: { id: string }) {
return (
<button onClick={() => deleteComment(id)}>
Delete
</button>
);
}
Passing extra arguments
A Server Action called from a form only ever receives (prevState, formData) — but called directly, you can pass whatever arguments you want, just like any function: deleteComment(comment.id). Prefer an inline arrow (() => deleteComment(id)) over .bind(null, id) when reading from a Client Component — both work, but the arrow is easier to follow.
Track pending per item, not globally
When several rows can each trigger the same action independently (delete this comment, like that one), a single isPending boolean isn't enough — clicking delete on comment A shouldn't disable comment B's button too. Track which id is in flight (deletingId) so only the relevant row shows a pending state.
Delete on click
Add a deleteCommentAction(id) simulated action (fake delay, then 'removes' the comment). Render a Delete button per comment that calls it directly in onClick — no form. Track a deletingId state so only the comment being deleted shows 'Deleting…' and a disabled button. Keep export default App.