In-depth Guides
Animations

Animating your applications with animate.enter and animate.leave

Well-designed animations can make your application more fun and straightforward to use, but they aren't just cosmetic. Animations can improve your application and user experience in a number of ways:

  • Without animations, web page transitions can seem abrupt and jarring
  • Motion greatly enhances the user experience, so animations give users a chance to detect the application's response to their actions
  • Good animations can smoothly direct the user's attention throughout a workflow

Angular provides animate.enter and animate.leave to animate your application's elements. These two features apply enter and leave CSS classes at the appropriate times or call functions to apply animations from third party libraries. animate.enter and animate.leave are not directives. They are special API supported directly by the Angular compiler. They can be used on elements directly and can also be used as a host binding.

animate.enter

You can use animate.enter to animate elements as they enter the DOM. You can define enter animations using CSS classes with either transforms or keyframe animations.

src/app/enter.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-enter',  templateUrl: 'enter.html',  styleUrls: ['enter.css'],})export class Enter {  isShown = signal(false);  toggle() {    this.isShown.update((isShown) => !isShown);  }}

src/app/enter.html

<h2><code>animate.enter</code> Example</h2><button type="button" (click)="toggle()">Toggle Element</button>@if (isShown()) {  <div class="enter-container" animate.enter="enter-animation">    <p>The box is entering.</p>  </div>}

src/app/enter.css

