How to Update React Server Components
React Server Components (RSC) represent a paradigm shift in web
development by rendering components on the server to improve performance
and SEO. Because Server Components run exclusively on the server, they
cannot use client-side state hooks like useState or
useEffect to trigger visual updates. Instead, updating a
Server Component requires requesting a new render from the server and
merging the new UI payload into the existing client-side page. This
article explains the primary methods used to update React Server
Components, including Server Actions, router refreshing, and URL
parameter mutations.
1. Using Server Actions and Data Revalidation
The most common and efficient way to update a Server Component is by using Server Actions combined with data revalidation. When a user interacts with a Client Component (like clicking a button or submitting a form), they trigger a Server Action that mutates data on the server. Once the mutation is complete, you tell React to revalidate the cache, which forces the Server Component to re-render with the fresh data.
In frameworks like Next.js, this is achieved using
revalidatePath or revalidateTag.
// app/actions.js (Server Action)
'use server'
import { revalidatePath } from 'next/cache';
export async function updateTodoItem(todoId, completed) {
// 1. Mutate the database
await db.todo.update({ where: { id: todoId }, data: { completed } });
// 2. Revalidate the path containing the Server Component
revalidatePath('/todos');
}When revalidatePath('/todos') is called, the server
re-renders the Server Components associated with that route and sends
the updated UI back to the browser, updating the screen seamlessly
without a full page reload.
2. Triggering a Router Refresh
If you need to update a Server Component without a direct data mutation action—such as after a specific client-side event—you can explicitly tell the router to refresh the current route.
In Next.js, you can use the useRouter hook from
next/navigation inside a Client Component. Calling
router.refresh() requests a new payload for the current
route from the server.
// app/components/RefreshButton.js (Client Component)
'use client'
import { useRouter } from 'next/navigation';
export default function RefreshButton() {
const router = useRouter();
return (
<button onClick={() => router.refresh()}>
Sync Data
</button>
);
}When router.refresh() is executed: * The server
re-renders the Server Components. * The client receives the updated
payload. * The browser retains the client-side state (such as input
values or scroll position) while updating the DOM with the new
server-rendered content.
3. Changing URL and Query Parameters
Because Server Components have access to route parameters and search queries (URL query parameters), changing the URL is an effective way to trigger a re-render. When the URL changes, the server processes the new request parameters and sends the updated Server Component output.
For example, a pagination component or a search input can update the Server Component by pushing a new query string to the router:
// app/components/SearchInput.js (Client Component)
'use client'
import { useRouter, usePathname } from 'next/navigation';
export default function SearchInput() {
const router = useRouter();
const pathname = usePathname();
function handleSearch(term) {
const params = new URLSearchParams();
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
router.push(`${pathname}?${params.toString()}`);
}
return (
<input
type="text"
onChange={(e) => handleSearch(e.target.value)}
placeholder="Search products..."
/>
);
}The parent Server Component reads the searchParams from
its props and fetches new data based on the updated search query,
rendering the fresh results automatically.