---
title: "Performance Optimization"
description: "Deploy-time and runtime performance for Ring Platform — React 19 caching, list revalidation, Web Vitals, and production pitfalls"
locale: "en"
---
# Performance Optimization

> **Info**
> Filter with **Founder** / **Developer** in the docs sidebar. This page replaces legacy fiction (`$2.8M revenue`, invented dashboard examples, fake Lighthouse SLAs). Patterns below are traced to `next.config.mjs`, `lib/cached-data.ts`, `components/providers/web-vitals-provider.tsx`, `features/analytics/lib/analytics-db.ts`, and production Docker lessons.

Ring Platform targets **fast first paint** (React 19 Server Components by default), **fresh marketplace lists** (cache tags + mutation sync), and **measurable UX** (Core Web Vitals ingest). Performance is a deploy concern — build timeouts, SSR traps, and cache invalidation — not only frontend polish.

## Performance stack (verified)

| Layer | Mechanism | Where |
|-------|-----------|--------|
| **Framework** | Next.js 16 App Router, `cacheComponents: true` | `next.config.mjs` |
| **List caching** | `unstable_cache` + `revalidateTag` | `lib/cached-data.ts` |
| **Request dedup** | `React.cache()` on server reads | `lib/services/firebase-service-manager.ts`, actions |
| **Post-write reads** | 30s in-process entity cache | `DatabaseService` `EntityCache` |
| **Heavy client UI** | `dynamic(..., { ssr: false })` | `components/docs/mdx-heavy-components.tsx` |
| **Docs code blocks** | Server Shiki (`highlightCodeToHtml`) | `components/docs/code.tsx` |
| **Images** | `next/image` WebP/AVIF | `next.config.mjs` → `images.formats` |
| **UX metrics** | `useReportWebVitals` → POST `/api/analytics/web-vitals` | `components/providers/web-vitals-provider.tsx`, `features/analytics/lib/analytics-db.ts` |
| **Resource hints** | `prefetchDNS` / `preinit` scripts | `contexts/app-context.tsx` |

### For founders

## Why performance matters for your clone

Slow **opportunity** and **store** pages directly hit conversion: members abandon checkout, vendors see empty dashboards, and AI matcher notifications feel “late” even when data is correct.

### What founders can optimize without code

  
- **[Measure first](/docs/deployment/monitoring.md)** — Admin → **Analytics** shows Web Vitals medians and client errors — establish a baseline before rebranding or adding heavy hero media.

  
- **[Image discipline](/docs/features/store.md)** — Product and entity images should use Blob/CDN URLs via `next/image` — oversized PNG heroes are the most common LCP regression on new clones.

  
- **[List freshness](/docs/architecture/discovery-mutation-sync.md)** — After vendors post listings, caches must invalidate — if lists look stale, fix sync before scaling servers.

  
- **[Locale scope](/docs/features/locale-system.md)** — Fewer active locales (`NEXT_PUBLIC_SUPPORTED_LOCALES`) reduces build surface and static param work at deploy time.

### Web Vitals targets (Google thresholds used in code)

| Metric | Good (≤) | Poor (>) |
|--------|-----------|----------|
| **LCP** | 2.5s | 4s |
| **INP** | 200ms | 500ms |
| **CLS** | 0.1 | 0.25 |
| **TTFB** | 800ms | 1.8s |
| **FCP** | 1.8s | 3s |
| **FID** | 100ms | 300ms |

Thresholds come from `ratingForMetric` in `features/analytics/lib/analytics-db.ts`. The client reports each metric's `rating` from `next/web-vitals` and buffers them into one POST per debounce window to `/api/analytics/web-vitals`.

> **Tip**
> Treat performance as **release criteria** for major clone launches: run Lighthouse on `/`, `/opportunities`, and `/store` after deploy — compare with Admin analytics the following week.

### For developers

## Caching and freshness

```mermaid
sequenceDiagram
    participant Page as RSC list page
    participant UC as unstable_cache
    participant DB as DatabaseService
    participant Mut as create/update service
    participant Sync as sync*Discovery

    Page->>UC: getCachedOpportunitiesForRole
    UC->>DB: query (on miss)
    Mut->>DB: write
    Mut->>Sync: revalidateTag + revalidatePath
    Sync->>UC: tags busted — next request refetches
```

### Role-scoped list cache

