Unit 2: Rendering Strategies and Data Fetching
I. Orientation: Rendering and Data in Modern Web Applications
Rendering is the process of transforming application code and data into HTML that a browser can display. Modern frameworks such as Next.js can perform this work at build time, on a server for each request, incrementally after deployment, or inside the browser. Data-fetching location, cache policy, personalization, freshness, performance, and interactivity determine which approach is appropriate.
- Governing principle: Execute each task as close as practical to the data it needs while sending the browser only the code and data required for interaction.
- Rendering locations:
- Build server: Produces HTML before deployment through Static Site Generation.
- Application server: Produces HTML when requests arrive through Server-Side Rendering.
- Browser: Produces or updates the interface through client-side rendering.
- Core outputs: Servers commonly return HTML for immediate display, JavaScript for interactivity, and serialized data for reconstructing application state.
- Hydration: Client-side JavaScript attaches event handlers and state to server-rendered HTML, making otherwise static markup interactive.
- Freshness requirement: Content may be immutable, periodically updated, request-specific, or continuously changing; its lifetime influences caching and rendering.
- Performance criteria: Important measures include server response time, time to visible content, JavaScript bundle size, interaction readiness, and backend workload.
- Architectural rule: A single application may combine strategies by route, component, or data request rather than adopting one strategy globally.
II. Page Rendering Strategies — When HTML Is Produced
Rendering strategies primarily differ in when HTML is generated, where computation occurs, and how quickly updated information becomes visible.
A. Server-Side Rendering (SSR)
Server-Side Rendering generates HTML on the server for an incoming request, making it suitable for personalized or frequently changing pages.
- Request lifecycle: The browser requests a URL, the server retrieves required data, renders HTML, and returns the response; client JavaScript may then hydrate interactive components.
- Fresh content: Request-time execution can display current inventory, account information, or search results without waiting for a static rebuild.
- Request context: Cookies, headers, authentication state, and URL parameters can influence the generated response.
- SEO and initial display: Crawlers and users receive meaningful HTML instead of an initially empty application container.
- Cost: Rendering and data retrieval occur repeatedly, increasing server computation and potentially increasing time to first byte.
- Concrete pattern:
export default async function AccountPage() {
const account = await getCurrentAccount();
return <h1>Welcome, {account.name}</h1>;
}- Best fit: Use SSR when content must be computed per request and cannot be safely shared through a static cache.
B. Static Site Generation (SSG)
Static Site Generation creates HTML during the build process and serves the resulting files repeatedly without request-time rendering.
- Build-time execution: Product documentation, articles, and marketing pages are rendered before deployment using data available during the build.
- Delivery speed: Generated files can be distributed through a content delivery network (CDN), placing responses near users.
- Reliability: Requests do not depend on live rendering or database access after deployment, reducing runtime failure points.
- Scalability: One generated file can serve many requests with little application-server work.
- Freshness limitation: Changes normally require a new build and deployment; a page may remain outdated until that process completes.
- Build growth: Generating 100,000 product routes can make builds substantially slower than generating 100 routes.
- Best fit: Use SSG for public content that is identical for all users and changes infrequently.
C. Incremental Static Regeneration (ISR)
Incremental Static Regeneration combines static delivery with controlled background regeneration after deployment.
- Revalidation interval: A route may declare a lifetime such as
revalidate = 300, allowing a cached result to remain valid for approximately five minutes. - Regeneration behavior: After content becomes stale, a request may receive the existing page while the framework generates and caches an updated version.
- Operational advantage: Large sites can update selected pages without rebuilding every route.
- On-demand revalidation: A content-management webhook can invalidate a path or cache tag immediately after an editor publishes a change.
- Consistency tradeoff: Users may briefly receive stale content, so ISR is unsuitable where every response must reflect the latest transaction.
- Concrete configuration:
export const revalidate = 300;
export default async function NewsPage() {
const stories = await getStories();
return <StoryList stories={stories} />;
}- Best fit: Use ISR for catalogs, news pages, and public profiles that need fast static delivery with bounded staleness.
D. Client-side Rendering
Client-side rendering uses JavaScript in the browser to create or update the visible interface, commonly after an initial application shell loads.
- Execution flow: The browser downloads HTML and JavaScript, executes the application, requests data, and renders the resulting view.
- Interactivity: Local state can immediately drive filters, drag-and-drop tools, editors, and dashboards without full-page requests.
- Navigation: Client routers can replace page content while preserving application state and avoiding complete document reloads.
- Initial-load cost: Large JavaScript bundles and delayed data requests can postpone meaningful content, especially on slower devices.
- SEO concern: An empty initial shell may be less dependable for indexing than HTML containing the page’s primary content.
- Security boundary: Browser code and its environment variables are visible to users; database credentials and private API keys must remain server-side.
- Best fit: Use client rendering for interaction-heavy, user-specific areas where search indexing and immediate server-rendered content are less important.
E. Rendering Strategy Selection
Rendering strategy selection balances freshness, personalization, performance, scalability, and implementation complexity.
- Static public content: Choose SSG when every visitor can receive the same version and deployment-time freshness is sufficient.
- Periodically changing content: Choose ISR when static speed is desirable and a defined stale period, such as five minutes, is acceptable.
- Request-specific content: Choose SSR when authentication, cookies, headers, or current transactional data determine the response.
- Highly interactive content: Choose client-side rendering for browser-driven state and updates after the initial page has loaded.
- Hybrid design: Render a product description statically, place personalized recommendations in a dynamic server region, and manage the shopping-cart controls on the client.
- Decision test:
- Determine whether output is shared or user-specific.
- Define the maximum acceptable data age.
- Identify whether browser APIs or event handlers are required.
- Estimate build size, server load, and client JavaScript cost.
III. Component Execution — Dividing Server and Browser Responsibilities
Component-based frameworks can assign different parts of one route to the server or browser, reducing client code while preserving focused interactivity.
A. Server Components
Server Components execute on the server and send rendered results rather than their complete implementation code to the browser.
- Direct data access: A component can query a database or internal service without exposing credentials or creating a public browser endpoint.
- Smaller bundles: Server-only dependencies, parsing libraries, and component logic do not need to enter the client JavaScript bundle.
- Security: Secrets remain protected when they are read only by server-executed modules.
- Limitations: Server Components cannot use browser APIs such as
window, attach event handlers such asonClick, or use client-only state hooks. - Composition: A Server Component can fetch product data and pass serializable values as props to an interactive Client Component.
- Concrete example:
export default async function ProductPage({ id }: { id: string }) {
const product = await database.product.findUnique({ where: { id } });
return <ProductDetails product={product} />;
}B. Client Components
Client Components execute in the browser and provide stateful behavior, event handling, and access to browser APIs.
- Client boundary: In Next.js, the
"use client"directive marks a module whose component tree must be included in the client bundle. - Interactive capabilities: Hooks such as
useStateand handlers such asonClicksupport menus, forms, counters, and live filters. - Browser integration: Client Components can access
localStorage, geolocation, observers, and other browser-only facilities after mounting. - Serializable props: Values passed from a Server Component must be transferable; database connections and ordinary function closures cannot cross the boundary.
- Bundle discipline: Place the client boundary around the smallest interactive region to avoid shipping unrelated server-renderable code.
- Concrete example:
"use client";
import { useState } from "react";
export function Quantity() {
const [count, setCount] = useState(1);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}IV. Data Retrieval — Obtaining Remote and Application Data
Data can be fetched through the browser or server, but location changes security, latency, caching opportunities, and access to request context.
A. Fetch API
The Fetch API is a promise-based interface for making HTTP requests and processing HTTP responses.
- Request form:
fetch(url, options)accepts a resource URL and optional method, headers, body, cache directives, and cancellation signal. - Response handling:
response.okindicates a successful200–299status;response.json()asynchronously parses a JSON body. - Error behavior:
fetch()rejects for network failures, but an HTTP404or500normally resolves to aResponseand must be checked explicitly. - Mutation requests: JSON bodies should be serialized with
JSON.stringify()and identified usingContent-Type: application/json. - Cancellation: An
AbortControllercan stop an obsolete request when a component unmounts or a search term changes.
const response = await fetch("/api/products");
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const products = await response.json();B. Client-side Fetching
Client-side fetching requests data after browser code loads and is useful for user-triggered or rapidly refreshed information.
- Lifecycle: A component typically represents loading, success, empty, and error states while an asynchronous request is pending.
- Use cases: Autocomplete results, notification counts, live analytics, and data loaded after clicking a tab suit browser requests.
- Request ordering: Search interfaces should cancel or identify outdated requests so a slow response for
"ca"does not overwrite results for"cat". - Caching libraries: Tools such as TanStack Query or SWR can provide deduplication, retries, background refresh, and cached client state.
- Tradeoff: Data appears after JavaScript execution and another network round trip, which can produce loading placeholders and slower initial content.
- Access constraint: The browser should call authorized public endpoints, not connect directly using privileged database credentials.
C. Server-side Data Fetching
Server-side data fetching retrieves information before or while server-rendered output is produced.
- Backend proximity: Server code can communicate directly with databases and internal services, often reducing browser-visible network steps.
- Credential protection: API secrets, service tokens, and database passwords remain in the trusted server environment.
- Rendering integration: Awaited data can be included in the initial HTML or streamed in server-rendered segments.
- Parallelization: Independent requests should begin together with
Promise.all()to avoid a sequential latency waterfall. - Request context: Authentication cookies and headers can select user-specific records while authorization remains enforced on the server.
- Failure handling: Timeouts, unavailable dependencies, and missing records should map to controlled error, fallback, or not-found states.
const [profile, orders] = await Promise.all([
getProfile(userId),
getOrders(userId),
]);V. Data Lifetime — Controlling Reuse and Freshness
Data lifetime policies determine whether an application reuses previous results, fetches new values, or regenerates affected content.
A. Caching and Revalidation
Caching stores reusable responses or computed results, while revalidation determines when cached information must be refreshed or invalidated.
- Cache layers: Reuse may occur in the browser, a CDN, a framework data cache, a rendered-route cache, or an application datastore.
- Freshness model: Time-based revalidation refreshes after an interval; event-based revalidation responds to a mutation, publication, or webhook.
- Cache keys: The URL, query parameters, method, headers, user scope, and framework tags may distinguish cached entries.
- Shared-data risk: Personalized responses must not be stored under a public cache key, because another user could receive private content.
- HTTP controls:
Cache-Control: public, max-age=60permits a fresh shared response for 60 seconds, whereasno-storerequests fresh retrieval. - Tag invalidation: Product pages and category listings can share a
productstag so one catalog update invalidates all dependent entries. - Stale-while-revalidate: A cache may serve an older response immediately while refreshing it in the background, improving speed at the cost of temporary staleness.
- Selection rule: Cache stable, shareable data aggressively; use short lifetimes or explicit invalidation for changing data; disable shared caching for sensitive request-specific results.
Did this save you a night before the exam?
LPU Notes is free, and it stays free. Ads cover part of the server bill. The rest comes out of a student's own pocket: the domain, the storage, and keeping the site up through the weeks everyone needs it at once.
The payment button didn't load. An ad blocker or a filtered network is the usual reason. to try again.
Nothing here is ever locked, and nothing unlocks. Chip in only if it was worth it. What it pays for →