Actors, Scripts & APIs: A Gentle Guide to Next.js Components

Picture your frontend as a theater.
The curtain rises. A spotlight sweeps the stage.
Some actors stand still, others dance, and a few call for lines from backstage.
This is the interplay of static, client, and fetcher components in Next.js.

Good web architecture isn’t just about speed—it’s about timing, purpose, and flow.

Static Pages — The Stage is Set


Some parts of your site are like a theater set: built once, ready before the audience arrives.


In Next.js, this is Static Site Generation (SSG). Pages are pre-rendered as HTML and CSS, served instantly, no live data required.


When I built Accessibly Yours at ZafronMoon, pages like /home, HeroSection.tsx, and DemoResults.tsx were static. They didn’t change unless we redeployed. Like sturdy stage props, they were reliable and fast.

Use SSG for content that stays the same, like landing pages or marketing sections.

⚡ Client Components — Actors Take the Stage

Some elements need to come alive in real-time.
A button click, a form input, or a smooth scroll—these are the actors of your app.


In Next.js, mark these components with "use client" to tell the browser: “This runs interactively.”


For example, in HeroSection.tsx, a CTA button scrolled users to an audit form. The component started as static HTML but “hydrated” into a reactive React component in the browser.


It’s like an actor stepping forward to engage the audience.

Add "use client" for components using useState, useEffect, or browser APIs like scrollTo.

🔥 Fetcher Components — Lines from Backstage

Some components don’t just react—they fetch live data from APIs, like actors calling for fresh lines mid-performance.


In Accessibly Yours, components like UrlScanner.tsx and SiteQualitySnapshot.tsx pulled audit results from a Lighthouse API or our Fly.io server. These fetchers used useEffect or async functions to grab data and update the UI dynamically.


If a component fetches data on load (e.g., useLighthouseScores()), it’s a fetcher.

🧩 Why Split /client/ and /fetchers/?

The distinction isn’t about when the code runs—it’s about what the component does.

Components that need "use client":


  • Use useState or useRef - Include button clicks or scroll logic - Call browser APIs like scrollTo

Components that don’t need "use client":


  • Render static layouts or markup only - Contain pure visual displays (no user input)
  • Don’t include interaction or data fetching

Organizing components into /client/ (interactive) and /fetchers/ (data-fetching) keeps roles clear. At ZafronMoon, this separation reduced bundle sizes and made our codebase easier to maintain.

🎭 SiteQualitySnapshot — A Supporting Role


Take SiteQualitySnapshot.tsx.


It receives pre-fetched data as props and displays it. It doesn’t fetch anything itself—it just delivers its lines.


That’s why it lives in /client/, not /fetchers/. It’s presentational, not operational, like an actor reciting a prepared script.


Presentational components belong in /client/. Fetchers, which handle API calls, go in /fetchers/.

🔧 Mind-Map Summary

forms, scrolling) /fetchers → Live data from APIs

This mental model helped us at ZafronMoon build faster, more accessible apps with clear separation of concerns.

When architecture aligns with intention, performance feels like poetry.