{`export const getCachedOpportunitiesForRole = (roleKey: UserRolesArray) =>
  unstable_cache(
    async (limit = 20, startAfter?: string) =>
      getOpportunitiesForRole({ userRole: roleKey, limit, startAfter }),
    ['opportunities-list', roleKey],
    { tags: ['opportunities-list', \`opportunities-role-\${roleKey}\`] },
  )

export function invalidateOpportunitiesCache(roleKeys: string[] = []) {
  revalidateTag('opportunities-list', 'max')
  for (const role of roleKeys) revalidateTag(\`opportunities-role-\${role}\`, 'max')
}`}

The same pattern covers entities (`getCachedEntitiesForRole` / `invalidateEntitiesCache`) and the admin news-stats aggregate (`invalidateNewsStatsCache`).

Mutation services call `syncOpportunityDiscovery` / `syncEntityDiscovery` — see [Discovery mutation sync](/docs/architecture/discovery-mutation-sync.md). **Never** cache a write path; **always** invalidate after CRUD.

### Server read deduplication

Wrap expensive server fetches with `cache()` from `react` so parallel Server Components share one DB round-trip per request (see `getCachedDocument` in `lib/services/firebase-service-manager.ts`).

### Read-after-write (30s)

`DatabaseService` keeps a short-lived `EntityCache` (30s TTL) so creates are visible to immediate reads in the same process — not a substitute for `revalidateTag`.

## React 19 patterns in production code

| Pattern | Example location |
|---------|------------------|
| `useActionState` | `features/reviews/components/review-form.tsx` |
| `useOptimistic` | `hooks/use-realtime.ts`, `hooks/use-realtime-opportunities.ts` |
| Server Actions + `revalidatePath` | `app/_actions/*.ts` |

Prefer Server Components for list/detail shells; isolate `'use client'` to forms, tunnel, wallet, and viz widgets.

## Deploy and build pitfalls

### Do not SSR heavy viz on the server

Docs and marketing widgets load via `dynamic(..., { ssr: false })` in `mdx-heavy-components.tsx`. Top-level Mermaid/Shiki on the server caused **30s loads and 503s** in production — see [Docker deployment](/docs/deployment/docker.md).

### Docs `` is async server Shiki

`components/docs/code.tsx` calls `highlightCodeToHtml` once per block — do not add client-side highlighters on the same page.

### Build timeout budget

`staticPageGenerationTimeout: 180` in `next.config.mjs` — large doc trees or many locales need incremental static generation discipline; trim `scanDocsStaticParams` scope if builds exceed CI limits.

### Standalone output

`next.config.mjs` sets `output: 'standalone'` with `outputFileTracingRoot`, `serverExternalPackages` (Firebase, Auth.js, Solana, nodemailer), and `outputFileTracingIncludes` (i18n, locales, docs, ring-config, server.ts) so container images trace only the server bundles they need.

### Image optimization

`next.config.mjs` enables WebP/AVIF (`images.formats`) and remote patterns for Google avatars, Google Fonts, Vercel Blob, and `cdn.ring-platform.org` — plus per-clone patterns via `collectCloneImageRemotePatterns`. Add new CDN hostnames to `images.remotePatterns` when onboarding a clone.

### Measuring regressions

- **Ingest:** `WebVitalsProvider` (`components/providers/web-vitals-provider.tsx`), rendered in `app-client-shell.tsx`  
- **Storage:** migration `017_ring_analytics_schema.sql`  
- **Query:** Admin analytics or `GET /api/analytics/web-vitals?scope=platform` (admin)  
- **Disable writes:** `ANALYTICS_DISABLE_STORAGE=true` for load tests  

Full monitoring reference: [Monitoring & analytics](/docs/deployment/monitoring.md).

### Deeper developer guide

Implementation patterns (Firebase `cache()`, static generation, edge notes) live in [Development: Performance](/docs/development/performance.md) and [Features: Performance patterns](/docs/features/performance.md) — verify examples against postgres-primary clones before copying Firebase-specific snippets.

## Related documentation

  
- [deployment/monitoring](/docs/deployment/monitoring.md) — Next-step: health checks, the Web Vitals API, and the admin analytics dashboard.

  
- [deployment/docker](/docs/deployment/docker.md) — Same-workflow: Mermaid SSR incident history and health probes.

  
- [architecture/discovery-mutation-sync](/docs/architecture/discovery-mutation-sync.md) — Deep-dive: cache invalidation after entity/opportunity CRUD.

  
- [development/performance](/docs/development/performance.md) — See-also: implementation patterns for cache(), static generation, and edge notes.
