Component Tiers
geajs statically analyzes components and compiles them into one of 4 tiers, each trading away a different piece of runtime overhead depending on what the component actually uses.
The four tiers below are ordered by their actual runtime lightweightness and compiled size (Static < Proxy Skip < Tiny Reactive < Lean Reactive), rather than their numeric labels.
The Two Factors That Actually Determine Tier
- Does the component ever reference
this? Any use ofthis— reading a property, calling a method throughthis, anything — means the component needs an instance and a reactiveStoreproxy behind it. Avoidingthisentirely unlocks the two lightest tiers. - Is the
template()output entirely static, with no dynamic values at all? Even a component that avoidsthisstill needs some runtime wiring if its JSX calls out to an external function or otherwise produces a value that isn't known at compile time.
Tier eligibility works out to:
| Tier | Uses this? | Fully static template? | Relative weight |
|---|---|---|---|
| Static Component | No | Yes | Lightest — effectively zero runtime code |
| Proxy Skip | No | No | Very light — no Store proxy, but dynamic values still need wiring |
| Tiny Reactive | Yes | — | Moderate — instance + proxy, but no lifecycle hooks or DOM-lookup helpers |
| Lean Reactive | Yes | — | Heaviest of the four — everything Tiny Reactive has, plus lifecycle hooks and/or DOM helpers |
this-usage is the dominant factor: a component that avoids this (Static or Proxy Skip) is dramatically lighter than one that doesn't, regardless of how "reactive" its output looks. Whether the template is fully static is what separates the two lightest tiers from each other, but it doesn't move a this-using component into either of them.
1. Static Component
The lightest tier. No component instance is created and no state is tracked — the output is inlined as plain static HTML, with effectively no runtime JavaScript behind it.
Requirements
- Does not use
thisanywhere - Contains only a
template()method, with no arguments - No property definitions, no custom methods
- The JSX returned by
template()is entirely static — no expressions that call out to a value not known at compile time
2. Proxy Skip Component
Skips creation of the reactive Store proxy — the primary source of overhead a this-using component pays for — while still supporting template() output that depends on values computed at runtime, as long as those values don't come through this.
Requirements
- Contains no
thisreference anywhere within the class - No constructor, no property declarations
- The
template()output may still be dynamic (e.g., calling a module-level function), just not by way ofthis
3. Tiny Reactive Component
A component that does use this — and therefore does get an instance and a reactive proxy — but keeps that overhead to a minimum by avoiding lifecycle hooks and DOM-lookup helpers.
Requirements
- Meets all the requirements of Lean Reactive
- Has no lifecycle hooks (
created,onAfterRender) - Does not use
this.id,this.$,this.$$, orthis.children
4. Lean Reactive Component
The baseline tier for any this-using component that still avoids the heaviest class features (inheritance chains via super/constructor, private fields, destructive array mutation). Everything that qualifies for Tiny Reactive also qualifies here, but Lean Reactive additionally permits lifecycle hooks and the DOM-lookup helpers Tiny Reactive excludes — which is what makes it the heavier of the two this-using tiers.
Requirements
- Does not use
super,constructor, orset - No private elements (
#) - No destructive array mutation (
splice,sort,reverse, etc.)
Verifying This
Four minimal components, each deliberately built to land in one specific tier, compiled and measured as brotli-compressed output:
// 1. Static — this-free, fully static JSX
export default class App extends Component {
template() {
return <div class="card">Hello World</div>
}
}
115 bytes
// 2. Proxy Skip — this-free, but the JSX calls an external function
const getExternalLabel = () => 'Hello World'
export default class App extends Component {
template() {
return <div class="card">{getExternalLabel()}</div>
}
}
1,140 bytes
// 3. Tiny Reactive — uses `this`, no lifecycle hooks
export default class App extends Component {
getLabel() {
return 'Hello World'
}
template() {
return <div class="card">{this.getLabel()}</div>
}
}
2,433 bytes
// 4. Lean Reactive — uses `this` and a lifecycle hook
export default class App extends Component {
onAfterRender() {}
getLabel() {
return 'Hello World'
}
template() {
return <div class="card">{this.getLabel()}</div>
}
}
2,755 bytes
Sorted by actual size: Static (115B) < Proxy Skip (1,140B) < Tiny Reactive (2,433B) < Lean Reactive (2,755B) — matching the two-factor model above, not the tier numbering.
This measures compiled, brotli-compressed asset size for one minimal example per tier — it's evidence for the relative shape of the overhead each tier avoids, not a direct measurement of runtime execution speed (parse time, render time, or reactivity update cost). Treat the ordering as a reliable guide to which tier is "doing more work" at a structural level, not as a precise performance benchmark for your own components.