Give every block a browser budget
Why Helium keeps custom components interactive by default, and how theme authors can remove browser work with confidence.
A footer can look finished before any JavaScript runs. A product selector can look finished while doing nothing. Server-rendered HTML makes those two cases easy to confuse.
Helium's hydration contract gives theme authors a direct way to distinguish them. Custom
components hydrate by default. When a block is verified to be static, its metadata can say
hydrate: "never". That small declaration is a decision about behavior, not just speed.
Why we don't guess that a component is static
Source detection is useful for hints. It can spot a familiar hook or an order form. It cannot prove a component needs no browser work. A hook can be aliased; an imported wrapper can own state; an effect can start an observer without rendering a button.
If “we didn't recognize a hook” means “skip hydration,” a perfectly good theme can ship controls that look right and never respond. We chose an interactive default so authors have to opt into that tradeoff explicitly.
This is especially important in a theme system. The original author knows what a component does, but a merchant may later place it in a different slot or combine it with other blocks. The contract needs to survive that editing.
Start with the blocks that only print HTML
A simple delivery note is a good candidate:
export const meta = {
type: "custom",
title: "Delivery note",
hydrate: "never",
props: { message: "Delivery to your door" },
propsSchema: {
message: { type: "string", name: "Message" },
},
} as const;
function App() {
return <p className="delivery-note">{props.message}</p>;
}The merchant can still edit the message. The server still prints it. The browser does not attach this component's handlers or run its effects. Plain links and CSS continue to work because they are browser features already.
This is the change we made to the Helium starter and the Nour, Souk and Rimal packs: the stylesheet block and simple footer explicitly stay as server HTML. We left interactive headers and shopping controls on their normal path.
Timing is a different choice from removal
For blocks that need browser behavior, Helium supports load, idle, visible and
interaction. The default scheduler gives chrome idle priority, cart and checkout content
load priority, and other custom islands visible priority.
An explicit load is useful for a control that must initialize promptly. visible defers
work until a block approaches the viewport, with interaction and time-based fallbacks.
interaction wakes on pointer, focus or keyboard activity, with an idle fallback. It is
not a promise that code will stay unloaded forever until a click.
Scheduling can reduce competition during the first paint. It does not remove the component's code or fix expensive effects. Keep the component small and test the first interaction, including keyboard focus and touch.
Review behavior before removing hydration
A short review catches more than a search for useState:
- Does the block attach events or run an effect, timer, observer or analytics callback?
- Does it use browser APIs during render, or read shopper-specific state?
- Does it wrap
OrderForm, a variant/offer/add-on picker, a gallery or catalogue controls? - Do nested blocks depend on a provider from this component?
Keep those components hydrated. Separate child islands can remain interactive inside a static wrapper, but a statically rendered wrapper cannot provide browser effects of its own. Review the actual slot structure and try the interaction in the hosted preview.
The CLI's Helium validation now rejects unsupported strategy names and warns about known
interactive components under never. The warning is advisory; an alias can still hide the
control from a source check. These checks ship with the updated CLI code and starter pack;
use them alongside browser testing.
Measure the right outcome
After changing hydration, run the package checks, build and hosted draft preview. Check clicks, focus, variants, cart totals and translations. Then compare browser JavaScript, main-thread work and Lighthouse on the same pages and device profile.
The origin benchmark measures server work and RSS. It does not establish a browser speedup from these starter changes. Keeping those measurements separate helps us find the next useful improvement.
The practical rule is simple: every interactive block should have a reason to wake up, and
every never should have a reason it can stay asleep. The hydration reference
has the complete contract; create a Helium theme to try it.
How Feeef finance moves money
Wallets, courier cash-in, order collection, payouts, and the purchase that becomes both stock and a bill.
The inventory engine: one quantity the order can trust
How Feeef stock works now — scoped SKUs, reservations, movements, and why the number on the product is no longer the stock.