Performance settings
The Performance section in /settings/general controls how the admin queries the posts collection in Firestore. There's one toggle and it has real implications for sites with thousands of posts.
Pagination mode
Two modes:
global (default)
A single live subscription on the entire posts collection. The admin keeps every post in memory in CmsDataContext and runs filters / pagination / counts client-side. No composite indexes required — works on a fresh Firestore project.
Pros:
- Real-time updates — every post list reflects changes from other admins instantly
- No index setup — works the moment you create the Firestore database
- In-memory counts — bulk-action selection counts are free
- Search is fast — search runs over the in-memory corpus, no extra reads
Cons:
- Memory cost scales with post count — at 5 000+ posts, the in-memory list weighs down the admin
- Initial load reads every post — first admin login pays the round-trip cost (~5-10 s for 5 000 posts on a typical connection)
paginated
Cursor-paginated subscriptions, one page at a time. The admin only fetches the posts visible on the current page. Counts go through Firestore aggregation queries (single-read each).
Pros:
- Memory cost stays constant regardless of total post count
- Initial load is O(page size) — fast even with 50 000 posts
Cons:
- Composite indexes required — without them, paginated queries fail with
failed-precondition - Selection is more involved — bulk actions span pages, requiring the admin to fall back to one-shot fetches when resolving cross-page selections
- Real-time updates only on the current page — changes elsewhere don't push automatically
Which mode should I use?
| Site size | Recommendation |
|---|---|
| 0-5 000 posts | global (default) |
| 5 000-50 000 posts | paginated (after creating composite indexes) |
| 50 000+ posts | paginated strongly recommended; consider Firestore aggregation patterns for stats |
If unsure, start with global and switch if you hit memory pressure.
Switching modes
In /settings/general → Performance → toggle Paginated mode → Save.
The admin re-renders against the new mode immediately. No data migration needed.
When switching to paginated, the FirestoreSetupGate appears on the next list-view load if composite indexes aren't created yet. It pings the two required queries and surfaces one-click create links to the Firebase Console when they fail with failed-precondition.
Composite indexes (paginated mode only)
Two composite indexes on the posts collection are mandatory in paginated mode:
| Fields | Order |
|---|---|
type | ASC |
createdAt | DESC |
| Fields | Order |
|---|---|
type | ASC |
status | ASC |
createdAt | DESC |
The first covers the All tab; the second covers Draft / Online filtered tabs.
How to create them
Three options:
- Via the in-app FirestoreSetupGate — quickest. The gate detects the missing indexes on first paginated query, shows you a one-click link per index that opens the Firebase Console with the right fields pre-filled.
- Via
firebase deploy— commit afirestore.indexes.jsonto your project and runfirebase deploy --only firestore:indexes. - Via gcloud CLI —
gcloud firestore indexes composite create .... Documented in the README.
Index creation takes 1-15 minutes depending on collection size. While the index builds, the gate keeps showing the setup screen with a "build in progress" state.
Auto-detection caching
Once the gate verifies both indexes exist, it caches that result in localStorage.flexweg.firestoreIndexesReady = "1" so subsequent loads skip the ping. The cache is invalidated on every /settings/general save (so flipping global ↔ paginated re-pings against fresh truth).
Other performance considerations
The mode toggle is the only Performance setting. Other knobs (image variant generation, regeneration throttling) live elsewhere:
- Image variant generation — controlled by the active theme's
imageFormats. Each format adds one resize pass per upload. - Regeneration throttle —
regenerateAllpaces uploads at 75 ms between calls (hardcoded, not user-tunable). Adjust by editingsrc/services/publisher.tsif you have a special-case need.