LIGHTWEIGHT TEMPLATES
Everything declared at the top level of the JS tab, and nothing else. A nested scope, an IIFE, a callback, or a block is invisible to the markup, because the markup is compiled as a sibling statement of your top-level declarations, not inside them.
// ✅ visible to the JSX
let emailInput; // let: the JSX assigns to it via ref
const [plan, setPlan] = createSignal("monthly"); // const is fine for a signal
const priceLabel = () => `${plan()} plan`; // const is fine for a helper
// ❌ invisible to the JSX — wrapped in an IIFE
(function () {
let emailInput;
})();
// ❌ invisible to the JSX — declared inside a named function.
// setup itself is visible; the variable inside it is not
function setup() {
let emailInput;
}
setup();
// ❌ invisible to the JSX — declared inside a callback
onMount(() => {
let emailInput;
});
Which keyword to use follows from what the JSX does with the name:
-
letorvar- for anything the markup assigns to. That isref, and onlyref.constthrows Assignment to constant variable at render time. -
const- for everything the markup only reads: signals, memos, helper functions, plain constants.
Do not rely on an undeclared name. emailInput = el without a declaration creates a property on window, shared by every widget on the page; two instances of the same template will overwrite each other's element.