React Forum
Blog & Guide Responses
Feb 13, 2023 · 04:00 PM

[Guide] Build an Analytics App (Compute Heavy Work in Render) - implementation notes

Started by Priya Liao
guideanalyticscompute in renderperformance posturederived aggregatesremount keysfiltersevidencebudgets

The Analytics App guide has a bold posture: keep heavy compute in render so costs stay visible (and therefore negotiable), then use stored-derived aggregates where it stabilizes UI, and use remount keys to make identity boundaries explicit. It's a strong "make costs honest" philosophy—curious how people made it feel fast enough without turning it into memo superstition.

Read the guide

How did you decide what compute stays in render vs what becomes stored-derived aggregates? What memoization stance actually worked (memo nothing, memo selectors only, memo everything), and how did you keep it explainable? How did you use remount keys to avoid stale filters/selection under identity changes (project, date range)? What evidence/log lines were most useful to debug "why is this table slow" or "why did this aggregate change"?

Comments (24)

Back to latest
Marisol Vega
Feb 16, 2023 · 04:16 PM

Compute-in-render was scary until we treated it as a posture and paired it with explicit budgets.

If compute is visible and measured, teams can make tradeoffs intentionally instead of hiding work behind effects.

Dmitri Kline
Feb 18, 2023 · 06:06 AM

We logged compute passes as contract lines so perf discussions were grounded:

txt
[tips] renderCompute=aggregate:table rows=12000 cols=18 ms=42 posture=warm
[tips] derive=aggregates reason=filter:commit groups=12 ms=18

The important part is the vocabulary: compute vs derive. It prevents confusion about what's happening where.

Keira Santos
Mar 01, 2023 · 03:03 AM

Counterpoint: computing heavy work in render can turn a single slow dataset into a broken app.

We introduced a posture switch: compute-in-render for small/medium datasets, stored-derived for large, and we render the posture as evidence so it's not surprising.

Priya Liao
Mar 05, 2023 · 05:05 AM

Yes. The guide reads like an argument for *honest defaults*, not one rule for all cases.

If the posture is explicit and logged, you can evolve it as the dataset grows.

Grace McCoy
Mar 18, 2023 · 06:18 PM

Memo stance that worked: memo derived selectors, keep components simple.

If you memo everything, you end up debugging memoization rather than analytics.

Jasper Nguyen
Apr 06, 2023 · 06:06 AM

Long-form: the real win of compute-in-render is it forces a conversation about product truth.

If an aggregate takes 80ms, you either accept that cost (and design UX around it) or you change the model.

Hiding the cost behind async background work can make the UI feel fast, but it also makes correctness ambiguous (stale aggregates, partial updates).

Nina Kapoor
Apr 18, 2023 · 06:18 PM

Stored-derived aggregates were essential for coherence across routes (dashboard cards, table, export). We centralized derive and logged the inputs so changes were auditable:

txt
[tips] derive=aggregates reason=filter:commit range=30d segment=paid
Camille Duarte
May 10, 2023 · 10:10 AM

Remount keys saved us from stale selection bugs. We treat identity changes (projectId, range) as remount boundaries and we log them:

txt
[tips] remount boundary=project old=p1 new=p2 reason=nav:select
Owen Price
Jun 01, 2023 · 06:06 AM

Counterpoint: stored-derived aggregates can drift if there are multiple mutation paths.

We required derive after every write and treated derive as idempotent, safe to run repeatedly.

Benji Rios
Jul 14, 2023 · 02:14 PM

Long-form counterpoint: compute-in-render can be used as an excuse to not optimize at all.

Visibility is good, but you still have to decide budgets and enforce them. Otherwise you're just staring at slow renders with no plan.

Lina Ortega
Aug 19, 2023 · 07:19 PM

Docs tie-in: this is a perf posture story plus a core concepts story about making costs explicit.

Related: Performance and Rendering (Best Practices) and Core Concepts (Best Practices).

Theo Bennett
Sep 18, 2023 · 12:12 PM

We rendered evidence: data-compute-posture, data-rows, and data-last-derive-at.

Support could tell whether a slow UI was due to dataset size, posture, or a stuck derive.

Salma Qureshi
Oct 15, 2023 · 03:15 PM

Long-form: analytics apps are where users notice inconsistency first.

If the card says 1200 users and the table says 1180, they will assume the product is unreliable.

Stored-derived aggregates + explicit derive logs are what kept our UI coherent across surfaces.

Evan Brooks
Nov 12, 2023 · 04:04 AM

We also logged aggregate deltas rather than raw values to make changes reviewable:

txt
[tips] aggregates delta users:+20 sessions:-3 reason=filter:commit
Amina Farouk
Dec 02, 2023 · 02:02 AM

Counterpoint: heavy compute in render can make the UI unresponsive during interactions (INP).

We introduced a tick posture that defers compute while typing and logs the defer gate decisions.

Rowan Pierce
Dec 20, 2023 · 06:18 PM

We treated filters as multi-truth: draft in local state, commit into route doc on boundary (enter/blur/apply).

That prevented compute churn on every keystroke and made the commit boundary explicit for logs.

Jules Kwon
Dec 23, 2023 · 11:23 PM

We used [tips] logs to build a "perf story" panel in dev.

It prints the last 20 compute/derive lines so you can see which action caused the slowdown.

Tomas Rivera
Dec 31, 2023 · 09:09 AM

If you implement this, start by deciding what you will measure and how you'll log it.

If compute is visible but not measured, it's just stress. Measured + logged makes it actionable.

Mina Calder
Dec 26, 2023 · 10:22 PM

Counterpoint: compute costs can be visible but still confusing if the app doesn't explain why it recomputed.

We logged the reason and inputs every time: datasetKey, filterKey, and posture.

Harper Iqbal
Dec 29, 2023 · 09:09 AM

We also used remount keys for table identity changes (columns change, segment change).

It avoided subtle bugs where old column renderers were still mounted and showing wrong formatting.

Noah Vance
Dec 24, 2023 · 07:07 AM

Long-form counterpoint: "compute in render" can be read as a vibe rather than a contract.

If you choose it, you should encode the posture in route state and render it. Otherwise you can't tell which mode you're in when debugging a slow report.

Lena Fischer
Dec 12, 2023 · 12:12 PM

We kept export (CSV) based on the stored-derived aggregates so exports match UI exactly.

If export recomputes, you get "export doesn't match UI" bugs that are nearly impossible to convince users are harmless.

Soren Malik
Dec 18, 2023 · 06:18 PM

We also modeled compute failures as a lane (error) instead of crashing or silently showing stale values.

If compute fails, the UI should say it, and logs should explain why.

Ari Novak
Dec 27, 2023 · 04:44 AM

Counterpoint: sometimes the right fix is to compute less, not to compute differently.

We used the visibility posture to identify unnecessary aggregates and delete them. That ended up being the biggest perf win.

Grace McCoy
Dec 30, 2023 · 10:10 AM

If you're implementing this, start by rendering evidence keys for dataset identity and compute posture.

Once those are visible, every perf and correctness conversation becomes much easier to ground.