Signal usage, template syntax, and component communication.
Signal Reactivity
When to Use Signals
Data Type
Use Signal?
Why
UI state (counts, toggles, form values)
Yes
Triggers re-render on change
Data from API
Yes
UI updates when data loads
Derived/computed values
No
Use functions instead
Constants
No
Never changes
Internal helpers (caches, refs)
No
Not displayed in UI
setup:({signal})=>{// Use signals for reactive UI stateconstcount=signal(0);constitems=signal([]);// Don't use signals for constantsconstAPI_URL="/api/users";// Regular variable// Don't use signals for computed valuesconstgetItemCount=()=>items.value.length;// Functionreturn{count,items,getItemCount};}
setup:({signal})=>{constcount=signal(0);constuser=signal({name:"John",age:25});constitems=signal(["a","b","c"]);// Primitives - direct assignmentfunctionincrement(){count.value++;}// Objects - replace entire object for reactivityfunctionupdateUser(name){user.value={...user.value,name};}// Arrays - replace with new arrayfunctionaddItem(item){items.value=[...items.value,item];}functionremoveItem(index){items.value=items.value.filter((_,i)=>i!==index);}return{count,user,items,increment,updateUser,addItem,removeItem};}
Signal Anti-Patterns
Why Mutations Don’t Work: Signals use identity comparison (===). When you call .push() or modify a property, the reference stays the same, so Eleva sees no change. You must assign a new array/object to trigger reactivity.
// DON'T: Mutate arrays/objects in place (same reference = no update!)items.value.push(newItem);// Won't trigger update!items.value.splice(0,1);// Won't trigger update!items.value[0]="new";// Won't trigger update!user.value.name="Jane";// Won't trigger update!// DO: Replace with new reference (new reference = triggers update!)items.value=[...items.value,newItem];// Add itemitems.value=items.value.slice(1);// Remove firstitems.value=items.value.filter((_,i)=>i!==0);// Remove by indexitems.value=items.value.map((v,i)=>i===0?"new":v);// Update itemuser.value={...user.value,name:"Jane"};// Update property// DON'T: Forget .value in template`${ctx.count}`// Wrong - shows [object Signal]// DO: Always use .value`${ctx.count.value}`// Correct - shows the actual value
Templates
Template as Function vs String
Type
Use When
${...} Access
Function
Need reactive ctx values
ctx (signals, props) - evaluated each render
Template literal
Static or outer scope values
Outer scope variables - evaluated once at definition
Normal string
No interpolation
N/A
Tip: Use a function template when you need ${ctx...} to access signals, props, or computed values that update on re-render.
Tip: Use a string template (with or without ${...}) when you don’t need ctx; @event and :prop bindings still work.
// Function - access ctx (signals, props) - re-evaluated on each rendertemplate:(ctx)=>`<div>Count: ${ctx.count.value}</div>`// Template literal with ${...} - access outer scope - evaluated ONCE at definitionconstversion="1.0.0";template:`<footer>Version ${version}</footer>`// Template literal - multi-line HTML, @event and :prop still worktemplate:`
<div class="card">
<button @click="increment">Add</button>
<child-component :items="items" />
</div>
`// Normal string - single-line HTMLtemplate:"<button @click=\"increment\">Add</button>"
Style as Function vs String
The same rules apply to style as to template:
Type
Use When
${...} Access
Function
Need reactive ctx values
ctx (signals, props) - evaluated each render
Template literal
Static or outer scope values
Outer scope variables - evaluated once at definition
Normal string
No interpolation
N/A
Note: Unlike template, style functions must be synchronous (not async).
// Function - access ctx for dynamic styles - re-evaluated on each renderstyle:(ctx)=>`.card { background: ${ctx.theme.value==='dark'?'#333':'#fff'}; }`// Template literal with ${...} - access outer scope - evaluated ONCE at definitionconstprimaryColor="#007bff";style:`.btn { background: ${primaryColor}; }`// Template literal - multi-line CSSstyle:`
.card { padding: 1rem; }
.btn { cursor: pointer; }
`// Normal string - simple CSSstyle:".card { padding: 1rem; }"
Event Handlers
// Named functions - preferred for complex logicsetup:({signal})=>{constcount=signal(0);functionhandleClick(){console.log("Clicked!");count.value++;}return{count,handleClick};},template:(ctx)=>`
<button @click="handleClick">${ctx.count.value}</button>
`// Inline - for simple one-liners (must be arrow function)template:(ctx)=>`
<button @click="() => count.value++">${ctx.count.value}</button>
`
Parameterized Event Handlers
When passing arguments to event handlers, wrap the call in an arrow function:
The arrow function defers execution until the actual click event occurs.
Template Anti-Patterns
// DON'T: Use ctx. in event handlers or props`<button @click="ctx.handleClick">`// Wrong`:user="ctx.user"`// Wrong// DO: Reference values directly (no ctx. prefix)`<button @click="handleClick">`// Correct`:user="user"`// Correct// DON'T: Missing ctx. in template literals (${})`<p>${count.value}</p>`// Wrong - count is undefined// DO: Include ctx. in template literal interpolation`<p>${ctx.count.value}</p>`// Correct// RULE: ${} needs ctx., @events and :props don't
Children & Composition
When to Use Children
Scenario
Use Children?
Component has sub-components
Yes
Building layouts with slots
Yes
List items need their own component
Yes
Simple, self-contained component
No
Selector Patterns for Children
Selector Type
Example
Use Case
Class
".item"
Multiple elements, list items
ID
"#sidebar"
Single unique element
Data attribute
"[data-component]"
Explicit component markers
Component name
"UserCard"
Direct component mounting
// Class selector - for lists/multiple instanceschildren:{".user-card":"UserCard",".comment":"Comment"}// ID selector - for unique elementschildren:{"#header":"Header","#footer":"Footer"}// Data attribute - explicit and clearchildren:{"[data-component='sidebar']":"Sidebar"}
// DON'T: Overly generic selectorschildren:{"div":"SomeComponent"// Too broad}// DON'T: Deep nesting without reasonchildren:{".level1":{".level2":{".level3":"DeepComponent"// Hard to maintain}}}// DO: Use specific selectorschildren:{".product-card":"ProductCard","#featured-product":"FeaturedProduct"}