I've been experimenting with Next.js Server Actions and noticed something unexpected while analyzing re-renders using React Scan.
When I add an onClick handler to a button that updates a useState value, only the button component re-renders (indicated by a blue outline in React Scan). However, when I call a Server Action within the same onClick handler, it causes a re-render of the entire page—including all components, as seen by blue outlines around both the button and the entire body.
Interestingly, this behavior does not occur when using fetch to call an API route instead of a Server Action.
Is this expected behavior when using Server Actions in client components?
code:
// page.tsx
import SimpleButton from "./simple-button";
export default function Home() {
return (
<div className="flex h-svh w-full flex-col items-center justify-center gap-2">
<SimpleButton />
</div>
);
}
// simple-button.tsx
"use client";
import { test } from "@/actions";
import { Button } from "@/components/ui/button";
import { useState } from "react";
export default function SimpleButton() {
const [counter, setCounter] = useState(0);
return (
<Button
onClick={() => {
test();
fetch("https://jsonplaceholder.typicode.com/todos/1");
setCounter((prev) => prev + 1);
}}
>
Start {counter}
</Button>
);
}
// actions.ts
"use server";
export async function test() {
return "test";
}
Yes, you are right this is expected behaviour while using server actions with client components. what actually happens, when you update your component using useState
Next.js considered it as client component state change only, nothing concerned with serve state. So, it only re-render the component on client side (you can observe this in network tab there should not be any call to server for updating the component).
However, when you call server action in client component Next.js consider it as mutation and re-render the entire route segment in order to sync any potential change in serve state and data dependency (you can observe this too in network tab this time there should be a network request(POST) to server for syncing the updates).
And, in case of fetch api, it's just call to external server when you have response you can use it as you want (typically setted to state, updated component only on client side). But there might be some weird case, where you are using the fetch api data in server actions.
Note: Server Actions are kind of new they might refine this behaviour in future.