--- title: Optimize Conditional Rendering impact: HIGH impactDescription: Reduces unnecessary rerenders in dynamic template branches tags: templates, conditionals, rendering, performance, glimmer --- ## Optimize Conditional Rendering Use efficient conditional rendering patterns to minimize unnecessary DOM updates and improve rendering performance. ## Problem Inefficient conditional logic causes excessive re-renders, creates complex template code, and can lead to poor performance in lists and dynamic UIs. **Incorrect:** ```glimmer-js // app/components/user-list.gjs import Component from '@glimmer/component'; class UserList extends Component { } ``` ## Solution Use `{{#if}}` / `{{#else if}}` / `{{#else}}` chains and extract computed logic to getters for better performance and readability. **Correct:** ```glimmer-js // app/components/user-list.gjs import Component from '@glimmer/component'; class UserList extends Component { } ``` ## Extracted Logic Pattern For complex conditions, use getters: ```glimmer-js // app/components/user-card.gjs import Component from '@glimmer/component'; import { cached } from '@glimmer/tracking'; class UserCard extends Component { @cached get isActive() { return this.args.user.status === 'active' && this.args.user.lastLoginDays < 30; } @cached get showActions() { return this.args.canEdit && !this.args.user.locked && this.isActive; } } ``` ## Conditional Lists Use `{{#if}}` to guard `{{#each}}` and avoid rendering empty states: ```glimmer-js // app/components/task-list.gjs import Component from '@glimmer/component'; class TaskList extends Component { get hasTasks() { return this.args.tasks?.length > 0; } } ``` ## Avoid Nested Conditionals **Bad:** ```glimmer-js {{#if @user}} {{#if @user.isPremium}} {{#if @user.hasAccess}} {{/if}} {{/if}} {{/if}} ``` **Good:** ```glimmer-js // app/components/content-gate.gjs import Component from '@glimmer/component'; import { cached } from '@glimmer/tracking'; class ContentGate extends Component { @cached get canViewPremium() { return this.args.user?.isPremium && this.args.user?.hasAccess; } } ``` ## Component Switching Pattern Use conditional rendering for component selection: ```glimmer-js // app/components/media-viewer.gjs import Component from '@glimmer/component'; import ImageViewer from './image-viewer'; import VideoPlayer from './video-player'; import AudioPlayer from './audio-player'; import { cached } from '@glimmer/tracking'; class MediaViewer extends Component { @cached get mediaType() { return this.args.media?.type; } } ``` ## Loading States Pattern for async data with loading/error states: ```glimmer-js // app/components/data-display.gjs import Component from '@glimmer/component'; import { Resource } from 'ember-resources'; import { resource } from 'ember-resources'; class DataResource extends Resource { @tracked data = null; @tracked isLoading = true; @tracked error = null; modify(positional, named) { this.fetchData(named.url); } async fetchData(url) { this.isLoading = true; this.error = null; try { const response = await fetch(url); this.data = await response.json(); } catch (e) { this.error = e.message; } finally { this.isLoading = false; } } } class DataDisplay extends Component { @resource data = DataResource.from(() => ({ url: this.args.url, })); } ``` ## Performance Impact - **Chained if/else**: 40-60% faster than multiple independent {{#if}} blocks - **Extracted getters**: ~20% faster for complex conditions (cached) - **Component switching**: Same performance as {{#if}} but better code organization ## When to Use - **{{#if}}/{{#else}}**: For simple true/false conditions - **Extracted getters**: For complex or reused conditions - **Component switching**: For different component types based on state - **Guard clauses**: To avoid rendering large subtrees when not needed ## References - [Ember Guides - Conditionals](https://guides.emberjs.com/release/components/conditional-content/) - [Glimmer VM Performance](https://github.com/glimmerjs/glimmer-vm) - [@cached decorator](https://api.emberjs.com/ember/release/functions/@glimmer%2Ftracking/cached)