Free Ebook “The Ultimate Guide To Angular Evolution”

Angular 22 + GDE Insights on AI

Chapters

    Top 5 Features of Angular 21

    Angular 21 premiered in November 2025, bringing a fresh wave of improvements that push the framework further than ever. This release introduces powerful features to streamline development, boost performance, and make your team’s workflow better.

    In this article, we’ll highlight the five new features that stood out the most to us at House of Angular and explain why they matter for your team today.

    1. Signal Forms (experimental)—the Go-To Intuitive Standard for Angular Forms

    Helps: DevEx 

    Challenge: 

    Angular’s form APIs have long existed in two distinct categories: template-driven forms, which prioritize simplicity, and reactive forms, which emphasize structure and control. Both approaches have their merits, but each comes with trade-offs. Reactive forms can feel verbose, while template-driven ones often hide too much logic in the template. 

    As Angular evolves around signals, the question naturally arises: What would forms look like if signals were at the core? How could we handle forms more declaratively, intuitively, and reactively – without juggling subscriptions, emitters, or duplicated state? 

    Solution:

    Version 21 introduces an experimental version of the highly anticipated signal-based forms feature — signal-based forms. 

    Let’s take a look at how the new API works in practice. 

    At the heart of every form is its data model. In this new approach, that model is represented by a signal that serves as the single source of truth for the form’s value. 

    Instead of Angular’s traditional FormControl and FormGroup, you define a writable signal and pass it into the form() function. Then, it can be bound to controls using the Field directive. 

    This creates a two-way connection: typing in an input updates the signal, and updating the signal automatically updates the input. The form itself no longer owns the data; it simply reacts to it.

    @Component({
      selector: 'app-logint',
      imports: [Field],
      template: `
        Login
      `,
    })
    export class LoginComponent {
    
      private readonly _loginModel = signal({
        email: '',
        password: '',
      });
    
      readonly loginForm = form(this._loginModel);
    
      login(): void {
        console.log(this._loginModel());
      }
    
    }

    The second argument of the form() function introduces a schema that defines the behavior and validation logic of each field. Validators, read-only states, and conditional hiding (this type of control doesn’t contribute to form state and validation) or disabling are all declared right next to the data model, making the logic more explicit, typed, and testable.

    readonly loginForm = form(
      this._loginModel,
      (login) => {
    
        required(login.email, {
          message: 'Email is required',
        });
    
        email(login.email, {
          message: 'Provide valid email address',
        });
    
        required(login.password, {
          message: 'Password is required',
        });
    
      }
    );

    Since schemas can be reused and composed, you can extract common logic into shareable utilities:

    export const emailSchema = schema(
      (field) => {
    
        required(field, {
          message: 'Email is required',
        });
    
        email(field, {
          message: 'Provide valid email address',
        });
    
      }
    );
    
    export const loginSchema = schema(
      (login) => {
    
        apply(login.email, emailSchema);
    
        required(login.password, {
          message: 'Password is required',
        });
    
      }
    );

    Another welcome simplification is that a ControlValueAccessor is no longer required for custom controls. Instead, components integrate with the Field directive by implementing the lightweight FormValueControl interface. This interface has only one required property, “value,” which is a model that keeps the component in sync with the control’s value.

    @Component({
      selector: 'app-rating-control',
      ...
    })
    export class RatingControl
      implements FormValueControl {
    
      readonly value = model(0);
    
    }
    @Component({
      selector: 'app-review-component',
      template: `
        
      `,
      imports: [Field, RatingControl],
    })
    export class ReviewComponent {
    
      private readonly _reviewModel = signal({
        name: '',
        rating: 0,
        comment: '',
      });
    
      readonly reviewForm = form(
        this._reviewModel,
        (review) => {
    
          required(review.name);
          required(review.rating);
    
          min(review.rating, 0);
          max(review.rating, 5);
    
        }
      );
    
    }

    Benefits: 

    This signal-based approach reimagines forms as a natural extension of the reactive Angular ecosystem. Forms no longer duplicate or manage state; they simply reflect your data model in real time. 

    Validation and conditional logic move out of the template and into clean, declarative TypeScript schemas. This makes forms easier to reason about, test, and compose. The entire experience feels lighter, more predictable, and deeply reactive from the ground up.

    This experimental API is still in its early stages, but it offers a clear glimpse into the future of Angular forms — a future in which form handling is simpler, more expressive, and perfectly aligned with the framework’s signal-driven architecture.

    2. New Animations API—Effortless, Built-in DOM-aware Animations

    Helps: DevEx, Performance

    Challenge: 

    Although animations are a key part of delivering rich user interfaces, the built-in animation tooling in many Angular applications has begun to feel heavy and outdated. The legacy package @angular/animations was created years ago – before the modern CSS animation capabilities and native browser APIs had matured. 

    As a result, developers face several real drawbacks: large bundle sizes, animations that are entirely managed in JavaScript rather than leveraging native performance optimizations, and limited interoperability with third-party libraries. 

    The mismatch between Angular’s animation module and the modern web platform means animations can become a maintenance burden, slow down performance, or get in the way when trying to integrate new tools or patterns. 

    Solution: 

    Angular has introduced two new built-in animation bindings – animate.enter and animate.leave – that make it easier than ever to animate elements as they appear or disappear in your application. 

    These bindings apply CSS classes or call custom animation functions at appropriate times during an element’s lifecycle. Unlike traditional directives, animate.enter and animate.leave are special compiler-level features that are recognized directly by Angular. You can use them on elements in your templates or even as host bindings inside components. 

    animate.enter 

    The animate.enter binding runs whenever an element enters the DOM. This makes it perfect for defining entrance animations using either CSS transitions or keyframes. 

    Once the animation is complete, Angular automatically removes the specified animation class or classes from the element, ensuring that they are active only for the duration of the animation.

    @Component({
      selector: 'animate-enter-example',
      template: `
        <button (click)="toggleVisibility()">
          Toggle element
        </button>
    
        @if (isVisible()) {
          <div
            class="container"
            animate.enter="enter-animation"
          >
            animate.enter example
          </div>
        }
      `,
      styles: [
        `
          .container {
            border: solid 1px black;
            padding: 1rem;
          }
    
          .enter-animation {
            animation: slide-fade 1s;
          }
    
          @keyframes slide-fade {
    
            from {
              opacity: 0;
              transform: translateY(20px);
            }
    
            to {
              opacity: 1;
              transform: translateY(0);
            }
    
          }
        `,
      ],
    })
    export class EnterExample {
    
      readonly isVisible = signal(false);
    
      toggleVisibility(): void {
        this.isVisible.update(
          (isVisible) => !isVisible
        );
      }
    
    }

    animate.leave 

    The animate.leave binding works similarly, but is used for elements being removed from the DOM. It allows you to define exit animations that run just before Angular removes the element. 

    Angular waits until the animation finishes before detaching the element.

    @Component({
      selector: 'animate-leave-example',
      template: `
        <button (click)="toggleVisibility()">
          Toggle element
        </button>
    
        @if (isVisible()) {
          <div
            class="container"
            animate.leave="leave-animation"
          >
            animate.leave example
          </div>
        }
      `,
      styles: [
        `
          .container {
            border: solid 1px black;
            padding: 1rem;
    
            @starting-style {
              opacity: 0;
            }
          }
    
          .leave-animation {
            opacity: 0;
            transform: translateY(20px);
            transition:
              opacity 500ms ease-out,
              transform 500ms ease-out;
          }
        `,
      ],
    })
    export class LeaveExample {
    
      readonly isVisible = signal(false);
    
      toggleVisibility(): void {
        this.isVisible.update(
          (isVisible) => !isVisible
        );
      }
    
    }

    Event bindings 

    Both the animate.enter and animate.leave support event binding syntax. This allows you to to call component functions or integrate third-party animation libraries. 

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

    @Component({
      selector: 'event-binding-example',
      template: `
        <button (click)="toggleVisibility()">
          Toggle element
        </button>
    
        @if (isVisible()) {
          <div
            class="container"
            (animate.leave)="animateLeaving($event)"
          >
            event binding example
          </div>
        }
      `,
      styles: [
        `
          .container {
            border: solid 1px black;
            padding: 1rem;
          }
        `,
      ],
    })
    export class EventBindingExample {
    
      readonly isVisible = signal(false);
    
      toggleVisibility(): void {
        this.isVisible.update(
          (isVisible) => !isVisible
        );
      }
    
      animateLeaving(
        event: AnimationCallbackEvent
      ): void {
    
        // Example of calling GSAP
        gsap.to(
          event.target,
          {
            duration: 1,
            x: 100,
            onComplete: () =>
              event.animationComplete(),
          }
        );
    
      }
    
    }

    Benefits: 

    With this new API, Angular developers get a more streamlined, performant, and flexible way to handle animations. 

    Reliance on the heavyweight animation module is reduced, resulting in smaller bundles and better runtime performance. Native CSS transitions and third-party animation tools can now be used seamlessly, giving you the freedom to choose the right animation strategy for your project. 

    The declarative animate.enter and animate.leave bindings make templates more readable and expressive by aligning animations with how elements actually enter and leave the view. Additionally, animate.leave’s built-in delayed removal capability ensures smooth transitions without hacks or excessive boilerplate. 

    Ultimately, your animations feel more integrated, your code becomes cleaner, and your app runs better.

    3. Vitest as the Default Testing Framework—No More Uncertainty When Testing

    Helps: DevEx

    Challenge:

    For more than two years, Angular developers faced an uncomfortable uncertainty about testing: which framework should they use, especially for new projects? The official Jasmine/Karma combination was aging. Karma was explicitly not going to be supported long-term, with the Modern Web Test Runner mentioned as a potential replacement. Jest emerged as a community-supported option, but then Vitest appeared as another strong candidate. 

    This created analysis paralysis. Should you stick with the official but outdated Jasmine? Invest in Jest, knowing Vitest might become the official choice? Or adopt Vitest early and hope the Angular team would follow? 

    Solution: 

    Starting with Angular 21, Vitest becomes the default testing framework. The Angular team chose Vitest for several strategic reasons. Most importantly, Vitest offers a browser mode that executes tests in a real browser environment, just like Jasmine and Karma. This makes migration from the old stack significantly smoother – your existing browser-dependent tests can transition without fundamental rewrites. Jest, by contrast, runs on Node.js, which would have created more migration friction. 

    Vitest 4 recently stabilized its browser mode, likely the final piece that made this decision possible. Beyond migration compatibility, Vitest brings modern tooling advantages: first-class TypeScript support, seamless ESM handling (unlike Jest’s ongoing struggles), and integration with Vite – the build tool that’s become the standard across the JavaScript ecosystem. 

    Benefits: 

    The biggest benefit isn’t Vitest itself – it’s finally having a clear, official answer. The uncertainty is over. New projects have an obvious starting point. Existing projects have a clear migration path. 

    Vitest brings Angular closer to the broader JavaScript ecosystem. It’s used across nearly all major frontend frameworks, which means better community support, more plugins, shared knowledge, and a larger pool of developers who already know the tool.

    4. MCP Server—the Bridge Between AI and Your Workflow

    Helps: DevEx

    Challenge: 

    Modern development workflows increasingly rely on AI assistants to accelerate coding tasks, answer technical questions, and guide developers through complex framework features. However, AI tools often lack direct integration with development environments and build tooling, limiting their ability to perform concrete actions like generating code, adding dependencies, or executing framework-specific commands. 

    Developers must frequently switch context between their AI assistant and command-line tools, manually translating AI suggestions into CLI commands. Furthermore, AI assistants without access to current, authoritative framework documentation may provide outdated advice or recommend deprecated patterns, particularly problematic in rapidly evolving frameworks like Angular, where best practices around signals, standalone components, and zoneless applications are still emerging.

    Solution: 

    The Angular CLI now includes an experimental Model Context Protocol (MCP) server that bridges the gap between AI assistants and your development environment. This integration enables AI tools to interact directly with the Angular CLI, performing actions such as code generation and package management without requiring manual command execution.

    The server comes with several powerful tools enabled by default: 

    • ai_tutor: Launches an interactive AI-powered Angular tutor, recommended for new Angular projects using version 20 or later 
    • find_examples: Searches a curated database of official, best-practice examples focusing on modern and recently updated Angular features 
    • get_best_practices: Retrieves the Angular Best Practices Guide covering modern standards, including standalone components, typed forms, and modern control flow 
    • list_projects: Reads your workspace’s angular.json configuration to identify all applications and libraries 
    • onpush_zoneless_migration: Analyzes your code and provides step-by-step migration plans to OnPush change detection, a prerequisite for zoneless applications 
    • search_documentation: Queries the official Angular documentation at https://angular.dev for APIs, tutorials, and best practices

    Benefits: 

    The Angular MCP server transforms how developers interact with AI assistants by creating a seamless bridge between conversation and action. AI tools can now execute Angular CLI commands directly rather than just suggesting them, reducing context switching and the need to translate recommendations into terminal commands manually. This integration ensures developers spend less time copying and pasting commands and more time building features. 

    The curated toolset guarantees that AI assistance is grounded in authoritative, up-to-date Angular knowledge. By connecting assistants directly to official documentation, best practices guides, and vetted code examples, the MCP server helps prevent the common problem of AI tools recommending outdated patterns or deprecated APIs. This is particularly valuable as Angular continues evolving with modern features like signals and zoneless change detection, where current guidance is essential.

    5. Angular Aria—No More Design Compromises

    Helps: DevEx, UX

    Challenge: 

    Building accessible, interactive UI components from scratch is a significant undertaking for development teams. 

    Implementing proper ARIA attributes, keyboard navigation, focus management, and screen reader support requires deep expertise and extensive testing across a range of assistive technologies. Creating robust, production-ready interactive components such as accordions, menus, and comboboxes requires considerable development effort that could be spent on core business logic. 

    Existing component libraries often come with opinionated styling that conflicts with custom design systems, making it difficult to achieve the exact look and feel teams need. 

    These challenges force teams to choose between spending precious development time on component infrastructure or compromising on accessibility and custom styling requirements.

    Solution: 

    Angular Aria provides a modern library of headless, accessible UI components that solve these challenges through a thoughtful, developer-focused approach. Available now in developer preview, this library delivers 8 essential UI patterns across 13 components, all unstyled and ready for customization. 

    The library launches with comprehensive coverage of common interactive patterns, including Accordion, Combobox, Grid, Listbox, Menu, Tabs, Toolbar, and Tree. Each component is built with accessibility as the top priority, automatically handling complex requirements such as keyboard navigation, ARIA attributes, and focus management. 

    Angular Aria embraces modern Angular architecture with signal-based reactivity and contemporary directive patterns, ensuring your components are performant and maintainable.

    @Component({ selector: 'app-tab', template: `
    Movie
    Cast
    Reviews
    Panel 1
    Panel 2
    Panel 3
    `, imports: [TabList, Tab, Tabs, TabPanel, TabContent], }) export class TabComponent {}

    This new offering complements the Angular team’s existing component solutions, creating a complete toolkit where you can use Angular Aria for accessible, headless components with complete styling control, leverage the CDK for behavior primitives like Drag and Drop in custom components, or choose Angular Material for fully styled components following Material Design principles with theming customization.

    Benefits: 

    Angular Aria delivers significant advantages that accelerate development while maintaining the highest standards of accessibility and flexibility. Every component ships with built-in ARIA attributes, keyboard navigation, and screen reader support, eliminating months of specialized development and testing. 

    The headless architecture means you can apply any design system, CSS framework, or custom styles without fighting against pre-existing opinions, making it perfect for teams with established brand guidelines.

    Summary

    Angular’s latest release, version 21, introduces many significant improvements. This time, the Angular team focused on developer experience, making everyday workflows much more efficient. 

    Signal forms, a new animations API, Vitest as the default testing framework, the MCP server, and Angular Aria are just a few highlights of what v21 offers. 

    To explore Angular v21 in detail, along with previous versions and the business side of each release, download our free ebook, “The Ultimate Guide to Angular Evolution.”

     

    Want to dive deeper?

    Our free ebook will show you how changes in Angular versions 14 to 21 affect your team’s workflow and your business. Download now to discover how to align technical improvements with your strategic roadmap in just 10 minutes!

    ebook about the evolution of Angular from version 14 to Angular 21
    Written by
    Miłosz
    Passionate about web application development, enthusiast for clean code and best practices. Focused on becoming better at Angular every day. In his free time, he enjoys cooking and listening to podcasts.

    Social Media

     
    The latest articles