|
| 1 | +# Route transition animations |
| 2 | + |
| 3 | +Route transition animations enhance user experience by providing smooth visual transitions when navigating between different views in your Angular application. [Angular Router](/guide/routing/overview) includes built-in support for the browser's View Transitions API, enabling seamless animations between route changes in supported browsers. |
| 4 | + |
| 5 | +HELPFUL: The Router's native View Transitions integration is currently in [developer preview](/reference/releases#developer-preview). Native View Transitions are a relatively new browser feature with limited support across all browsers. |
| 6 | + |
| 7 | +## How View Transitions work |
| 8 | + |
| 9 | +View transitions use the browser's native [`document.startViewTransition` API](https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition) to create smooth animations between different states of your application. The API works by: |
| 10 | + |
| 11 | +1. **Capturing the current state** - The browser takes a screenshot of the current page |
| 12 | +2. **Executing the DOM update** - Your callback function runs to update the DOM |
| 13 | +3. **Capturing the new state** - The browser captures the updated page state |
| 14 | +4. **Playing the transition** - The browser animates between the old and new states |
| 15 | + |
| 16 | +Here's the basic structure of the `startViewTransition` API: |
| 17 | + |
| 18 | +```ts |
| 19 | +document.startViewTransition(async () => { |
| 20 | + await updateTheDOMSomehow(); |
| 21 | +}); |
| 22 | +``` |
| 23 | + |
| 24 | +For more details about the browser API, see the [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions). |
| 25 | + |
| 26 | +## How the Router uses view transitions |
| 27 | + |
| 28 | +Angular Router integrates view transitions into the navigation lifecycle to create seamless route changes. During navigation, the Router: |
| 29 | + |
| 30 | +1. **Completes navigation preparation** - Route matching, [lazy loading](/guide/routing/define-routes#lazily-loaded-components), [guards](/guide/routing/route-guards), and [resolvers](/guide/routing/data-resolvers) execute |
| 31 | +2. **Initiates the view transition** - Router calls `startViewTransition` when routes are ready for activation |
| 32 | +3. **Updates the DOM** - Router activates new routes and deactivates old ones within the transition callback |
| 33 | +4. **Finalizes the transition** - The transition Promise resolves when Angular completes rendering |
| 34 | + |
| 35 | +The Router's view transition integration acts as a [progressive enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). When browsers don't support the View Transitions API, the Router performs normal DOM updates without animation, ensuring your application works across all browsers. |
| 36 | + |
| 37 | +## Enabling View Transitions in the Router |
| 38 | + |
| 39 | +Enable view transitions by adding the `withViewTransitions` feature to your [router configuration](/guide/routing/define-routes#adding-the-router-to-your-application). Angular supports both standalone and NgModule bootstrap approaches: |
| 40 | + |
| 41 | +### Standalone bootstrap |
| 42 | + |
| 43 | +```ts |
| 44 | +import { bootstrapApplication } from '@angular/platform-browser'; |
| 45 | +import { provideRouter, withViewTransitions } from '@angular/router'; |
| 46 | +import { routes } from './app.routes'; |
| 47 | + |
| 48 | +bootstrapApplication(MyApp, { |
| 49 | + providers: [ |
| 50 | + provideRouter(routes, withViewTransitions()), |
| 51 | + ] |
| 52 | +}); |
| 53 | +``` |
| 54 | + |
| 55 | +### NgModule bootstrap |
| 56 | + |
| 57 | +```ts |
| 58 | +import { NgModule } from '@angular/core'; |
| 59 | +import { RouterModule } from '@angular/router'; |
| 60 | + |
| 61 | +@NgModule({ |
| 62 | + imports: [RouterModule.forRoot(routes, {enableViewTransitions: true})] |
| 63 | +}) |
| 64 | +export class AppRouting {} |
| 65 | +``` |
| 66 | + |
| 67 | +[Try the "count" example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-2dnvtm?file=src%2Fmain.ts) |
| 68 | + |
| 69 | +This example demonstrates how router navigation can replace direct `startViewTransition` calls for counter updates. |
| 70 | + |
| 71 | +## Customizing transitions with CSS |
| 72 | + |
| 73 | +You can customize view transitions using CSS to create unique animation effects. The browser creates separate transition elements that you can target with CSS selectors. |
| 74 | + |
| 75 | +To create custom transitions: |
| 76 | + |
| 77 | +1. **Add view-transition-name** - Assign unique names to elements you want to animate |
| 78 | +2. **Define global animations** - Create CSS animations in your global styles |
| 79 | +3. **Target transition pseudo-elements** - Use `::view-transition-old()` and `::view-transition-new()` selectors |
| 80 | + |
| 81 | +Here's an example that adds a rotation effect to a counter element: |
| 82 | + |
| 83 | +```css |
| 84 | +/* Define keyframe animations */ |
| 85 | +@keyframes rotate-out { |
| 86 | + to { |
| 87 | + transform: rotate(90deg); |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +@keyframes rotate-in { |
| 92 | + from { |
| 93 | + transform: rotate(-90deg); |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | +/* Target view transition pseudo-elements */ |
| 98 | +::view-transition-old(count), |
| 99 | +::view-transition-new(count) { |
| 100 | + animation-duration: 200ms; |
| 101 | + animation-name: -ua-view-transition-fade-in, rotate-in; |
| 102 | +} |
| 103 | + |
| 104 | +::view-transition-old(count) { |
| 105 | + animation-name: -ua-view-transition-fade-out, rotate-out; |
| 106 | +} |
| 107 | +``` |
| 108 | + |
| 109 | +IMPORTANT: Define view transition animations in your global styles file, not in component styles. Angular's [view encapsulation](/guide/components/styling#view-encapsulation) scopes component styles, which prevents them from targeting the transition pseudo-elements correctly. |
| 110 | + |
| 111 | +[Try the updated “count” example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-fwn4i7?file=src%2Fmain.ts) |
| 112 | + |
| 113 | +## Advanced transition control with onViewTransitionCreated |
| 114 | + |
| 115 | +The `withViewTransitions` feature accepts an options object with an `onViewTransitionCreated` callback for advanced control over view transitions. This callback: |
| 116 | + |
| 117 | +- Runs in an [injection context](/guide/di/dependency-injection-context#run-within-an-injection-context) |
| 118 | +- Receives a [`ViewTransitionInfo`](/api/router/ViewTransitionInfo) object containing: |
| 119 | + - The `ViewTransition` instance from `startViewTransition` |
| 120 | + - The [`ActivatedRouteSnapshot`](/api/router/ActivatedRouteSnapshot) for the route being navigated from |
| 121 | + - The [`ActivatedRouteSnapshot`](/api/router/ActivatedRouteSnapshot) for the route being navigated to |
| 122 | + |
| 123 | +Use this callback to customize transition behavior based on navigation context. For example, you can skip transitions for specific navigation types: |
| 124 | + |
| 125 | +```ts |
| 126 | +import { inject } from '@angular/core'; |
| 127 | +import { Router, withViewTransitions } from '@angular/router'; |
| 128 | + |
| 129 | +withViewTransitions({ |
| 130 | + onViewTransitionCreated: ({transition}) => { |
| 131 | + const router = inject(Router); |
| 132 | + const targetUrl = router.getCurrentNavigation()!.finalUrl!; |
| 133 | + |
| 134 | + // Skip transition if only fragment or query params change |
| 135 | + const config = { |
| 136 | + paths: 'exact', |
| 137 | + matrixParams: 'exact', |
| 138 | + fragment: 'ignored', |
| 139 | + queryParams: 'ignored', |
| 140 | + }; |
| 141 | + |
| 142 | + if (router.isActive(targetUrl, config)) { |
| 143 | + transition.skipTransition(); |
| 144 | + } |
| 145 | + }, |
| 146 | +}) |
| 147 | +``` |
| 148 | + |
| 149 | +This example skips the view transition when navigation only changes the [URL fragment or query parameters](/guide/routing/read-route-state#query-parameters) (such as anchor links within the same page). The `skipTransition()` method prevents the animation while still allowing the navigation to complete. |
| 150 | + |
| 151 | +## Examples from the Chrome explainer adapted to Angular |
| 152 | + |
| 153 | +The following examples demonstrate various view transition techniques adapted from the Chrome team's documentation for use with Angular Router: |
| 154 | + |
| 155 | +### Transitioning elements don't need to be the same DOM element |
| 156 | + |
| 157 | +Elements can transition smoothly between different DOM elements as long as they share the same `view-transition-name`. |
| 158 | + |
| 159 | +- [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#transitioning_elements_dont_need_to_be_the_same_dom_element) |
| 160 | +- [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-dh8npr?file=src%2Fmain.ts) |
| 161 | + |
| 162 | +### Custom entry and exit animations |
| 163 | + |
| 164 | +Create unique animations for elements entering and leaving the viewport during route transitions. |
| 165 | + |
| 166 | +- [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#custom_entry_and_exit_transitions) |
| 167 | +- [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-8kly3o) |
| 168 | + |
| 169 | +### Async DOM updates and waiting for content |
| 170 | + |
| 171 | +Angular Router prioritizes immediate transitions over waiting for additional content to load. |
| 172 | + |
| 173 | +- [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#async_dom_updates_and_waiting_for_content) |
| 174 | + |
| 175 | +NOTE: Angular Router does not provide a way to delay view transitions. This design choice prevents pages from becoming non-interactive while waiting for additional content. As the Chrome documentation notes: "During this time, the page is frozen, so delays here should be kept to a minimum…in some cases it's better to avoid the delay altogether, and use the content you already have." |
| 176 | + |
| 177 | +### Handle multiple view transition styles with view transition types |
| 178 | + |
| 179 | +Use view transition types to apply different animation styles based on navigation context. |
| 180 | + |
| 181 | +- [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#view-transition-types) |
| 182 | +- [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-vxzcam) |
| 183 | + |
| 184 | +### Handle multiple view transition styles with a class name on the view transition root (deprecated) |
| 185 | + |
| 186 | +This approach uses CSS classes on the transition root element to control animation styles. |
| 187 | + |
| 188 | +- [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#changing-on-navigation-type) |
| 189 | +- [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-nmnzzg?file=src%2Fmain.ts) |
| 190 | + |
| 191 | +### Transitioning without freezing other animations |
| 192 | + |
| 193 | +Maintain other page animations during view transitions to create more dynamic user experiences. |
| 194 | + |
| 195 | +- [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#transitioning-without-freezing) |
| 196 | +- [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-76kgww) |
| 197 | + |
| 198 | +### Animating with JavaScript |
| 199 | + |
| 200 | +Control view transitions programmatically using JavaScript APIs for complex animation scenarios. |
| 201 | + |
| 202 | +- [Chrome Explainer](https://developer.chrome.com/docs/web-platform/view-transitions/same-document#animating-with-javascript) |
| 203 | +- [Angular Example on StackBlitz](https://stackblitz.com/edit/stackblitz-starters-cklnkm) |
| 204 | + |
| 205 | +## Alternative: Angular Animations |
| 206 | + |
| 207 | +If you need broader browser support or more granular control over animations, you can use the [`@angular/animations`](/guide/animations) package instead of native view transitions. Angular's animation system works with router state changes and provides: |
| 208 | + |
| 209 | +- **Universal browser support** - Works across all browsers that support Angular |
| 210 | +- **Fine-grained control** - Define complex animation sequences and timing |
| 211 | +- **Router integration** - Create animations based on route changes, URL patterns, or [`ActivatedRoute`](/api/router/ActivatedRoute) data |
| 212 | + |
| 213 | +Learn more about creating route-based animations with [animation triggers and transitions](/guide/animations/transition-and-triggers). |
0 commit comments