w3resource

Route transition animations

Routing enables users to navigate between different routes in an application. When a user navigates from one route to another, the Angular router maps the URL path to a relevant component and displays its view. Animating this route transition can greatly enhance the user experience.

The Angular router comes with high-level animation functions that let you animate the transitions between views when a route changes. To produce an animation sequence when switching between routes, you need to define nested animation sequences. Start with the top-level component that hosts the view, and nest additional animations in the components that host the embedded views.

To enable routing transition animation, do the following:

Import the routing module into the application and create a routing configuration that defines the possible routes.

Add a router outlet to tell the Angular router where to place the activated components in the DOM.

Define the animation.

Let's illustrate a router transition animation by navigating between two routes, Home and About associated with the `HomeComponent` and `AboutComponent` views respectively. Both of these component views are children of the top-most view, hosted by AppComponent. We'll implement a router transition animation that slides in the new view to the right and slides out the old view when the user navigates between the two routes.

Route configuration:

To begin, configure a set of routes using methods available in the RouterModule class. This route configuration tells the router how to navigate.

Use the RouterModule.forRoot method to define a set of routes. Also, import this RouterModule to the imports array of the main module, AppModule.

Note: Use the RouterModule.forRoot method in the root module, AppModule, to register top-level application routes and providers. For feature modules, call the RouterModule.forChild method to register additional routes.

The following configuration defines the possible routes for the application.

TypeScript Code:


import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { RouterModule } from '@angular/router';
import { AppComponent } from './app.component';
import { OpenCloseComponent } from './open-close.component';
import { OpenClosePageComponent } from './open-close-page.component';
import { OpenCloseChildComponent } from './open-close.component.4';
import { ToggleAnimationsPageComponent } from './toggle-animations-page.component';
import { StatusSliderComponent } from './status-slider.component';
import { StatusSliderPageComponent } from './status-slider-page.component';
import { HeroListPageComponent } from './hero-list-page.component';
import { HeroListGroupPageComponent } from './hero-list-group-page.component';
import { HeroListGroupsComponent } from './hero-list-groups.component';
import { HeroListEnterLeavePageComponent } from './hero-list-enter-leave-page.component';
import { HeroListEnterLeaveComponent } from './hero-list-enter-leave.component';
import { HeroListAutoCalcPageComponent } from './hero-list-auto-page.component';
import { HeroListAutoComponent } from './hero-list-auto.component';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
import { InsertRemoveComponent } from './insert-remove.component';


@NgModule({
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    RouterModule.forRoot([
      { path: '', pathMatch: 'full', redirectTo: '/enter-leave' },
      { path: 'open-close', component: OpenClosePageComponent },
      { path: 'status', component: StatusSliderPageComponent },
      { path: 'toggle', component: ToggleAnimationsPageComponent },
      { path: 'heroes', component: HeroListPageComponent, data: {animation: 'FilterPage'} },
      { path: 'hero-groups', component: HeroListGroupPageComponent },
      { path: 'enter-leave', component: HeroListEnterLeavePageComponent },
      { path: 'auto', component: HeroListAutoCalcPageComponent },
      { path: 'home', component: HomeComponent, data: {animation: 'HomePage'} },
      { path: 'about', component: AboutComponent, data: {animation: 'AboutPage'} },

    ])
  ],
  
  

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen route-transition by w3resource (@w3resource) on CodePen.


The `home` and `about` paths are associated with the `HomeComponent` and `AboutComponent` views. The route configuration tells the Angular router to instantiate the `HomeComponent` and AboutComponent views when the navigation matches the corresponding path.

In addition to path and component, the data property of each route defines the key animation-specific configuration associated with a route. The data property value is passed into AppComponent when the route changes. You can also pass additional data in the route config that is consumed within the animation. The data property value has to match the transitions defined in the routeAnimation trigger, which we'll define later.

Router outlet

After configuring the routes, tell the Angular router where to render the views when matched with a route. You can set a router outlet by inserting a <router-outlet> container inside the root AppComponent template.

The <router-outlet> container has an attribute directive that contains data about active routes and their states, based on the data property that we set in the route configuration

TypeScript Code:

<div [@routeAnimations]="prepareRoute(outlet)" >
  <router-outlet #outlet="outlet"></router-outlet>
</div>

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen route-transition-example by w3resource (@w3resource) on CodePen.


AppComponent defines a method that can detect when a view changes. The method assigns an animation state value to the animation trigger (@routeAnimation) based on the route configuration data property value. Here's an example of an AppComponent method that detects when a route change happens.

TypeScript Code:

prepareRoute(outlet: RouterOutlet) {
  return outlet && outlet.activatedRouteData && outlet.activatedRouteData['animation'];
}

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen router-transition-example-2 by w3resource (@w3resource) on CodePen.


Here, the prepareRoute() method takes the value of the output directive (established through #outlet="outlet") and returns a string value representing the state of the animation based on the custom data of the current active route. You can use this data to control which transition to execute for each route.

Animation definition

Animations can be defined directly inside your components. For this example we are defining the animations in a separate file, which allows us to re-use the animations.

The following code snippet defines a reusable animation named slideInAnimation.

TypeScript Code:

export const slideInAnimation =
  trigger('routeAnimations', [
    transition('HomePage <=> AboutPage', [
      style({ position: 'relative' }),
      query(':enter, :leave', [
        style({
          position: 'absolute',
          top: 0,
          left: 0,
          width: '100%'
        })
      ]),
      query(':enter', [
        style({ left: '-100%'})
      ]),
      query(':leave', animateChild()),
      group([
        query(':leave', [
          animate('300ms ease-out', style({ left: '100%'}))
        ]),
        query(':enter', [
          animate('300ms ease-out', style({ left: '0%'}))
        ])
      ]),
      query(':enter', animateChild()),
    ]),
    transition('* <=> FilterPage', [
      style({ position: 'relative' }),
      query(':enter, :leave', [
        style({
          position: 'absolute',
          top: 0,
          left: 0,
          width: '100%'
        })
      ]),
      query(':enter', [
        style({ left: '-100%'})
      ]),
      query(':leave', animateChild()),
      group([
        query(':leave', [
          animate('200ms ease-out', style({ left: '100%'}))
        ]),
        query(':enter', [
          animate('300ms ease-out', style({ left: '0%'}))
        ])
      ]),
      query(':enter', animateChild()),
    ])
  ]);
  
  

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen vMQmMz by w3resource (@w3resource) on CodePen.


The animation definition does several things:

  • Defines two transitions. A single trigger can define multiple states and transitions.
  • Adjusts the styles of the host and child views to control their relative positions during the transition.
  • Uses query() to determine which child view is entering and which is leaving the host view.

A route change activates the animation trigger, and a transition matching the state change is applied.

Note: The transition states must match the data property value defined in the route configuration.

Make the animation definition available in your application by adding the reusable animation (slideInAnimation) to the animations metadata of the AppComponent.

TypeScript Code:

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.css'],
  animations: [
    slideInAnimation
    // animation triggers go here
  ]
})

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen ROqVXq by w3resource (@w3resource) on CodePen.


