Original text: Introducing Angular v17
Last month marked the 13th anniversary of Angular’s red shield. AngularJS (note that AngularJS is not Angular, which is the 1.x version) is the starting point of a new wave of JavaScript frameworks designed to support the growing demand for rich web experiences. Today, we're taking you into the future with v17, with a new look and a set of forward-looking features, laying a new foundation for performance and development experience.
In v17 we are happy to introduce:
- Deferrable views Deferrable views take performance and development experience to the next level, officially stable from development preview
- In public benchmarks , runtime improved by 90% with loops built into control flow
- Builds are 87% faster for hybrid rendering and 67% faster for client-side rendering
- A new look represents Angular’s future-oriented features
- Brand new interactive learning tutorials, from simple to in-depth, including online playground
- And many other features and improvements!
Future-proof identity
The Angular renaissance has been in full swing for the past few releases. We've been building momentum with improvements like signal-based reactivity, hydration, independent components, combined instructions, and many other features. Although Angular has grown rapidly, its brand hasn't kept up - it's pretty much the same since the early days of AngularJS.
Today, the framework you love and tested by millions of developers gets a new look that reflects its future-proof development experience and performance!
Documentation for the future
Along with the new brand, we've also developed a new home for Angular documentation — angular.dev. For the new documentation site, we've adopted a new structure, new guidance, improved content, and built an interactive learning journey platform that allows you to learn Angular and the Angular CLI at your own pace, right in your browser.
The new interactive learning experience is powered by WebContainers , allowing you to use the power of Angular CLI in any modern web browser!
Today, we are launching a beta preview of angular.dev and plan to make it the default website for Angular in v18. You can learn more about Angular's new look and angular.dev at Angular.dev It's Coming (original text Announcing angular.dev ).
Now let me dive into the features of v17, we can’t wait to show you!
Built-in control flow
To improve the development experience, we've released a new block template syntax that gives you powerful functionality through a simple, declarative API. Under the hood, the Angular compiler converts syntax into efficient JavaScript instructions that can perform control flow, lazy loading, and more.
We use the new block syntax to implement an optimized built-in control flow. After conducting user research, we found that many developers were experiencing some difficulties using , and . Having been using Angular since 2016 and being part of the Angular team for the past 5 years, I personally still need the syntax for finding and . After gathering feedback from the community, partners, and conducting user experience research, we developed a new built-in control flow for Angular! *ngIf
*ngSwitch
*ngFor
*ngFor
trackBy
Built-in control flow can:
- More comfortable syntax, closer to JavaScript and therefore more intuitive and requiring less documentation lookups
- Better type checking thanks to optimized type narrowing
- This is a concept that exists primarily at build time and can reduce the runtime footprint (make it "disappear"), thereby reducing your bundle size by up to 30 KB and further improving your Core Web Vital score
- It will be automatically available in your template without additional import. In the past, structural directives required importing NgIf, NgFor, etc. in the common module.
- Significant performance improvements will be introduced later
Conditional statements
Let's take a look at *ngIf
the comparison with:
<div *ngIf="loggedIn; else anonymousUser">
The user is logged in
</div>
<ng-template #anonymousUser>
The user is not logged in
</ng-template>
Using the built-in if statement, this condition would look like this:
@if (loggedIn) {
The user is logged in
} @else {
The user is not logged in
}
Being able to fill in the template content directly is a major simplification compared to the older alternative's else clause . The current flow of control also makes it possible that was not possible with structural instructions in the past. *ngIf
@else
@else if
*ngSwitch
The improvement effect is more obvious:
<div [ngSwitch]="accessLevel">
<admin-dashboard *ngSwitchCase="admin"/>
<moderator-dashboard *ngSwitchCase="moderator"/>
<user-dashboard *ngSwitchDefault/>
</div>
With built-in control flow, it becomes:
@switch (accessLevel) {
@case ('admin') { <admin-dashboard/> }
@case ('moderator') { <moderator-dashboard/> }
@default { <user-dashboard/> }
}
The new control flow allows for better type narrowing in individual branches, which is not possible with structural instructions. @switch
*ngSwitch
Built-in for loop
One of my favorite updates is the built-in for loop we introduced, which, in addition to improving the development experience, pushes Angular's rendering speed to another level!
Its basic syntax is:
@for (user of users; track user.id) {
{{ user.name }}
} @empty {
Empty list of users
}
We often see performance issues in applications due to *ngFor
missing trackBy
functionality. Some of the differences are that track is mandatory to ensure quick comparison of performance. Additionally, it's easier to use since it's just an expression rather than a method in the component class. The built-in loop also has a shortcut for zero-item collection via optional blocks. @for
@for
@empty
@for
Statements use a new comparison algorithm and have a more optimized implementation compared to , which results in a 90% improvement in running time on the Community Framework benchmark! *ngFor
Give it a try!
Built-in control flow is now available in v17 developer preview!
One of the design goals of the built-in control flow is to enable fully automated migrations. To try it out in an existing project, use the following Schematics one-click migration:
ng generate @angular/core:control-flow
What's next?
You can already use built-in control flow with the latest language services, and we work closely with JetBrains to provide better support in their products. We also contacted Sosuke Suzuki at Prettier to ensure that the Angular templates would be formatted correctly.
*ngIf
There are still some differences in how built - in control flow handles content projection compared to , and we will be working hard to resolve these over the next few months *ngFor
. *ngSwitch
Beyond that, we're confident in the implementation and stability of built-in control flow, so you can try it out today! We want to keep this in Developer Preview until the next major release so that we can work on fixes for potential backwards incompatibilities in case we find an opportunity to further enhance the development experience.
Deferrable views
Now let’s talk about the future of lazy loading! With the new block syntax, we've developed a new powerful mechanism you can use to make your applications faster. At the beginning of the blog post, I said that deferrable views take performance and development experience to the next level because they enable declarative and powerful lazy loading with unprecedented comfort.
Let's say you have a blog and you want to lazy load a list of user comments. Currently, you have to use and manage cleanup, manage loading errors, display placeholders, and a host of complex issues. Handling various edge cases can result in some complex code that will be difficult to test and debug. ViewContainerRef
The new deferrable view allows you to lazy load a list of comments and all of their dependencies with a single line of declarative code:
@defer {
<comment-list />
}
The most incredible part is that this all happens via compile-time transformations: Angular abstracts away all the complexity and the transitions between states by finding the components, directives, and pipes used inside the block, generating dynamic imports, and managing the loading process. switch. @defer
There's a lot more important logic and IntersectionObserver
API involved in starting a lazy-loaded component when a DOM element enters the viewport. Angular makes usage as easy as adding a deferrable view trigger! IntersectionObservers
@defer (on viewport) {
<comment-list />
} @placeholder {
<!-- A placeholder content to show until the comments load -->
<img src="comments-placeholder.png">
}
In the above example, Angular first renders the content of the placeholder block. The component starts loading when it becomes visible in the viewport . Once loaded, Angular removes the placeholder and renders the component. <comment-list/>
There are also blocks for loading and error status:
@defer (on viewport) {
<comment-list/>
} @loading {
Loading…
} @error {
Loading failed :(
} @placeholder {
<img src="comments-placeholder.png">
}
That's it! Angular manages a lot of complex logic for you.
Deferrable views provide additional triggers:
on idle
- Delay loading blocks when the browser isn't doing any heavy liftingon immediate
— Automatically start lazy loading without blocking the browseron timer(<time>)
— Lazy loading using a timeron viewport
and - Viewports also allow specifying references to anchor elements. Angular will lazy load the component and render it when the anchor element is visibleon viewport(<ref>)
on interaction
and — enables you to initiate lazy loading when the user interacts with a specific elementon interaction(<ref>)
on hover
and - trigger lazy loading when the user hovers over the elementon hover(<ref>)
when <expr>
— Enables you to specify your own conditions via an expression that returns a promise
Deferrable views also provide the ability to prefetch dependencies before rendering them. Adding prefetching is as simple as adding statements to a defer block, and all the same triggers are supported. prefetch
@defer (on viewport; prefetch on idle) {
<comment-list />
}
Deferrable views are available in developer preview of v17 today! Learn more about this feature in this guide .
What's next?
Deferrable views are ready to use and we strongly encourage you to give it a try! The reason we keep them in developer preview is so we can collect more feedback and introduce changes in the API until we lock them into following semantic versioning like the rest of the framework.
Currently, server-side rendering will render the specified placeholder. Once the framework loads the application and hydrates it, deferrable views will work as we described above.
Next, we'll explore rendering content inside deferred blocks on the server and enabling partial hydration on the client. In this case, the client does not download the deferred view's code until the trigger requests it. At this point, Angular will download the relevant JavaScript and hydrate only this part of the view.
There will also be a lot of exciting signal interoperability, so stay tuned!
Improved hybrid rendering experience
Today we're ng new
bringing server-side rendering (SSR) and static site generation (SSG or pre-rendering) closer to developers with tips in:
This is a change we've been wanting to make for a long time, but first we wanted the Angular SSR development experience to feel confident.
Otherwise, you can enable SSR in a new project by:
ng new my-app --ssr
Hydration is officially stable from developer preview
In the past 6 months, we've seen thousands of applications using hydration. Today, we’re excited to announce that hydration is no longer in developer preview and is enabled by default in all new apps that use server-side rendering!
New @angular/ssr package
We're moving the Angular Universal repository to the Angular CLI repository and making server-side rendering an even more integral part of our tooling!
Starting today, to add Hybrid rendering support to an existing application simply run:
ng add @angular/ssr
This command will generate the service entry point, add SSR and SSG build functionality, and enable hydration by default. Provides functional equivalents to those currently in maintenance mode . If you are using express-engine, Angular CLI will automatically update your code to . @angular/ssr
@nguniversal/express-engine
@angular/ssr
Virgin Media O2's sales increased by 112% after migrating from the old platform to the latest Angular hybrid rendering solution. By using it in conjunction with Angular SSR with DOM Hydration, cumulative layout offsets were reduced by an average of 99.4%. NgOptimizedImage
Deploy your application using SSR
To further enhance the development experience, we work closely with cloud providers to enable smooth deployment to their platforms.
Firebase will now automatically recognize and deploy your Angular apps with near-zero configuration and provide an early preview of the new framework-aware CLI .
firebase experiments:enable webframeworks
firebase init hosting
firebase deploy
The framework-aware CLI recognizes the use of SSR, i18n, image optimization, and more, enabling you to deliver high-performance web applications on a cost-effective serverless infrastructure.
For those who have complex Angular monorepos or just prefer native tools, AngularFire allows deploying to Firebase using: ng deploy
ng add @angular/fire
ng deploy
To enable deployment by ordinary developers, we enabled ECMAScript module support in Angular's server-side rendering, introduced a fetch backend, and worked with CloudFlare to simplify the process. HttpClient
New lifecycle hooks
To improve the performance of Angular's SSR and SSG, we hope to move away from DOM simulation and direct DOM manipulation in the long term. At the same time, during the life cycle of most applications, they need to interact with elements to instantiate third-party libraries, measure element sizes, etc.
To achieve this, we developed a new set of lifecycle hooks:
afterRender
— Register a callback function to be called each time the application completes renderingafterNextRender
— Register a callback function to be called the next time the application completes rendering. Note that the word Next means next time, which means it will only be executed once.
Only browser-related logic will call these hooks, which allows you to safely insert custom DOM logic directly into the component. For example, if you want to instantiate a charting library, you can use : afterNextRender
@Component({
selector: 'my-chart-cmp',
template: `<div #chart>{{ ... }}</div>`,
})
export class MyChartCmp {
@ViewChild('chart') chartRef: ElementRef;
chart: MyChart|null;
constructor() {
afterNextRender(() => {
this.chart = new MyChart(this.chartRef.nativeElement);
}, {phase: AfterRenderPhase.Write});
}
}
Each hook supports a phase value (e.g. read, write) that Angular will use to schedule callbacks to reduce layout thrashing and improve performance.
New projects use Vite and esbuild by default
Without making underlying changes to the Angular CLI build pipeline, we wouldn't be able to enable SSR in Angular in the first place!
In v16, we introduced developer previews of esbuild and Vite builds. Since then, many developers and some enterprise partners have experimented with it, reducing build times for some of their applications by 67%! Today, we’re excited to announce that the new App Builder is officially stable from Developer Preview and enabled by default for all new apps!
Additionally, we've updated our build pipeline when using hybrid rendering. With SSR and SSG, you can observe an 87% increase in speed and an 80% increase in edit refresh loop speed. ng build
ng serve
The original picture is the animation of the bin file. The current platform does not support it. If you want to view the original text
In a future minor release, we will provide schematics to automatically migrate existing projects using hybrid rendering (client-side rendering using SSG or SSR). If you want to test out the new app builder today, check out this guide in our documentation .
Dependency injection debugging in DevTools
Last year, we showed a preview of dependency injection debugging capabilities in Angular DevTools. Over the past few months, we have implemented a brand new debugging API that allows us to plug into the framework's runtime and inspect the injector tree.
Based on these APIs we built an inspection user interface that allows you to preview the :
Dependencies of your components in the component inspector- Injector tree and dependency resolution path
injector tree and dependency resolution path - Providers declared within the individual
injectors
You can find a quick preview of the features in the animation below . Learn more about Angular DevTools on angular.io . Learn more about Angular DevTools at http://angular.io .
Next, we will refine the UI and work on better visualizing the injector hierarchy, providers, and their resolution.
Provide independent API from the beginning
After spending the past year and a half gathering feedback on independent components, directives, and pipelines and refining the development experience for them, we are confident that we will enable them in all new applications from day one. All ng generate
commands will now build independent components, directives and pipelines.
At the same time, we've also revisited the entire documentation for Angular.io and Angular.dev to ensure a consistent learning experience, development practices, and recommendations .
We will retain NgModules for the foreseeable future, but seeing the benefits of the new independent APIs, we strongly recommend that you gradually migrate your project to them. We also provide a schematic that automates much of the work for you:
ng generate @angular/core:standalone
For more information, check out our migration guide .
What’s next for Reactivity
Angular’s new signal-based reactive system is one of the biggest shifts we’ve made in the framework. To ensure backward compatibility and interoperability with Zone.js-based change detection, we've been hard at work prototyping and designing a path forward.
Today, we are happy to announce that Angular Signals is officially stable from Developer Preview . For now, we'll keep the function in developer preview so we can further iterate on its semantics. effect
Over the next few months, we will begin rolling out features such as signal-based inputs, view queries, and more. By next May, in Angular v18, we will have many features available to further improve the developer experience with Signals.
Next step in testing
We are continuing to experiment with Jest and ensure we build a solution that is performant, flexible, and intuitive that developers need. We also started experimenting with the Web Test Runner and submitted a PR for the initial implementation . In the near future, we may first look at Web Test Runner to unlock projects eager to move away from Karma.
What’s next for Material 3
We've been working hard with Google's Material Design team to refactor the internals of Angular Material to incorporate design tokens , a system that will provide more customization options for components and enable Material 3 support. Although we are not yet ready to provide design token and M3 support for v17, we expect to provide these features in the v17 minor version soon.
In Q4 2022, we announced the launch of new Angular Material components based on MDC and deprecated the old components with the same functionality but different DOM structure and style. We deprecated the old component in v15 and will remove it in v17. Even though they are not part of the Angular Material v17 packages, you can still update your application to Angular v17 and use the v16 Angular Material packages. This will be an option until v18, after which Angular Material v16 will no longer be compatible with newer versions of Angular. We also work with partners at HeroDevs who will provide endless paid support in case you are temporarily unable to perform the migration.
Improved quality of life
In addition to all of these future-proof features, we've also got a bunch of smaller developer experience enhancements off the backlog!
Experimental view transition support
The View Transitions API enables smooth transitions when changing the DOM. In Angular Router, we now provide direct support for this API through functions. With this feature, you cannot use the browser's native capabilities to create animated transitions between routes. withViewTransitions
You can now add this feature to your application by configuring this feature through the router's provider declaration at application startup:
bootstrapApplication(App, {
providers: [
provideRouter(routes, withViewTransitions()),
]
});
withViewTransitions
Accepts an optional configuration object with properties, a callback that gives you some extra control: onViewTransitionCreated
- Decide if you want to skip a specific animation
- Add Classes to the Document to customize the animation and remove these Classes when the animation is complete
- etc.
Automatic pre-connection in image directives
The Angular image directive now automatically generates pre-wired links for domains you provide as arguments to the image loader. The image directive will issue a warning during development if it cannot automatically identify the source and does not detect a pre-connected link to the LCP image.
Learn more about this feature in the Image Directives guide .
Lazy loading animation module
This feature reduces your initial bundle (16KB gzipped) by 60KB. Community contributor Matthieu Riegler proposed and implemented a feature that allows you to lazy load animation modules via an asynchronous provider function:
import { provideAnimationsAsync } from '@angular/platform-browser/animations-async';
bootstrapApplication(RootCmp, {
providers: [provideAnimationsAsync()]
});
Input value transformation
A common pattern is to have components that receive boolean input. However, this places limitations on how values can be passed to such components. For example, if we have the following definition for the Expander component:
@Component({
standalone: true,
selector: 'my-expander',
template: `…`
})
export class Expander {
@Input() expanded: boolean = false;
}
...we tried using it as:
<my-expander expanded/>
You'll get an error "String cannot be assigned to boolean". Input value conversion allows you to solve this problem by configuring the input decorator:
@Component({
standalone: true,
selector: 'my-expander',
template: `…`
})
export class Expander {
@Input({ transform: booleanAttribute }) expanded: boolean = false;
}
The original feature request can be found on GitHub - Boolean Properties as HTML Binary Properties and Boolean Properties as HTML Binary Properties.
styles and styleUrls as strings
Angular components support multiple stylesheets per component. However, the vast majority of the time, when I want to style a component, I create an array that contains a single element that points to an inline style or references an external stylesheet. A new feature lets you switch
@Component({
styles: [`
...
`]
})
...@Component({
styleUrls: ['styles.css']
})...
to something simpler and more logical:
@Component({
styles: `
...
`
})
...@Component({
styleUrl: 'styles.css'
})
...
We still support multiple stylesheets when you use arrays. This is simpler, more intuitive, and works better with automatic formatting tools.
Community Schematics Schematics
To support community schematic development, we provide some practical methods as part of the . You can now import expressions directly into the root of your Angular application and add providers to the root of your Angular application, as well as add dependencies to existing functions. @schematics/angular/utility
package.json
You can learn more in the schematic guide in the documentation .
Angular developer training
We've partnered with interactive EdTech platform SoloLearn to develop new Angular training based on our recently developed ' Introduction to Angular ' course. They created an interactive learning journey that reached over 70,000 people in the past two months!
Learn more in our recent announcement .
Community Highlights
We would like to thank the 346 contributors who make Angular v17 so special! Some highlights we would like to list:
HttpClient
now can use fetch as a backend which is one of the features enabling Angular to run in an edge worker. We'd like to thank to Matthieu Riegler for the help now can use fetch as a backend which is one of the features enabling Angular to run in an edge worker. One of the functions that runs in a thread. We would like to thank Matthieu Riegler for his help
HttpClient
- Matthieu also enabled customization , which allows specifying headers, filters, and caching for post requests
HttpTransferCache
- Cédric Exbrayat introduces support for
namedChunks
- Angular Challenges by Thomas Laforge is an excellent resource that has been helping Angular developers reach the next level
- AnalogJS has been steadily evolving and is approaching 1.0. Congratulations to Brandon Roberts for a job well done!
- Congratulations to Santosh Yadav for hitting 1 million views for his Angular Beginners course
Building the future with Angular
Over the past six months, we've been continuing the Angular renaissance, releasing features to provide a better development experience and performance. Today, we're excited to reflect that momentum in Angular's new brand and angular.dev learning experience.
In the next release cycle, expect a lot of evolution in Angular's signal-based reactive, hybrid rendering and learning journey.
We're honored to be a part of your journey to build your future with Angular! Thank you!