Performance: Server state vs client state
This concept affects page load speed and interaction responsiveness by determining where data is fetched and stored.
export async function getServerSideProps() { const res = await fetch('https://api.example.com/data'); const data = await res.json(); return { props: { data } }; } export default function Page({ data }) { return <div>{data.content}</div>; }
export default function Page() { const [data, setData] = React.useState(null); React.useEffect(() => { fetch('/api/data').then(res => res.json()).then(setData); }, []); if (!data) return <p>Loading...</p>; return <div>{data.content}</div>; }
| Pattern | DOM Operations | Reflows | Paint Cost | Verdict |
|---|---|---|---|---|
| Client fetch on mount | Minimal initial DOM | Multiple reflows due to loading state changes | Multiple paints for loading and content | [X] Bad |
| Server-side data fetch | Full DOM with data on load | Single reflow on initial render | Single paint with content | [OK] Good |
| Client state for UI interactions | DOM updates on interaction | Reflows only on state change | Paints only on changed elements | [OK] Good |
| Server state for UI interactions | Network requests on each interaction | Reflows delayed by network | Paints delayed by data arrival | [X] Bad |
server state in Next.js?useState hook for client state in Next.js?const [count, setCount] = useState(0). const [data, setData] = useState(fetch('/api/data')) wrongly calls fetch inside useState. const data = await fetch('/api/data') is an async fetch, not useState. const count = useState(0) misses destructuring.import { useState, useEffect } from 'react';
export default function Example() {
const [serverData, setServerData] = useState(null);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => setServerData(data.message));
}, []);
return {serverData ?? 'Loading...'};
}serverData starts as null, so the component shows 'Loading...' initially because of the nullish coalescing operator.import { useState, useEffect } from 'react';
export default function DataFetcher() {
const [data, setData] = useState();
useEffect(async () => {
const res = await fetch('/api/data');
const json = await res.json();
setData(json);
}, []);
return <div>{data.message}</div>;
}useEffect callback cannot be async directly; it should call an async function inside.getServerSideProps fetches data on the server before rendering, providing server state.useState allows local filtering without refetching, which is client state.getServerSideProps and store in client state with useState for filtering. [OK]Pass the test and this pattern moves unknown