Styling the host and child components

During a transition, a new view is inserted directly after the old one and both elements appear on the screen at the same time. To prevent this, apply additional styling to the host view, and to the removed and inserted child views. The host view must use relative positioning, and the child views must use absolute positioning. Adding styling to the views animates the containers in place, without the DOM moving things around.

TypeScript Code:

trigger('routeAnimations', [
  transition('HomePage <=> AboutPage', [
    style({ position: 'relative' }),
    query(':enter, :leave', [
      style({
        position: 'absolute',
        top: 0,
        left: 0,
        width: '100%'
      })
    ]),

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen eoQRYv by w3resource (@w3resource) on CodePen.


Querying the view containers

Use the query() method to find and animate elements within the current host component. The query(":enter") statement returns the view that is being inserted, and query(":leave") returns the view that is being removed.

Let's assume that we are routing from the Home => About.

TypeScript Code:

query(':enter', [
    style({ left: '-100%'})
  ]),
  query(':leave', animateChild()),
  group([
    query(':leave', [
      animate('300ms ease-out', style({ left: '100%'}))
    ]),
    query(':enter', [
      animate('300ms ease-out', style({ left: '0%'}))
    ])
  ]),
  query(':enter', animateChild()),
]),
transition('* <=> FilterPage', [
  style({ position: 'relative' }),
  query(':enter, :leave', [
    style({
      position: 'absolute',
      top: 0,
      left: 0,
      width: '100%'
    })
  ]),
  query(':enter', [
    style({ left: '-100%'})
  ]),
  query(':leave', animateChild()),
  group([
    query(':leave', [
      animate('200ms ease-out', style({ left: '100%'}))
    ]),
    query(':enter', [
      animate('300ms ease-out', style({ left: '0%'}))
    ])
  ]),
  query(':enter', animateChild()),
])

Live Demo:

It is just a code snippet explaining a particular concept and may not have any output

See the Pen GLwEge by w3resource (@w3resource) on CodePen.


The animation code does the following after styling the views:

  • query(':enter style({ left: '-100%'}) matches the view that is added and hides the newly added view by positioning it to the far left.
  • Calls animateChild() on the view that is leaving, to run its child animations.
  • Uses group() function to make the inner animations run in parallel.
  • Within the group() function:
  • Queries the view that is removed and animates it to slide far to the right.
  • Slides in the new view by animating the view with an easing function and duration.
    This animation results in the about view sliding from the left to right.

Previous: Security
Next: Angular service worker introduction



Follow us on Facebook and Twitter for latest update.