7 Modern Angular UI Animations You Can Build in 2026
Seven practical Angular UI animations for 2026 — from view transitions and staggered lists to scroll reveals and micro-interactions — built the modern, performant way.
TL;DR: Modern Angular animation in 2026 leans on the platform rather than the old
@angular/animationsDSL: the View Transitions API for page transitions, CSS keyframes for staggered list reveals,IntersectionObserverfor scroll reveals, CSS transitions for micro-interactions, signal-driven values for number counters, and shimmer skeletons for loading. Animate onlytransformandopacityfor performance, and always respectprefers-reduced-motion.
Animation is where a competent Angular app starts to feel premium. The gap between a screen that snaps and a screen that moves is small in code and large in perception.
The way Angular teams build animations in 2026 has shifted, though. The old @angular/animations package, with its trigger/state/transition DSL, is no longer the default reach. Modern apps lean on CSS, native browser APIs, and a few Angular features that play nicely with signals and zoneless change detection.
Here are seven animations worth knowing, with the modern approach for each.
1. Page transitions with the View Transitions API
Route changes used to be the hardest thing to animate cleanly. The browser's View Transitions API changed that, and Angular's router supports it directly.
Enable it once in your router config:
import { provideRouter, withViewTransitions } from '@angular/router';
export const appConfig = {
providers: [provideRouter(routes, withViewTransitions())],
};
Then the browser cross-fades between routes automatically. You refine it with CSS:
::view-transition-old(root),
::view-transition-new(root) {
animation-duration: 250ms;
}
This is the single highest-impact animation you can add. It is a few lines and it makes navigation feel like an app rather than a series of page loads.
2. Staggered list reveals
Lists that appear all at once feel flat. A small stagger — each item arriving a beat after the last — reads as intentional and polished. You no longer need the animations DSL for this; a CSS keyframe with a per-item delay does it.
@Component({
selector: 'app-feature-list',
template: `
<ul>
@for (item of items(); track item.id; let i = $index) {
<li class="reveal" [style.animation-delay.ms]="i * 60">
{{ item.label }}
</li>
}
</ul>
`,
})
export class FeatureList {
items = input<{ id: string; label: string }[]>([]);
}
.reveal {
opacity: 0;
animation: reveal 300ms ease forwards;
}
@keyframes reveal {
to {
opacity: 1;
transform: translateY(0);
}
}
Binding animation-delay to the index keeps the stagger data-driven, so it scales with the list automatically.
3. Scroll-triggered reveals with IntersectionObserver
Animating elements as they scroll into view is a staple of modern marketing pages. The performant way is IntersectionObserver rather than scroll listeners, wrapped in a small directive so it is reusable.
import { Directive, ElementRef, inject, signal } from '@angular/core';
@Directive({
selector: '[appReveal]',
host: { '[class.is-visible]': 'visible()' },
})
export class Reveal {
private readonly el = inject(ElementRef<HTMLElement>);
visible = signal(false);
constructor() {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
this.visible.set(true);
observer.disconnect();
}
},
{ threshold: 0.2 },
);
observer.observe(this.el.nativeElement);
}
}
The host binding flips a class when the element enters the viewport, and CSS handles the rest. Disconnecting after the first reveal keeps it cheap.
4. Button and card micro-interactions
The smallest animations carry the most weight per byte. A button that lifts slightly on hover, a card that raises its shadow — these are pure CSS and cost nothing at runtime.
.button {
transition:
transform 150ms ease,
box-shadow 150ms ease;
}
.button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}
.button:active {
transform: translateY(0);
}
Resist the urge to animate everything. A consistent set of small, fast transitions across your components reads as quality. A page where everything moves reads as noise.
5. Smooth height transitions for expanding panels
Animating an element from zero to "however tall its content is" has always been awkward because CSS could not transition to height: auto. Modern CSS fixes this with interpolate-size and calc-size(), and where that is not available the CDK's accordion and overlay primitives handle it.
.panel {
interpolate-size: allow-keywords;
transition: height 250ms ease;
height: 0;
overflow: hidden;
}
.panel.open {
height: auto;
}
For wide browser support today, prefer the Angular CDK's BidiModule and overlay/accordion behaviors, which manage the measurement for you.
6. Animated number counters
Dashboards feel alive when numbers count up instead of snapping into place. With signals this is clean — drive a signal with requestAnimationFrame and read it in the template.
import { Component, signal } from '@angular/core';
@Component({
selector: 'app-counter',
template: `<span class="stat">{{ display() }}</span>`,
})
export class StatCounter {
display = signal(0);
countTo(target: number, duration = 800) {
const start = performance.now();
const step = (now: number) => {
const progress = Math.min((now - start) / duration, 1);
this.display.set(Math.round(target * progress));
if (progress < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
}
}
Because display is a signal, the template updates with each frame and nothing else re-renders.
7. Skeleton loaders instead of spinners
A spinner says "wait." A skeleton says "your content is almost here." Skeletons reduce perceived load time and prevent layout shift, which also helps your Core Web Vitals.
.skeleton {
background: linear-gradient(90deg, #eee 25%, #f5f5f5 50%, #eee 75%);
background-size: 200% 100%;
animation: shimmer 1.2s infinite;
border-radius: 8px;
}
@keyframes shimmer {
to {
background-position: -200% 0;
}
}
Render the skeleton while a signal-driven loading() flag is true, then swap in the real content. It is a small touch that makes an app feel fast even when the network is not.
A word on performance and accessibility
Two things keep animation from backfiring:
- Animate
transformandopacity, notwidth,height,top, orleft. The first two are GPU-composited and cheap; the others trigger layout and paint on every frame. - Respect
prefers-reduced-motion. Some users get motion sick or simply prefer stillness. Wrap non-essential animation in a media query.
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation: none !important;
transition: none !important;
}
}
Skipping the reduced-motion check is the most common animation mistake, and it is an accessibility issue, not just a nicety.
Where a component library saves you time
Every animation here is a few lines, but doing them consistently across an app — same timing curves, same durations, same reduced-motion handling — is the actual work. That consistency is what a design system gives you for free.
A library like ZyraUI ships these micro-interactions and loaders with motion built in and accessibility handled, so you compose animated components instead of re-tuning easing curves on every screen. Build the bespoke ones yourself; let the library cover the repeatable ones.
Frequently asked questions
How do I animate route transitions in Angular?
Enable the browser's View Transitions API in your router config with withViewTransitions(). The browser then cross-fades between routes automatically, and you refine the timing with ::view-transition-old and ::view-transition-new CSS rules.
Do I still need the @angular/animations package in 2026?
Often not. Most common animations — hover effects, reveals, staggers, loaders — are cleaner with CSS, the View Transitions API, IntersectionObserver, and signals. The animations package is still available, but it is no longer the default reach for everyday UI motion.
Which CSS properties are safe to animate for performance?
Animate transform and opacity. They are GPU-composited and cheap. Avoid animating width, height, top, and left, which trigger layout and paint on every frame.
How do I make Angular animations accessible?
Respect the prefers-reduced-motion media query and disable non-essential animation for users who request reduced motion. Skipping this is the most common animation mistake and is an accessibility issue, not just a preference.
Final thoughts
Modern Angular animation is less about a special package and more about using the platform well: the View Transitions API, IntersectionObserver, signal-driven values, and disciplined CSS. Used sparingly and consistently, these turn a functional Angular app into one that feels genuinely premium.
To go deeper: