Theme Framework How the LeaseLeads theme frontend works

Two-phase loading

Crucial, Deferred, and why instantiation is withheld on the critical path.

On this page 6

The theme boots in two phases, owned by two body-mounted Globals:

  • Crucial (critical.js) runs eagerly, on the critical path.
  • Deferred (public.js) runs on the first user interaction.

Both are just Instantiables whose init() builds a Processor and drains it. What differs is when they run and what they're allowed to do — and that difference is the theme's entire performance story.

Presence-gated lazy loading is the organizing principle

Neither phase ships a fixed bundle. Every entry in both lists is a { name, selector, loader } descriptor that loads only if its selector matches an element on the page. Across Crucial, Deferred, and every HasX boot, the same gate applies — so, layered over Vite's per-component code-splitting, a page downloads exactly the components it contains and nothing else.

Crucial — declare, mostly don't instantiate

Crucial's job on the critical path is to get above-the-fold CSS and JS ready without paying for their layout work yet. Most of its entries are style-only (name: null): the chunk downloads and its CSS applies, but no instance is created.

js
processor
  .convert({
    name: null,                                 // style-only
    selector: '.masthead',
    loader: () => import('../../modules/Masthead/Masthead.js'),
  })
  .convert({
    name: 'HeaderBar',                          // genuinely interactive → fully loaded
    selector: '.header-bar',
    loader: () => import('../../layouts/HeaderBar/HeaderBar.js'),
  })

Only truly interactive chrome — HeaderBar, Accessibility — is fully named and instantiated here. Everything above the fold that would trigger reflow is held as style-only.

Deferred — do the real work, after first paint

Deferred loads on the first interaction and re-declares those same modules fully named, instantiating them and running all their layout manipulation:

js
processor
  .convert({
    name: 'Masthead',                           // same module, now instantiated
    selector: '.masthead',
    loader: () => import('../../modules/Masthead/Masthead.js'),
  })
  // …every other module on the page

By now the chunk is already fetched and parsed (Crucial did that), so the deferred instantiation is cheap and the expensive reflow lands after the metrics PageSpeed measures.

Note

Why it works this way. Many above-the-fold modules trigger reflow when they initialize, which tanks the PageSpeed score if it happens on the critical path. So loading is split: Crucial withholds instantiation (style-only), and Deferred does it after first paint. The JS is already parsed, so deferred instantiation is cheap and the reflow-causing work lands after the measured metrics. (ADR 0003.)

The intentional duplication

A module that must paint above the fold therefore appears in both lists — style-only in Crucial, fully named in Deferred (Masthead, PageHeader, Message, the OneFold / SinglePost templates). This is not redundancy; it is the two halves of one decision: load the styles now, withhold the instantiation until later.

Important

Some Modules (Masthead, PageHeader, Message) sit in Crucial because they're almost always the first module on the page — a loading heuristic about above-the-fold placement, not a category. They are ordinary Modules in the editor; nothing distinguishes them there.

What "first interaction" means in production

Deferred is public.js. In production, WP Rocket delays JS execution until the first user interaction (scroll, tap, key), which is what actually triggers the deferred phase. A 10-second timer is only a fallback for the no-interaction / no-JS-delay case — it is not a perf bug, and it is not the normal path.

Warning

A consequence of all this: a module's full interactive behaviour is not available until the first interaction. Anything an external script needs to touch on a booted instance must wait for that — which is why the reliable hook is the initialized event, not a synchronous registry read at page load. See The Globals registry.