skills/goncy/skills/nextjs-rendering

nextjs-rendering

SKILL.md

Next.js Rendering

Expert guidance on Next.js rendering modes, streaming behavior, and static vs dynamic page generation.

Static vs Dynamic Rendering

Next.js has two fundamental rendering types: static and dynamic.

Static rendering:

  • Pages are built at build time and uploaded to a CDN
  • No compute occurs at request time
  • Assets are replicated to edge locations globally
  • Faster response times (no compute, cached near user)
  • Remains available even if origin region goes down

Dynamic rendering:

  • Pages are executed on-demand in a configured region
  • Involves compute at request time
  • Goes down if the region becomes unavailable
  • Required when content cannot be determined at build time

SSG (Static Site Generation) and ISR (Incremental Static Regeneration) are the same underlying mechanism — the only difference is whether all paths are generated upfront or incrementally on-demand.

Determining Render Mode: Async Dynamic APIs

Next.js determines whether a page is static or dynamic using async dynamic APIs. The framework performs a prospective render to detect what resolves synchronously vs asynchronously.

The microtask test:

  1. Next.js runs an initial "warmup render" to search for cached instances and fill caches
  2. A second render runs for just one tick, then aborts
  3. Anything that resolves within that microtask is considered "instant" and can be static
  4. Anything that cannot resolve in one tick is treated as dynamic

Special case for params: Routes using params can be static because each route is called once per item in generateStaticParams. Since the params are known at build time, they resolve instantly.

// Static: params from generateStaticParams resolve instantly
export async function generateStaticParams() {
  return [{ id: '1' }, { id: '2' }];
}

export default async function Page({ params }: { params: { id: string } }) {
  // params.id is instant - this page can be static
}

Streaming and Suspense Boundaries

Streaming is only enabled for dynamic rendering. Static content does not stream because no compute is involved — the complete HTML is served from the CDN.

Document-level Suspense requirement: To enable streaming with a loading skeleton, wrap the HTML document with an empty Suspense boundary. Without a document wrapper outside the boundary, there is nothing to stream — the framework needs the outer shell to send while inner content loads.

// Enable streaming with document shell
<Suspense fallback={null}>
  <html>
    <body>
      <Suspense fallback={<Skeleton />}>
        <SlowComponent />
      </Suspense>
    </body>
  </html>
</Suspense>

ISR and Suspense fallbacks: Suspense boundary fallbacks do not display when generating ISR paths. Since ISR generation happens at build time or on first request (static generation), there is no streaming — the complete page is generated before being served.

Cache Components (use cache)

Cache components can combine static and dynamic rendering within the same page, but the page itself still has a render mode.

Applying use cache to a page component: When use cache is added to a page or the main component of a page, the page produces static output that:

  • Will be cached (static)
  • Won't be streamed
  • Won't have "holes" (Suspense boundaries) in it

Cache components with dynamic pages: Even with use cache on individual components, the page overall is still either static or dynamic. Cache components allow granular control — static cached sections within dynamic pages, or dynamic sections within static pages.

// Page-level cache: produces static output
'use cache';
export default async function Page() {
  // Entire page is static, no streaming
}

// Component-level cache: can mix static/dynamic
export default async function Page() {
  return (
    <>
      <StaticCachedSection />  {/* has 'use cache' */}
      <DynamicSection />        {/* no cache directive */}
    </>
  );
}

Rendering Workflow

When working with Next.js rendering:

  1. Determine if the page can be static (all data available at build time)
  2. If static, decide between full SSG (all paths) or ISR (incremental)
  3. If dynamic is required, plan Suspense boundaries for streaming
  4. Add document-level Suspense wrapper if streaming with skeletons
  5. Use use cache granularly to cache expensive components within dynamic pages
  6. Remember: streaming only works for dynamic rendering, not static/ISR
Weekly Installs
27
Repository
goncy/skills
GitHub Stars
14
First Seen
Feb 12, 2026
Installed on
opencode26
codex26
gemini-cli26
github-copilot25
kimi-cli24
amp24