Docs

This page isn't translated yet

Revalidation

What each save revalidates, skipping it in scripts, and manual revalidation.

On this page

Systhema wires Next.js cache revalidation hooks so editor changes appear without a manual rebuild. The triggers:

  • Header or Footer global saved (published) → flushes the / layout cache, so every page revalidates on next request.
  • General Settings global saved → tab-aware:
    • Pages, SEO, or Cookie Consent tab → flush the / layout cache (every page revalidates — SEO defaults affect every page's metadata).
    • Emails tab → no revalidation (email settings don't affect page renders).
  • Page saved or deleted → revalidates that page's own path; if it is the currently configured homepage, / is revalidated too.
  • Form saved or deleted (from @payloadcms/plugin-form-builder) → revalidates only the published Pages that embed that form via a form block — not the whole site. Draft-only saves are skipped.
  • Upload's alt text changed → flushes the / layout cache. An image can appear anywhere (hero backgrounds, blocks, inline lexical nodes), so which pages embed it isn't reliably queryable — a site-wide flush guarantees every page showing the image picks up the new alt.

This is a behaviour change from versions that flushed on every general-settings save. If you have custom afterChange hooks on a global, check whether they should be restricted to specific tabs.

Posts, categories, tags and authors have their own fan-out; see Revalidation (ISR freshness). With the Cloudflare integration on, every revalidation also purges the edge cache; see Auto-purge.

Localized sitesLink to this section

Localized hooks invalidate the internal catch-all route, not the displayed public pathname. contentLocaleInternalPath() produces /<locale><pathname> for exact-one-locale, as-needed, always, and domain routing, while locale-off keeps the original pathname. Keep contentLocalePath() and absolute public-href helpers for rendered links, canonical metadata, and sitemaps; an unprefixed public path is not a safe locale-specific revalidation key. (contentLocalePath / contentLocaleInternalPath are @systhemaui/payload/next's content-locale-scoped wrappers around @systhemaui/core's getSysthemaLocalizedPathname / getSysthemaInternalLocalePathname — see Frontend locales.)

Skipping revalidation in scripts (context.disableRevalidate)Link to this section

revalidatePath() only works inside a Next.js request — called from a seed script, a migration, or any other Local API program it throws Invariant: static generation store missing. Because afterChange runs inside Payload's transaction, that throw rolls the write back: the script reports success and the database is unchanged.

So every write from a script should carry the opt-out:

await payload.update({
  collection: 'forms',
  id,
  data,
  overrideAccess: true,
  context: { disableRevalidate: true },
})

Every Systhema revalidation hook honours it — pages, posts, categories, tags, components, forms, redirects, users, uploads, and the globals. (Before 1.7, the forms, redirects, components and upload hooks did not, so seeding those collections needed an error-swallowing wrapper. Drop the wrapper and pass the context instead.) Since a script's writes never revalidate, finish by calling POST /sys/revalidate with { "paths": "*" }.

Manual revalidationLink to this section

For the "something looks stale, refresh it now" case there are two on-demand entry points, both backed by the same internal revalidateAllPublishedPages helper (enumerate published Pages → revalidate each path, plus / when a homepage is configured):

  • POST /sys/revalidate — machine-facing, authenticated with the SYSTHEMA_API_SECRET bearer token (for scripts and cron). Body { "paths": "*" } revalidates every published page; { "paths": ["/a", "/b"] } targets specific paths. Returns { revalidated, count }.
  • General Settings → Troubleshoot → "Revalidate all pages" — the same paths: "*" behaviour, gated on a logged-in admin session instead of the API secret. See Troubleshoot tab.