Build & Rendering Strategy
How Girard AI decides between static rendering, dynamic rendering, and ISR
This document explains the rendering strategy used across the app and the build-time tradeoffs behind it.
Current Strategy
Girard AI is intentionally split into two broad surfaces:
- Public marketing/content routes: can remain static when they are simple and stable.
- Authenticated product routes: should render dynamically at request time.
This split exists to keep production builds from doing unnecessary static work across the authenticated app surface.
Authenticated App Surface
The authenticated route groups are forced dynamic at the layout boundary:
app/(dashboard)/layout.tsxapp/(god-mode)/layout.tsx
Both export:
export const dynamic = "force-dynamic"
This means pages under those route groups are treated as request-scoped application routes rather than static generation candidates.
Why
- The dashboard contains hundreds of app pages.
- Most of those pages depend on auth, org membership, request context, or frequently changing database state.
- Treating them as dynamic avoids build-time fan-out and reduces the amount of route analysis/prerender work during
next build. - It also aligns the rendering model with how the product actually behaves in production.
Public Blog
The public blog currently uses dynamic server rendering, not ISR.
/blog/blog/[slug]
Both routes read from the database at request time and are documented in SEO & Blog Content System.
Important distinction
Other Girard apps may use on-demand ISR for large markdown-backed blog estates. This repository does not currently do that.
Dashboard Docs Route
The dashboard docs article route:
app/(dashboard)/docs/[category]/[slug]/page.tsx
no longer uses generateStaticParams().
That route now renders dynamically under the dashboard layout instead of expanding doc pages at build time.
When To Use Static Rendering
Static rendering is still appropriate when all of the following are true:
- the route is public
- the content is stable
- the page does not depend on request-specific auth or org context
- build-time route expansion is small and predictable
Examples:
- marketing landing pages
- legal pages
- simple public pages with fixed content
When To Use Dynamic Rendering
Use dynamic rendering when any of the following are true:
- the route requires authentication
- the route depends on tenant or organization context
- the route reads frequently changing operational data
- the route tree is large enough that static generation materially slows builds
Examples:
- dashboard workspaces
- admin pages
- god-mode pages
- internal docs under authenticated app surfaces
When To Use On-Demand ISR
Use on-demand ISR for large public content estates where:
- pages are public
- content changes are event-driven rather than request-driven
- immediate post-publish freshness matters
- pre-rendering the entire corpus at build time would be too expensive
Typical examples:
- public blog networks
- large help centers
- documentation sites with thousands of public pages
In those cases, the preferred model is:
- pre-render only what is worth caching
- invalidate with
revalidatePath()orrevalidateTag()on publish/update - avoid
generateStaticParams()for very large content sets
Developer Guidance
When adding a new route:
- Put authenticated app pages under the existing dynamic route groups.
- Do not add
generateStaticParams()to high-cardinality app routes unless there is a strong reason. - Prefer layout-level dynamic boundaries over repeating
export const dynamic = "force-dynamic"across many child pages. - For large public content systems, decide explicitly between full static generation and on-demand ISR. Do not let the default behavior make that decision implicitly.
Verification
When changing rendering behavior:
- run
npm run build - inspect the Next route output
- confirm the affected routes show as dynamic (
ƒ) or static (○) as intended
This is the fastest way to verify the build strategy matches the code, rather than relying on assumptions.