tune-render-mode
Skill106 starsChanged 7 months agoMITView on GitHub
Recommend a render mode (ssg, isg, ssr, spa) for each pracht route from what its loader does, and apply the changes after confirmation. Use for "tune render modes", "should this route be SSG", "review SSG/ISG/SSR choices", "make my site faster".
- Reads credentials
In this repository33 other files it gives its agents.
- AGENTS.mdAGENTS.md
- add-authSkill
- add-capabilitiesSkill
- add-contentSkill
- add-dbSkill
- add-i18nSkill
- add-imagesSkill
- add-observabilitySkill
- add-openapiSkill
- audit-a11ySkill
- audit-agent-surfaceSkill
- audit-authSkill
- audit-bundlesSkill
- audit-csrfSkill
- audit-depsSkill
- audit-headersSkill
- audit-islandsSkill
- audit-loadersSkill
- audit-redirectsSkill
- audit-secretsSkill
- audit-seoSkill
- audit-shellsSkill
- configure-isgSkill
- migrate-nextjsSkill
- pracht-debugSkill
- pracht-deploySkill
- pracht-scaffoldSkill
- pracht-test-apiSkill
- pre-deploySkill
- scaffold-e2eSkill
- scaffold-testsSkill
- typed-routesSkill
- upgrade-prachtSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 2 are distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 206
Never silently change render modes.
- Very specific: line 208
If a route uses auth middleware, default to ssr — auth implies cookies.
- Distinctive: line 211
All three adapters support ISG — the mechanisms differ.
- Very specific: line 221
Use pracht inspect routes --json rather than reading src/routes.ts manually — the resolved graph already accounts for groups and …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Pracht Tune Render Mode
Walk every route, read its loader, and recommend the cheapest render mode that still satisfies the route's data dependencies.
This is a tune skill, not a report-only audit: it ends by applying edits. The contract is propose-then-apply — produce the recommendation table and the exact diffs first, then apply them only after the user explicitly confirms (per route or as a batch). Never edit before that confirmation.
Decision Tree
For each route:
- No
loader, nogetStaticPaths, no per-request data →ssg- Pure UI. Build once, serve from CDN. Highest performance.
- Loader reads only build-time-stable data (filesystem, static config,
typed CMS export, no
request/params/context.envuse) →ssgorisg- Pick
isgwithtimeRevalidate(seconds)if the source can change between deploys (CMS, pricing pages, public catalog). - Pick
ssgif the source only changes when you redeploy.
- Pick
- Loader reads
paramsto fetch data, but notrequest/cookies →ssgwithgetStaticPaths, orisgif the universe of params is open-ended (millions of slugs). - Loader reads
request, but the data is shareable (request-static: request used only for cache keys likeAccept-Language, never for identity) →ssrby default;isgis possible on adapters whose cache can key on the varying dimension (e.g. a normalized cache key at a Cloudflare gateway). If the variant fan-out is unbounded or the adapter cache can't express the Vary, stay onssr. - Loader reads cookies, auth headers,
context.envper-request, or anything personalized →ssr- Auth dashboards, anything user-specific, anything that varies by user identity at request time.
- Unless only a small part of the page is personal (a cart count,
"signed in as …", a recommendation strip) and the rest is shared: then
recommend
ssg/isgfor the route plus a server island for the personal part (see branch 5b).
5b. Personal data is confined to a small, self-contained part of the page
→ keep the route ssg/isg and move that part into src/server-islands/:
a default-export component plus a loader that reads context instead of
the page loader, used as JSX with a fallback. See
https://pracht.resynapse.dev/docs/server-islands. Not available on the static
adapter. Server island props are untrusted input — the server island loader must
authorize from context itself.
- Heavy client interactivity, no SEO need, auth-gated →
spa- Internal admin tools, post-login dashboards where the first paint can be a skeleton.
Step 1: Enumerate
MCP: if pracht dev-mcp is registered, prefer its inspect_*, doctor, and verify tools to the CLI.
pracht inspect routes --jsonPrerequisite: pracht inspect needs a vite config with the pracht plugin
wired up.
Capture: path, file, current render, revalidate, middleware, and
the top-level mode field (manifest vs pages router — Step 4 depends on it).
Step 2: Read each loader
For each route, open the file and look at the loader/getStaticPaths
exports. Tag the loader with one of:
none— no loader at allstatic— only reads imports / pure dataparam-static— readsparamsonlyrequest-static— readsrequestfor cache keys but data is shareable (e.g.Accept-Language) — decision-tree branch 4request-personalized— reads cookies, auth headers, user-specificcontext.envlookups — decision-tree branch 5
Step 3: Recommend
Produce a table:
| Route | Current | Recommended | Severity | Reason |
Severity: error (broken today, e.g. ssg with a loader that reads
request — cannot be prerendered correctly), warn (works but mis-tuned,
e.g. ssr with no loader), info (optional improvement, e.g. hydration
tuning).
Examples of recommendations:
ssr→ssgwhen loader is empty: "no loader; no per-request data — make it static." (warn)ssr→isg(3600)when loader fetches a public CMS: "shared data, freshness acceptable at 1 hour." (warn)ssg→ssrwhen loader readsrequest.headers.get('cookie'): "reads request — cannot be prerendered." (error)spa→ssrwhen route has SEO-relevanthead()and unauthenticated visitors should see content. (warn)ssr→isg(600)+ server island when the loader reads the session only for a header greeting or cart badge: "move the personal part intosrc/server-islands/; the page itself is shared." (info)
Step 3b: Consider the hydration mode too
Render mode controls when HTML is generated; hydration mode controls how
much JavaScript ships afterwards (hydration: "full" | "islands" | "none",
default "full" — see https://pracht.resynapse.dev/docs/islands). A current @pracht/cli emits the
resolved hydration per route in the inspect JSON; if your CLI predates the
field (absent from the JSON), grep the manifest for hydration: (pages apps:
HYDRATION exports) instead. While tuning, also flag:
- Routes with no interactivity at all (no event handlers, no hooks) →
hydration: "none"— zero JS shipped. - Content-heavy routes with one or two isolated widgets (counter, search
box, newsletter form) →
hydration: "islands"with the widgets moved tosrc/islands/. - Caveats: islands routes use MPA-style full-document navigation (no client
router) unless
client.islandsNavigationswaps islands pages in place, island props must be JSON-serializable, andrender: "spa"cannot combine with"islands"/"none".
Step 3c: Consider streaming for slow SSR routes
An ssr route whose loader has one slow call and several fast ones is a
candidate for defer() plus streaming: true. Flag it when the loader awaits
more than one independent source and at least one is materially slower — the
whole document waits on the slowest. On Preact 11 the shell also
becomes interactive before the slow data arrives; on Preact 10 it waits for the
whole document.
// before: TTFB waits on getReviews()
return { product: await getProduct(id), reviews: await getReviews(id) };
// after: shell flushes without reviews, which stream in
return { product: await getProduct(id), reviews: defer(getReviews(id)) };Pages routes use export const STREAMING = true with SSR and full hydration.
Recommend streaming: true only alongside a <Suspense> boundary reading the
deferred value with use(); without a boundary there is nothing to flush
early. Do not recommend it for:
ssg/isgroutes — they write files and the manifest rejects the combination- routes with
hydrationother than"full"— no client runtime resumes a boundary, and the manifest rejects it - routes whose loader is a single slow call with nothing else to show — the shell would be empty and the user sees a skeleton rather than content
Also mention the tradeoffs when proposing it: a deferred rejection surfaces at
the read site rather than as an error document (the response is already
committed at 200), and streaming needs a script-src that allows the
renderer's inline bootstrap script. Route head() and headers() hooks run
before deferred work settles, so any data they need must stay awaited. Confirm
the app uses
preact-render-to-string 6.7 or newer before enabling streaming.
Step 4: Propose diffs, then apply on confirmation
Present the exact edits and wait for approval. Where the edit lands depends
on the router mode from Step 1:
- Manifest apps: edit
src/routes.tsto update therenderfield. For ISG, addrevalidate: timeRevalidate(N)and importtimeRevalidatefrom@pracht/core. Hydration changes update thehydrationfield the same way. - Pages apps: render mode is a per-file constant —
export const RENDER_MODE = "ssg"in the page module (valid values"ssr" | "ssg" | "isg" | "spa"; the default is"ssr", overridable globally viapracht({ pagesDefaultRender: "..." })in vite config). For ISG, also add a positive integer time policy such asexport const REVALIDATE = 3600; pages mode requires it and supports time-based revalidation only. Eject for webhook or combined policies. Hydration isexport const HYDRATION = "..."in the same file. If most pages want the same mode, prefer changingpagesDefaultRenderover adding a constant to every file.
Apply the edits only after the user confirms.
Rules
- Never silently change render modes. Always present the recommendation and the exact diff first; apply only after explicit user approval.
- If a route uses
authmiddleware, default tossr— auth implies cookies. The exception is middleware that only reads the session for a small personal part of an otherwise public page: that is the server island case (5b). All three adapters support ISG — the mechanisms differ. Confirm which adapter is in play, then use this capability table:
Adapter ISG mechanism (default) Notes Node Filesystem: isg-manifest.json+ file-mtime revalidationServes stale immediately, refreshes in place. Cloudflare Worker-managed Workers Cache API, per colo — works without any extra config cloudflareAdapter({ cache: true })+"cache": { "enabled": true }in wrangler config is an optional upgrade that moves time-revalidated routes to an edge-tier cache in front of the Worker; webhook-only routes stay worker-managed. Webhook invalidation on the default path is per-colo, not a global purge.Vercel Native ISR: Build Output API prerender functions with expirationfrom the time policy; for webhook revalidation,PRACHT_REVALIDATE_TOKENbecomes thebypassTokenand must be set at build timeISG routes deploy as Node Serverless Functions (Vercel rejects ISR on an Edge Function) while SSR stays on the edge. Time-only ISR does not require the token. See https://pracht.resynapse.dev/docs/adapters/vercel-netlify#vercel. - For dynamic SSG/ISG routes, ensure
getStaticPathsexists. Flag if missing. - Use
pracht inspect routes --jsonrather than readingsrc/routes.tsmanually — the resolved graph already accounts for groups and inheritance.
$ARGUMENTS
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

