Appearance
Cache
Rendered sections are fragment-cached in published mode. Invalidation works by cache tag, so caching only happens on a taggable store — one whose driver extends Laravel's TaggableStore. On any other driver the tag probe fails and FilamentCraft transparently falls back to no-cache: pages render fresh, nothing breaks, and you get no speedup.
| Taggable — caching works | Not taggable — silently uncached |
|---|---|
redis, memcached, apc, array | file, database, dynamodb |
file is Laravel's default, so out of the box you get no fragment caching until you point this at a taggable store.
php
'cache' => [
'enabled' => true,
'store' => null, // shipped default: the app's default cache store
'ttl' => 3600, // fragment lifetime in seconds
'tag_prefix' => 'fc',
],'store' => null (the shipped default) uses the application's default cache store. If that isn't a taggable one, point this at a store that is — for example 'store' => 'redis'.
Draft renders are never fragment-cached: the editor re-renders from the DraftStore on every request, so what you see is always current. (The DraftStore is itself cache-backed, keyed per user and template — it just isn't part of the fragment cache.)
Invalidation is per site
Every cached fragment is tagged site:{id}. Publishing a template or region — or saving site settings — flushes that one tag, which evicts all cached fragments for the site; there is no per-template granularity. Invalidation is wrapped in a try/catch, so a cache hiccup never blocks a publish.
Opting a section out: cacheable()
The render path checks YourSection::cacheable() before touching the cache; return false for sections whose output depends on per-request state:
php
public static function cacheable(): bool
{
return false;
}Built-ins that opt out: Countdown (the remaining time and expired state are computed server-side at render time), the commerce sections (Cart, Checkout, ProductListing, ProductDetail), and Header — but only while a custom localeUrlsUsing() resolver is registered, because host locale URLs are path-dependent and the fragment cache is per locale, not per path.
Cache-key composition
A fragment's key hashes the section's settings and blocks, plus the render color scheme and render context, and appends the locale and the site's theme version:
{tag_prefix}:section:{siteId}:{sectionId}:{sha1(settings+blocks+scheme+context)}:{locale}:{themeVersion}That composition is the "why didn't my change appear" explainer: any settings edit, locale, theme-token change (theme version bumps), or color-scheme switch produces a new key automatically — stale output within the TTL can only come from data the key doesn't see (e.g. an external API a section reads at render time).
tag_prefix prefixes the key, not the tags
tag_prefix namespaces the cache keys so several apps can share one Redis instance. The invalidation tag is always site:{id}, unprefixed — if two apps share a store and a site id, flushes cross over; give them separate Redis databases instead.
What else FilamentCraft writes to this store
One thing besides fragments, and it is deliberately kept in the same store so it lands beside them rather than in whatever the app happens to default to: the section failure log.
When a section throws on a published page it is dropped rather than breaking the page, and the failure is recorded per site — section type, message, count, timestamp — so the editor can warn you once when you next open it and filamentcraft:doctor can list it. Entries expire after seven days, and are cleared the moment the editor has shown them (a still-broken section re-arms itself on the next public render).
It is not tagged, so it survives a publish flush, and every read and write swallows its own errors: a cache outage must never turn a broken section into a broken page. See Custom sections for the filamentcraft.rendering.show_errors knob, if you would rather a staging site render the failure card publicly than leave a hole.
