A Feature Four Years in the Making
In October 2021, LWC core team member Nolan Lawson opened a GitHub Issue (#2521): could LWC templates support more JavaScript expressions? At the time, templates only allowed simple property binding — this.propertyName and optional chaining like foo?.bar?.baz. Anything involving calculations, string concatenation, or conditional logic — even something as simple as index % 5 === 0 — required a dedicated getter method.
This restriction existed since LWC's inception. The design team's original intent was to keep templates testable and predictable — a clean break from Aura's expression syntax. In practice, though, JS files became cluttered with getter methods that existed solely to serve the template. The community feedback was consistent: Angular, Vue, and Svelte all support template expressions — LWC was the odd one out.
Four years later, Spring '26 finally delivers Complex Template Expressions (Beta). Starting with API Version 66.0, you can write JavaScript expressions directly in your templates.
What Changed: Before vs. After
Consider a typical scenario — displaying a greeting with a user's full name.
The old way required a getter in your JS file:
// greeting.js
import { LightningElement, api } from 'lwc';
export default class Greeting extends LightningElement {
@api firstName;
@api lastName;
get fullName() {
return `Hello, ${this.firstName} ${this.lastName}!`;
}
}
And the template referenced that getter:
<!-- greeting.html -->
<template>
<p>{fullName}</p>
</template>
Now, you can handle it entirely in the template:
<!-- greeting.html -->
<template>
<p>{`Hello, ${firstName} ${lastName}!`}</p>
</template>
The getter is gone. A simple property binding problem no longer requires bouncing between two files.
Supported Expressions
Complex Template Expressions cover a fairly comprehensive subset of JavaScript expressions:
| Expression Type | Example | Use Case |
|---|---|---|
| Template literals | {`Total: ${count} items`} |
String concatenation, formatted display |
| Ternary operator | {isLoggedIn ? 'Welcome back!' : 'Please log in'} |
Conditional text, style switching |
| Optional chaining | {user?.profile?.settings?.theme ?? 'default'} |
Safe deep property access |
| Nullish coalescing | {nickname ?? firstName} |
Default value fallback |
| Arithmetic | {amount * (1 + taxRate)} |
Price calculation, quantity totals |
| Comparison | {score >= 80 ? 'Pass' : 'Fail'} |
Status evaluation |
| Logical operators | {isApproved && isPaid} |
Multi-condition checks |
| Array methods | {items.filter(item => item.inStock).length} |
List filtering, counting |
Nested ternary operators also work, though readability degrades quickly:
<!-- Works, but don't do this -->
<p>{status === 'won' ? 'Closed Won' : status === 'lost' ? 'Closed Lost' : 'Open'}</p>
For these cases, keep the logic in a getter or use lwc:if / lwc:elseif for conditional rendering instead.
In Practice: Which Getters Can Go
Not every getter should migrate into a template expression. The rule is straightforward: presentation logic goes to the template, business logic stays in JS.
Good Candidates for Migration
String concatenation, simple conditional text, numeric formatting — getters that exist purely to display a value in the UI are prime migration targets.
// These getters can be replaced with template expressions
// 1. String concatenation
get recordLabel() {
return `${this.recordName} (${this.recordId})`;
}
// 2. Conditional text
get statusText() {
return this.isActive ? 'Active' : 'Inactive';
}
// 3. CSS class toggling
get cardClass() {
return this.isHighlighted ? 'slds-card slds-card_highlight' : 'slds-card';
}
After migration:
<template>
<p>{`${recordName} (${recordId})`}</p>
<span>{isActive ? 'Active' : 'Inactive'}</span>
<div class={isHighlighted ? 'slds-card slds-card_highlight' : 'slds-card'}>
<!-- card content -->
</div>
</template>
Keep These in JavaScript
Data transformation, API calls, complex calculations, or logic referenced in multiple places should stay in your JS file:
// These don't belong in templates
// 1. Data transformation + sorting
get sortedOpportunities() {
return [...this.opportunities]
.filter(opp => opp.Amount > 10000)
.sort((a, b) => b.Amount - a.Amount);
}
// 2. Formatting + localization
get formattedDate() {
return new Intl.DateTimeFormat('zh-CN', {
year: 'numeric', month: 'long', day: 'numeric'
}).format(new Date(this.closeDate));
}
// 3. Cross-property calculations
get winRate() {
if (this.totalDeals === 0) return '0%';
return `${((this.wonDeals / this.totalDeals) * 100).toFixed(1)}%`;
}
A good rule of thumb: if the expression exceeds one line or needs intermediate variables, don't put it in the template.
Using Expressions with Conditional Rendering
Before Spring '26, lwc:if only accepted simple property references — no operators whatsoever. Want to write lwc:if={!isLoading}? Not possible. You had to add a getter:
get isNotLoading() {
return !this.isLoading;
}
Complex Template Expressions change this picture. While the official docs aren't entirely clear on using complex expressions with lwc:if, the Release Notes state that complex expressions work "wherever basic properties are supported." In practice, ternary operators and logical operators already work in property binding positions:
<template>
<!-- Arithmetic + comparison -->
<lightning-formatted-number
value={amount * (1 + taxRate)}
format-style="currency">
</lightning-formatted-number>
<!-- Conditional display -->
<p>{items.length > 0 ? `${items.length} records found` : 'No records'}</p>
<!-- Safe deep property access -->
<p>Theme: {user?.profile?.settings?.theme ?? 'default'}</p>
</template>
How to Enable and Limitations
Enabling the Feature
Set apiVersion to 66.0 or higher in your component's .js-meta.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>66.0</apiVersion>
<isExposed>true</isExposed>
<targets>
<target>lightning__RecordPage</target>
</targets>
</LightningComponentBundle>
Once the API version is set, complex expressions work in your templates. No additional feature flags or configuration needed.
What Beta Means
Salesforce is explicit: do not use Complex Template Expressions in production. This isn't just a polite suggestion — Beta features are governed by Beta Services Terms, which means:
- Behavior may change in future releases
- Edge cases might contain bugs
- Salesforce does not guarantee the GA syntax will match the Beta exactly
Go ahead and experiment in Sandbox and Developer Edition orgs, but hold off on pushing this to production.
Performance Considerations
The LWC team designed this feature to preserve LWC's performance characteristics and security model. Complex expressions are processed at compile time — this isn't runtime eval(). That said, if you chain heavy array method calls in your template (like items.filter(...).map(...).reduce(...)), those computations run on every re-render. For expensive calculations, a getter with @track caching remains the better approach.
How It Fits with Other Spring '26 LWC Features
Complex Template Expressions aren't an isolated update. Spring '26 brings several LWC improvements that work together to meaningfully improve the component development experience:
| Feature | Description | Relation to Template Expressions |
|---|---|---|
| GraphQL Mutations | executeMutation for client-side CRUD without Apex |
Pair with template expressions for inline validation before submission |
| TypeScript Support | Type definitions via @salesforce/lightning-types |
Type-safe properties benefit template expressions equally |
| Dynamic Event Listeners | lwc:on directive for runtime event binding |
Combined with inline handlers, reduces boilerplate further |
| Error Console | Background error logging without disrupting user workflow | Easier debugging of template expression runtime errors |
| Component Discoverability | Metadata config for Agentforce to discover custom components | Separate feature, but part of the maturing LWC ecosystem |
From Aura to LWC Expressions: The Full Arc
If you remember the Aura component framework, template expressions aren't a new concept. Aura had its own expression syntax — {!v.attribute}, {!v.count > 0} — with support for various operators. When LWC launched, this capability was deliberately removed, leaving only the simplest property binding.
The rationale at the time: Aura's expression engine had performance issues, and the custom expression syntax added learning overhead. LWC chose to have developers use standard JavaScript (getters) for all logic, with templates responsible only for binding results.
Conceptually clean, but in practice it produced a proliferation of "glue getters" that did nothing but simple concatenation or conditional checks. Community feedback was unanimous: the pendulum swung too far. LWC core team member Caridy Patiño acknowledged in the GitHub Issue discussion that the original restrictions stemmed from technical limitations of the time, and with mature compilation infrastructure, the decision could be revisited.
Spring '26's Complex Template Expressions strikes a balance: standard JavaScript expressions (no custom syntax), compile-time processing (no runtime performance hit), and full compatibility with the Locker Service security sandbox.
Migration Recommendations
If you plan to experiment during the Beta phase, here's a suggested priority order:
- Use it in new components: For components built in your Sandbox, set apiVersion to 66.0 and write presentation logic directly in templates
- Migrate existing components incrementally: Identify getters that do nothing but simple concatenation or conditional checks. Replace them one at a time and run your tests after each change
- Leave business logic alone: Getters handling data processing, API calls, or complex calculations stay in JavaScript
- Wait for GA before production: Beta syntax may change — don't plant landmines in your production org
A useful self-check: if removing a getter leaves your JS file with only property declarations and lifecycle hooks, that component is probably an ideal candidate for template expressions.
References
- Template Expressions - LWC Developer Guide
- Use Complex Expressions to Compute Values - LWC Developer Guide
- The Salesforce Developer's Guide to the Spring '26 Release
- 9 Features for Salesforce Developers in the Spring '26 Release - Salesforce Ben
- Latest Salesforce LWC Updates for Developers - Winter & Spring '26
- Support JavaScript expressions in templates - GitHub Issue #2521
- Salesforce Spring 26 Developer Updates That Actually Change How You Build