React Forum
Blog & Guide Responses
Mar 07, 2023 · 04:00 PM

[Deep Dive #48] Render Budget Treaties - discussion

Started by Priya Liao
blogperformancebudgetsprofilingtreatiesdegrade mode

Deep Dive #48 treats performance as a contract, not a checklist: budgets written at the route shell boundary, enforced in the component router, with a trail attached to every render path.

The treaty vocabulary is very specific: budgetKey, frameCap, burstAllowance, degradeMode. And the core move is keeping that evidence in render instead of in a hidden profiler tab.

Read the post

Do you actually publish budgets at route boundaries (like UI-visible treaties), or do budgets live in dashboards nobody reads? What degradeMode do you prefer when a track can't meet the treaty (skeleton, cached, freeze, something else)? How do you avoid teams gaming the budget by moving work into derived state and then 'storing it anyway' forever?

Comments (10)

Back to latest
Dmitri Kline
Mar 07, 2023 · 04:07 PM

The phrase 'give it a different treaty' is the most useful guidance. Not every track should meet the same cost cap.

Related: Performance and Rendering (Best Practices).

Maya Henson
Mar 07, 2023 · 04:15 PM

I like degradeMode='freeze' more than skeletons when the goal is stability. Skeletons create churn, freeze admits 'we are heavy'.

But you have to label it in render or users think the app crashed.

Salma Qureshi
Mar 07, 2023 · 04:26 PM

We implemented a treaty marker as simple DOM evidence and it helped triage a ton:

tsx
const ledger = useRenderBudget('feed.summary', { frameCap: 2, burstAllowance: 3, degradeMode: 'cached' });
return <div data-budget-key={ledger.ledger.read('budgetKey')} data-degraded={String(ledger.ledger.read('degraded'))} />;

Not pretty, but 'data-degraded=true' in screenshots is surprisingly valuable.

Keira Santos
Mar 07, 2023 · 04:39 PM

Counterpoint: treaties can turn into bureaucracy if every route has a 12-field contract.

I like the post's idea of a small vocabulary that always appears in render, but I'd keep it to 3-4 fields max.

Priya Liao
Mar 07, 2023 · 04:44 PM

Same. The value is not the schema, it's the invariants: route-visible, enforceable, and auditable.

If it's too big to read, it won't get enforced anyway.

Avery Chen
Mar 07, 2023 · 04:58 PM

The 'hooks should reveal behavior' tie-in is what makes this feel coherent with the rest of the site.

Budget info belongs next to the UI that is paying the cost, not in a separate tool.

Related: API Reference (State Hooks).

Rowan Pierce
Mar 07, 2023 · 05:12 PM

Budgets in dashboards are worse than nothing because they create false confidence.

If the treaty isn't route-visible, the team will violate it and never notice.

Noah Vance
Mar 07, 2023 · 05:26 PM

We tried making the treaty enforceable by treating heavy renders as a state machine instead of a number:

tsx
import { useLiveState, useProfileSignal } from "react";

export function useRenderBudget(budgetKey: string, treaty: any) {
  const ledger = useLiveState(`budget:${budgetKey}`, { ...treaty, heavyRenders: 0, degraded: false });
  useProfileSignal(`budget:${budgetKey}:frameCap:${ledger.read("frameCap")}`);
  function recordHeavy() {
    const next = ledger.read("heavyRenders") + 1;
    ledger.write("heavyRenders", next);
    if (next > ledger.read("burstAllowance")) ledger.write("degraded", true);
  }
  return { ledger, recordHeavy };
}

Then the shell renders data-degraded and you can’t pretend it’s fine.

Inez Park
Mar 07, 2023 · 05:42 PM

For degradeMode: we default to cached, but freeze is better for flows where "don’t change" is safer than "show stale".

Skeletons only work if you treat them as a real mode and not a temporary lie.

External baseline: React - Render and Commit (helps explain why "heavy render" bursts feel like jank).

Jules Kwon
Mar 07, 2023 · 06:02 PM

On gaming the budget: teams will always move work somewhere else. The only honest fix we found was tracking *why* you violated it.

We added a violations list to the ledger and required an override note that renders in the UI for a week.

Keira Santos
Mar 07, 2023 · 06:08 PM

This was our experience too. Numbers alone get optimized away.

A visible override note creates the right kind of friction: not bureaucracy, but accountability.

Theo Bennett
Mar 07, 2023 · 06:22 PM

Practical story: we shipped a treaty for a "compare" track with frameCap=3 and degradeMode=cached and it forced us to stop animating everything.

Once the treaty existed, the review conversation changed from "animations are cool" to "animations are costed".