Performance settings

The Performance section in 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.

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:

  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