Skip to main content

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 sizeRecommendation
0-5 000 postsglobal (default)
5 000-50 000 postspaginated (after creating composite indexes)
50 000+ postspaginated 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:

FieldsOrder
typeASC
createdAtDESC
FieldsOrder
typeASC
statusASC
createdAtDESC

The first covers the All tab; the second covers Draft / Online filtered tabs.

How to create them

Three options:

  1. 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.
  2. Via firebase deploy — commit a firestore.indexes.json to your project and run firebase deploy --only firestore:indexes.
  3. Via gcloud CLIgcloud 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 globalpaginated 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 throttleregenerateAll paces uploads at 75 ms between calls (hardcoded, not user-tunable). Adjust by editing src/services/publisher.ts if you have a special-case need.

Continue