:host {  display: block;  height: 200px;}.enter-container {  border: 1px solid #dddddd;  margin-top: 1em;  padding: 20px 20px 0px 20px;  color: #000000;  font-weight: bold;  font-size: 20px;}.enter-animation {  animation: slide-fade 1s;}@keyframes slide-fade {  from {    opacity: 0;    transform: translateY(20px);  }  to {    opacity: 1;    transform: translateY(0);  }}

When the animation completes, Angular removes the class or classes that you specified in animate.enter from the DOM. Animation classes are only be present while the animation is active.

NOTE: When using multiple keyframe animations or transition properties on an element, Angular removes all classes only after the longest animation has completed.

You can use animate.enter with any other Angular features, such as control flow or dynamic expressions. animate.enter accepts both a single class string (with multiple classes separated by spaces), or an array of class strings.

src/app/enter-binding.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-enter-binding',  templateUrl: 'enter-binding.html',  styleUrls: ['enter-binding.css'],})export class EnterBinding {  isShown = signal(false);  toggle() {    this.isShown.update((isShown) => !isShown);  }  enterClass = signal('enter-animation');}

src/app/enter-binding.html

<h2><code>animate.enter</code> Binding Example</h2><button type="button" (click)="toggle()">Toggle Element</button>@if (isShown()) {  <div class="enter-container" [animate.enter]="enterClass()">    <p>The box is entering.</p>  </div>}

src/app/enter-binding.css

:host {  display: block;  height: 200px;}.enter-container {  border: 1px solid #dddddd;  margin-top: 1em;  padding: 20px 20px 0px 20px;  color: #000000;  font-weight: bold;  font-size: 20px;}.enter-animation {  animation: slide-fade 1s;}@keyframes slide-fade {  from {    opacity: 0;    transform: translateY(20px);  }  to {    opacity: 1;    transform: translateY(0);  }}

animate.leave

You can use animate.leave to animate elements as they leave the DOM. You can define leave animations using CSS classes with either transforms or keyframe animations.

src/app/leave.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-leave',  templateUrl: 'leave.html',  styleUrls: ['leave.css'],})export class Leave {  isShown = signal(false);  toggle() {    this.isShown.update((isShown) => !isShown);  }}

src/app/leave.html

<h2><code>animate.leave</code> Example</h2><button type="button" (click)="toggle()">Toggle Element</button>@if (isShown()) {  <div class="leave-container" animate.leave="leaving">    <p>Goodbye</p>  </div>}

src/app/leave.css

:host {  display: block;  height: 200px;}.leave-container {  border: 1px solid #dddddd;  margin-top: 1em;  padding: 20px 20px 0px 20px;  color: #000000;  font-weight: bold;  font-size: 20px;  opacity: 1;  transition: opacity 200ms ease-in;  @starting-style {    opacity: 0;  }}.leaving {  opacity: 0;  transform: translateY(20px);  transition: opacity 500ms ease-out, transform 500ms ease-out;}

When the animation completes, Angular automatically removes the animated element from the DOM.

NOTE: When using multiple keyframe animations or transition properties on a an element, Angular waits to remove the element only after the longest of those animations has completed.

animate.leave can also be used with signals, and other bindings. You can use animate.leave with a single class or multiple classes. Either specify it as a simple string with spaces or a string array.

src/app/leave-binding.ts

import {Component, signal} from '@angular/core';@Component({  selector: 'app-leave-binding',  templateUrl: 'leave-binding.html',  styleUrls: ['leave-binding.css'],})export class LeaveBinding {  isShown = signal(false);  toggle() {    this.isShown.update((isShown) => !isShown);  }  farewell = signal('leaving');}

src/app/leave-binding.html

<h2><code>animate.leave</code> Binding Example</h2><button type="button" (click)="toggle()">Toggle Element</button>@if (isShown()) {  <div class="leave-container" [animate.leave]="farewell()">    <p>Goodbye</p>  </div>}

src/app/leave-binding.css

:host {  display: block;  height: 200px;}.leave-container {  border: 1px solid #dddddd;  margin-top: 1em;  padding: 20px 20px 0px 20px;  color: #000000;  font-weight: bold;  font-size: 20px;  opacity: 1;  transition: opacity 200ms ease-in;  @starting-style {    opacity: 0;  }}.leaving {  opacity: 0;  transform: translateY(20px);  transition: opacity 500ms ease-out, transform 500ms ease-out;}

Event Bindings, Functions, and Third-party Libraries

Both animate.enter and animate.leave support event binding syntax that allows for function calls. You can use this syntax to call a function in your component code or utilize third-party animation libraries, like GSAP, anime.js, or any other JavaScript animation library.

src/app/leave-event.ts

import {AnimationCallbackEvent, Component, signal} from '@angular/core';@Component({  selector: 'app-leave-binding',  templateUrl: 'leave-event.html',  styleUrls: ['leave-event.css'],})export class LeaveEvent {  isShown = signal(false);  toggle() {    this.isShown.update((isShown) => !isShown);  }  leavingFn(event: AnimationCallbackEvent) {    // Example of calling GSAP    // gsap.to(event.target, {    //   duration: 1,    //   x: 100,    //   // arrow functions are handy for concise callbacks    //   onComplete: () => event.animationComplete()    // });    event.animationComplete();  }}

src/app/leave-event.html

<h2><code>animate.leave</code> Function Example</h2><button type="button" (click)="toggle()">Toggle Element</button>@if (isShown()) {  <div class="leave-container" (animate.leave)="leavingFn($event)">    <p>Goodbye</p>  </div>}

src/app/leave-event.css

:host {  display: block;  height: 200px;}.leave-container {  border: 1px solid #dddddd;  margin-top: 1em;  padding: 20px 20px 0px 20px;  color: #000000;  font-weight: bold;  font-size: 20px;  opacity: 1;  transition: opacity 200ms ease-in;  @starting-style {    opacity: 0;  }}.leaving {  opacity: 0;  transform: translateY(20px);  transition: opacity 500ms ease-out, transform 500ms ease-out;}

The $event object has the type AnimationCallbackEvent. It includes the element as the target and provides an animationComplete() function to notify the framework when the animation finishes.

IMPORTANT: You must call the animationComplete() function when using animate.leave for Angular to remove the element.

If you don't call animationComplete() when using animate.leave, Angular calls the function automatically after a four-second delay. You can configure the duration of the delay by providing the token MAX_ANIMATION_TIMEOUT in milliseconds.

  { provide: MAX_ANIMATION_TIMEOUT, useValue: 6000 }

Testing

TestBed provides built-in support for enabling or disabling animations in your test environment. CSS animations require a browser to run, and many of the APIs are not available in a test environment. By default, TestBed disables animations for you in your test environments.

If you want to test that the animations are animating in a browser test, for example an end-to-end test, you can configure TestBed to enable animations by specifying animationsEnabled: true in your test configuration.

  TestBed.configureTestingModule({animationsEnabled: true});

This will configure animations in your test environment to behave normally.

NOTE: Some test environments do not emit animation events like animationstart, animationend and their transition event equivalents.