Local State and Context
Explore how to effectively manage local state and context in Next.js using React hooks such as useState, useEffect, and useContext. Understand the distinction between server and client components, how to build interactive client component islands within server-rendered pages, and implement patterns for persistent state using browser APIs safely. This lesson teaches you to create efficient stateful components while maintaining fast server-side rendering and minimizing client-side JavaScript.
In a previous lesson, we laid down the foundational theory of the App Router: Server Components by default, with Client Component “islands” for interactivity. We talked about the "use client" directive and the rule that Server Components can import Client Components, but not the other way around.
Now let’s zoom into specific hooks and connect theory with practice. We’ll focus on the regularly used React hooks (like useState, useEffect, and useContext) within the Next.js component model.
Handling local state
We already know useState is for managing state within a component. In Next.js, its presence is the primary trigger for making a component a Client Component.
Let’s revisit the “islands of interactivity” pattern. We’ll have a server-rendered page displaying static data, with a small, interactive Client Component nested inside it. We can create a simple quantity selector for a product.
// A Server Component
import QuantitySelector from '../../components/QuantitySelector';
// This is an RSC, so it can fetch data directly
async function getProductData(slug) {
// ... fetch product data from a database or API ...
return { name: 'Super Widget', description: 'An amazing widget for all your needs.' };
}
export default async function ProductPage({ params }) {
const slug = (await params).slug;
const product = await getProductData(slug);
return (
<div>
<h1>{product.name}</h1>
<p>{product.description}</p>
{/* We are embedding our interactive "island" here. */}
<QuantitySelector />
</div>
);
}This is the core pattern in action. The ProductPage remains a fast, lean Server Component, handling data fetching. The QuantitySelector, because it needs state and interactivity, is a self-contained Client Component marked with "use client". By embedding the selector in the page, we create an interactive “island” without forcing the entire page to become a Client Component. The page’s JavaScript footprint remains minimal.
Managing side effects with useEffect
useEffect ...