llms.txt
llms.txt

Rendering Model

2 min read

Choose dynamic rendering, static rendering, ISR, or PPR with route-level exports and config.

Rendering controls decide when HTML is produced. Route Runtime separately decides where a dynamic page or API handler executes and how deployment limits are applied.

Rendering options

Mode How to opt in Best for
Dynamic Default for request-bound pages Dashboards and personalized UI.
Static dynamic = force-static or use static directive Marketing pages and stable docs.
ISR revalidate = seconds Content that can refresh on a schedule.
PPR experimental_ppr = true Static shells with dynamic holes.

Route-level config

src/app/pricing/page.tsx
export const dynamic = "force-static";export const revalidate = 300;export default async function PricingPage() {  return <main>Pricing</main>;}

Use directives when compactness wins

Farm also recognizes compact rendering directives at the top of route modules. This keeps small examples readable while preserving explicit exports for Next-style compatibility.

src/app/blog/page.tsx
"use ssg; 60";export default function BlogPage() {  return <main>Blog</main>;}

use ssg; 60 statically generates the route and revalidates it every 60 seconds. Farm also accepts use ssg, use dynamic, and use ppr; 60 when those rendering modes fit the route.

Dynamic rendering

Use dynamic rendering for request-specific pages such as dashboards, account settings, and pages that depend on cookies, headers, or per-user data.

src/app/dashboard/page.tsx
export const dynamic = "force-dynamic";export default async function DashboardPage() {  return <main>Dashboard</main>;}

Static rendering

Use static rendering for pages that can be built once and served quickly.

src/app/about/page.tsx
export const dynamic = "force-static";export default function AboutPage() {  return <main>About Farm</main>;}

ISR-style revalidation

revalidate caches a static response and refreshes it after the configured number of seconds.

src/app/pricing/page.tsx
export const dynamic = "force-static";export const revalidate = 300;export default async function PricingPage() {  const plans = await loadPlans();  return <PricingTable plans={plans} />;}

PPR with Suspense

PPR is for pages with a stable shell and dynamic sections. Put dynamic work behind Suspense boundaries so the shell can be cached while the slower section resolves independently.

src/app/dashboard/page.tsx
import { Suspense } from "react";export const experimental_ppr = true;export const revalidate = 60;export default function DashboardPage() {  return (    <main>      <h1>Dashboard</h1>      <Suspense fallback={<RevenueSkeleton />}>        <RevenuePanel />      </Suspense>    </main>  );}

Choosing a mode

Need Use
User-specific data on every request dynamic = &quot;force-dynamic&quot;
Stable docs, marketing, or policy page dynamic = &quot;force-static&quot;
Stable page with scheduled refresh revalidate = 60
Static shell plus dynamic holes experimental_ppr = true with Suspense