← Back
Platform2026-03-07

LWC Template Expression Revolution: Spring '26 Complex Template Expressions in Practice

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:

  1. Use it in new components: For components built in your Sandbox, set apiVersion to 66.0 and write presentation logic directly in templates
  2. 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
  3. Leave business logic alone: Getters handling data processing, API calls, or complex calculations stay in JavaScript
  4. 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

Related Articles

Discussion

Ask a Question

Your email will not be published.

No questions yet. Be the first to ask!

LWC Template Expression Revolution: Spring '26 Complex Template Expressions in Practice | Agentforce Lens