<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Piyush Bhatt</title>
    <description>The latest articles on DEV Community by Piyush Bhatt (@piyushpbhatt).</description>
    <link>https://dev.to/piyushpbhatt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4215686%2F428f7872-4fcb-43eb-b365-5a42b6308a5f.png</url>
      <title>DEV Community: Piyush Bhatt</title>
      <link>https://dev.to/piyushpbhatt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/piyushpbhatt"/>
    <language>en</language>
    <item>
      <title>Angular Best Practices for 2026: What Still Holds and What's New</title>
      <dc:creator>Piyush Bhatt</dc:creator>
      <pubDate>Thu, 29 Oct 2026 16:53:36 +0000</pubDate>
      <link>https://dev.to/piyushpbhatt/angular-best-practices-for-2026-what-still-holds-and-whats-new-2l7a</link>
      <guid>https://dev.to/piyushpbhatt/angular-best-practices-for-2026-what-still-holds-and-whats-new-2l7a</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://www.sharpquest.com/blog/posts/2026/october/angular-best-practices-2026/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=blog-angular-2026" rel="noopener noreferrer"&gt;SharpQuest blog&lt;/a&gt;, written by Greg Kabacinski with our Angular team.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build new apps with standalone components; migrate NgModule apps step by step with the CLI's migrations.&lt;/li&gt;
&lt;li&gt;Use signals for component state (&lt;code&gt;signal&lt;/code&gt;, &lt;code&gt;computed&lt;/code&gt;, &lt;code&gt;input()&lt;/code&gt;, &lt;code&gt;output()&lt;/code&gt;), and RxJS for events and async streams.&lt;/li&gt;
&lt;li&gt;Use the built-in control flow (&lt;code&gt;@if&lt;/code&gt;, &lt;code&gt;@for&lt;/code&gt; with &lt;code&gt;track&lt;/code&gt;) and &lt;code&gt;@defer&lt;/code&gt; to load heavy parts of a page only when needed.&lt;/li&gt;
&lt;li&gt;Make components &lt;code&gt;OnPush&lt;/code&gt;; with signals, new apps can run without zone.js.&lt;/li&gt;
&lt;li&gt;Keep the 2022 basics: small single-purpose files, Prettier, lazy-loaded routes, presentational components, and no leaked subscriptions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2022 we wrote down the &lt;a href="https://www.sharpquest.com/blog/posts/2022/september/angular-best-practices-and-core-concepts-2022/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=blog-angular-2026" rel="noopener noreferrer"&gt;Angular best practices our team had learned&lt;/a&gt; from four years of enterprise apps. Since then Angular has changed more than in any period since Angular 2: standalone components, signals, a new template syntax and a faster build. This post covers what we do on new Angular apps in 2026, and which of our 2022 practices still hold.&lt;/p&gt;

&lt;p&gt;Our Angular work includes a &lt;a href="https://www.sharpquest.com/portfolio/graham-healthcare-group-clinician-pwa-for-homecare/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=blog-angular-2026" rel="noopener noreferrer"&gt;progressive web app for home-care clinicians&lt;/a&gt; at a healthcare provider, and products for a technology startup and an employee-benefits software company.&lt;/p&gt;

&lt;h2&gt;
  
  
  What still holds from 2022
&lt;/h2&gt;

&lt;p&gt;These haven't changed, and we still apply them on every project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One thing per file.&lt;/strong&gt; One component, service or directive per file, named after what it is.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Let tools do the formatting.&lt;/strong&gt; &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; with a pre-commit hook, plus ESLint through &lt;a href="https://github.com/angular-eslint/angular-eslint" rel="noopener noreferrer"&gt;angular-eslint&lt;/a&gt; (&lt;code&gt;ng add @angular-eslint/schematics&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lazy-load features.&lt;/strong&gt; The first download should contain only what the first screen needs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smart and presentational components.&lt;/strong&gt; Screens load data and handle logic; the components inside them just take inputs and raise outputs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't leak subscriptions.&lt;/strong&gt; Use the async pipe, &lt;code&gt;take(1)&lt;/code&gt;, or &lt;code&gt;takeUntilDestroyed()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No expensive work in templates.&lt;/strong&gt; Templates should read values, not compute them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Path aliases&lt;/strong&gt; in &lt;a href="https://www.typescriptlang.org/tsconfig/#paths" rel="noopener noreferrer"&gt;&lt;code&gt;tsconfig.json&lt;/code&gt;&lt;/a&gt; (&lt;code&gt;@core/*&lt;/code&gt;, &lt;code&gt;@shared/*&lt;/code&gt;) instead of long &lt;code&gt;../../../&lt;/code&gt; imports.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What changed is how you do most of these. The rest of this post is the 2026 version.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Standalone components, not NgModules
&lt;/h2&gt;

&lt;p&gt;Since Angular 19, components, directives and pipes are standalone by default. A component imports what it uses directly, and the app starts with &lt;code&gt;bootstrapApplication()&lt;/code&gt; instead of an &lt;code&gt;AppModule&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.ts&lt;/span&gt;
&lt;span class="nf"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nf"&gt;provideRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;withInterceptors&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;authInterceptor&lt;/span&gt;&lt;span class="p"&gt;])),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-order-list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CurrencyPipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;OrderRow&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;templateUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./order-list.html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Existing apps:&lt;/strong&gt; you don't need to rewrite anything. NgModules are still supported, and the two styles work side by side. &lt;a href="https://angular.dev/reference/migrations/standalone" rel="noopener noreferrer"&gt;&lt;code&gt;ng generate @angular/core:standalone&lt;/code&gt;&lt;/a&gt; migrates an app in three passes (convert components, remove unneeded modules, switch to &lt;code&gt;bootstrapApplication&lt;/code&gt;). We run it feature by feature, with a build and a test run between passes.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The new style guide: shorter names
&lt;/h2&gt;

&lt;p&gt;Angular rewrote its &lt;a href="https://angular.dev/style-guide" rel="noopener noreferrer"&gt;style guide&lt;/a&gt; in 2025 (Angular 20). The biggest visible change: no type suffixes for components, directives and services. The CLI now generates &lt;code&gt;order-list.ts&lt;/code&gt; with class &lt;code&gt;OrderList&lt;/code&gt;, not &lt;code&gt;order-list.component.ts&lt;/code&gt; with &lt;code&gt;OrderListComponent&lt;/code&gt;. Other points:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use the &lt;a href="https://angular.dev/guide/di" rel="noopener noreferrer"&gt;&lt;code&gt;inject()&lt;/code&gt; function&lt;/a&gt; instead of constructor injection.&lt;/li&gt;
&lt;li&gt;Keep components focused on presentation; move logic into services or plain functions.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;protected&lt;/code&gt; for members that only the template uses.&lt;/li&gt;
&lt;li&gt;Group files by feature, not by type.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an existing project uses suffixes, keep them there. Consistency within a codebase matters more than following the newest guide.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Signals for state
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://angular.dev/guide/signals" rel="noopener noreferrer"&gt;Signals&lt;/a&gt; are Angular's built-in reactive values. A signal holds a value, a &lt;code&gt;computed()&lt;/code&gt; signal derives one, and Angular updates exactly the parts of the page that read them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Cart&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;signal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
  &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

  &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CartItem&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Total: {{ total() | currency }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling &lt;code&gt;total()&lt;/code&gt; in the template is fine. That's how signals are read, and Angular only re-evaluates it when &lt;code&gt;items&lt;/code&gt; changes. (Our 2022 advice against functions in templates still applies to ordinary methods.)&lt;/p&gt;

&lt;p&gt;Components use signal-based &lt;a href="https://angular.dev/guide/components/inputs" rel="noopener noreferrer"&gt;inputs&lt;/a&gt;, &lt;a href="https://angular.dev/guide/components/outputs" rel="noopener noreferrer"&gt;outputs&lt;/a&gt; and queries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LessonsList&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;lessons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Lesson&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;selected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Lesson&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;           &lt;span class="c1"&gt;// two-way binding&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Practices we follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;computed()&lt;/code&gt; for derived values, not &lt;code&gt;effect()&lt;/code&gt; that sets another signal.&lt;/li&gt;
&lt;li&gt;Keep &lt;code&gt;effect()&lt;/code&gt; for side effects that leave Angular: logging, local storage, a third-party chart.&lt;/li&gt;
&lt;li&gt;Update signals immutably (&lt;code&gt;update(items =&amp;gt; [...items, item])&lt;/code&gt;), so &lt;code&gt;computed()&lt;/code&gt; and &lt;code&gt;track&lt;/code&gt; see the change.&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://angular.dev/guide/signals/linked-signal" rel="noopener noreferrer"&gt;&lt;code&gt;linkedSignal()&lt;/code&gt;&lt;/a&gt; for a value that resets when another changes (for example, the selected item when the list changes).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Signals and RxJS: use both
&lt;/h2&gt;

&lt;p&gt;Signals don't replace &lt;a href="https://rxjs.dev/" rel="noopener noreferrer"&gt;RxJS&lt;/a&gt;. Our rule of thumb:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use signals for&lt;/th&gt;
&lt;th&gt;Use RxJS for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Component and screen state&lt;/td&gt;
&lt;td&gt;HTTP calls, websockets, server events&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Values the template reads&lt;/td&gt;
&lt;td&gt;User input over time (search-as-you-type with &lt;code&gt;debounceTime&lt;/code&gt; + &lt;code&gt;switchMap&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Derived values (&lt;code&gt;computed&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Combining, retrying or cancelling async work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Convert at the boundary with &lt;a href="https://angular.dev/ecosystem/rxjs-interop" rel="noopener noreferrer"&gt;RxJS interop&lt;/a&gt;: &lt;code&gt;toSignal(this.http.get(...))&lt;/code&gt; to show the result in a template, &lt;code&gt;toObservable(signal)&lt;/code&gt; to feed a signal into an RxJS pipeline. For loading data, &lt;a href="https://angular.dev/guide/signals/resource" rel="noopener noreferrer"&gt;&lt;code&gt;resource()&lt;/code&gt; and &lt;code&gt;httpResource()&lt;/code&gt;&lt;/a&gt; put async results straight into signals, with loading and error states built in; they're stable since Angular 22.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Built-in control flow
&lt;/h2&gt;

&lt;p&gt;Angular 17 added &lt;a href="https://angular.dev/guide/templates/control-flow" rel="noopener noreferrer"&gt;control flow blocks&lt;/a&gt; to templates, and they're now the standard. They replace &lt;code&gt;*ngIf&lt;/code&gt;, &lt;code&gt;*ngFor&lt;/code&gt; and &lt;code&gt;*ngSwitch&lt;/code&gt; (deprecated since Angular 20):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@if (user(); as user) {
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Welcome {{ user.name }}&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
} @else {
  &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;routerLink=&lt;/span&gt;&lt;span class="s"&gt;"/login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign in&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
}

@for (order of orders(); track order.id) {
  &lt;span class="nt"&gt;&amp;lt;app-order-row&lt;/span&gt; &lt;span class="na"&gt;[order]=&lt;/span&gt;&lt;span class="s"&gt;"order"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @empty {
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;No orders yet.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@for&lt;/code&gt; requires &lt;code&gt;track&lt;/code&gt;, which fixes the most common list performance problem from our 2022 post. Track by a unique id, not &lt;code&gt;$index&lt;/code&gt;. &lt;a href="https://angular.dev/reference/migrations/control-flow" rel="noopener noreferrer"&gt;&lt;code&gt;ng generate @angular/core:control-flow&lt;/code&gt;&lt;/a&gt; converts existing templates.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. &lt;a class="mentioned-user" href="https://dev.to/defer"&gt;@defer&lt;/a&gt;: lazy-load inside a page
&lt;/h2&gt;

&lt;p&gt;Route-level lazy loading is still the first step (&lt;a href="https://angular.dev/guide/routing/loading-strategies" rel="noopener noreferrer"&gt;&lt;code&gt;loadComponent&lt;/code&gt; for one component, &lt;code&gt;loadChildren&lt;/code&gt; for a feature's routes&lt;/a&gt;). &lt;a href="https://angular.dev/guide/templates/defer" rel="noopener noreferrer"&gt;&lt;code&gt;@defer&lt;/code&gt;&lt;/a&gt; goes further and lazy-loads part of a template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;@defer (on viewport) {
  &lt;span class="nt"&gt;&amp;lt;app-sales-chart&lt;/span&gt; &lt;span class="na"&gt;[data]=&lt;/span&gt;&lt;span class="s"&gt;"sales()"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
} @placeholder {
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"chart-placeholder"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
} @loading (minimum 300ms) {
  &lt;span class="nt"&gt;&amp;lt;app-spinner&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The chart's code isn't downloaded until it scrolls into view. Good candidates: charts, rich text editors, maps, and anything below the fold or behind a tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Change detection: OnPush and zoneless
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Make every component &lt;a href="https://angular.dev/best-practices/skipping-subtrees" rel="noopener noreferrer"&gt;&lt;code&gt;OnPush&lt;/code&gt;&lt;/a&gt;.&lt;/strong&gt; With signals and inputs, it's almost free, and it stops Angular from checking components whose data hasn't changed. Set it as the CLI default in &lt;code&gt;angular.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://angular.dev/guide/zoneless" rel="noopener noreferrer"&gt;Zoneless&lt;/a&gt;.&lt;/strong&gt; Angular no longer needs zone.js to know when to update the page; signals, events and the async pipe tell it directly. Zoneless is the default for new apps since Angular 21. For an existing app, remove zone.js only after its components are OnPush and signal-based, then test the screens that update after timers or third-party callbacks.&lt;/li&gt;
&lt;li&gt;Large lists: use &lt;code&gt;@for&lt;/code&gt; with &lt;code&gt;track&lt;/code&gt;, and the &lt;a href="https://material.angular.dev/cdk/scrolling/overview" rel="noopener noreferrer"&gt;CDK's virtual scrolling&lt;/a&gt; (&lt;code&gt;cdk-virtual-scroll-viewport&lt;/code&gt;) for thousands of rows.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. State management: start simple
&lt;/h2&gt;

&lt;p&gt;Most business apps don't need a state library on day one. What we use:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App size&lt;/th&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A few screens with their own data&lt;/td&gt;
&lt;td&gt;Signals in the component&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State shared between screens&lt;/td&gt;
&lt;td&gt;A service holding signals (private &lt;code&gt;signal&lt;/code&gt;, public &lt;code&gt;asReadonly()&lt;/code&gt; and &lt;code&gt;computed&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large apps, many teams, complex shared state&lt;/td&gt;
&lt;td&gt;
&lt;a href="https://ngrx.io/guide/signals/signal-store" rel="noopener noreferrer"&gt;NgRx Signal Store&lt;/a&gt;; classic &lt;a href="https://ngrx.io/" rel="noopener noreferrer"&gt;NgRx&lt;/a&gt; Store where an app already uses it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;NgRx's older &lt;a href="https://ngrx.io/guide/component-store" rel="noopener noreferrer"&gt;Component Store&lt;/a&gt; is still supported, but for new code we use Signal Store.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Forms
&lt;/h2&gt;

&lt;p&gt;In existing code, use &lt;a href="https://angular.dev/guide/forms/typed-forms" rel="noopener noreferrer"&gt;typed reactive forms&lt;/a&gt; (since Angular 14): &lt;code&gt;FormControl&lt;/code&gt; catches mistakes at compile time that untyped forms only show at runtime. Use &lt;code&gt;nonNullable&lt;/code&gt; controls so &lt;code&gt;reset()&lt;/code&gt; doesn't produce &lt;code&gt;null&lt;/code&gt;. For new forms, &lt;a href="https://angular.dev/guide/forms/signals/overview" rel="noopener noreferrer"&gt;Signal Forms&lt;/a&gt; build the form model on signals and are stable since Angular 22.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Build, SSR and performance
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build:&lt;/strong&gt; the &lt;code&gt;application&lt;/code&gt; builder (&lt;a href="https://esbuild.github.io/" rel="noopener noreferrer"&gt;esbuild&lt;/a&gt; and &lt;a href="https://vite.dev/" rel="noopener noreferrer"&gt;Vite&lt;/a&gt;) is the default. &lt;a href="https://angular.dev/tools/cli/build-system-migration" rel="noopener noreferrer"&gt;Moving an older app off the webpack builder&lt;/a&gt; usually cuts build times a lot. &lt;code&gt;ng update&lt;/code&gt; offers the migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server-side rendering:&lt;/strong&gt; for public, search-indexed pages, &lt;a href="https://angular.dev/guide/ssr" rel="noopener noreferrer"&gt;&lt;code&gt;@angular/ssr&lt;/code&gt;&lt;/a&gt; with hydration (and incremental hydration with &lt;code&gt;@defer&lt;/code&gt;) gives a faster first paint and better SEO. Internal business apps behind a login usually don't need it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Images:&lt;/strong&gt; &lt;a href="https://angular.dev/guide/image-optimization" rel="noopener noreferrer"&gt;&lt;code&gt;NgOptimizedImage&lt;/code&gt;&lt;/a&gt; (&lt;code&gt;ngSrc&lt;/code&gt;) sets sizes, lazy-loads below the fold and prioritizes the main image.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budgets:&lt;/strong&gt; keep the bundle budgets in &lt;code&gt;angular.json&lt;/code&gt; and fail the build when they're exceeded.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  11. Testing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit tests:&lt;/strong&gt; &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt; is the default test runner for new CLI projects (see Angular's &lt;a href="https://angular.dev/guide/testing" rel="noopener noreferrer"&gt;testing guide&lt;/a&gt;); Karma is still supported for existing ones. Test components through their template (what the user sees), not their private methods.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signals in tests:&lt;/strong&gt; set inputs with &lt;code&gt;fixture.componentRef.setInput()&lt;/code&gt;, then check the rendered output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;End-to-end:&lt;/strong&gt; &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; or &lt;a href="https://www.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; for the main user flows. Protractor is gone.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  12. Security basics
&lt;/h2&gt;

&lt;p&gt;Angular &lt;a href="https://angular.dev/best-practices/security" rel="noopener noreferrer"&gt;escapes values in templates by default&lt;/a&gt;, so most cross-site scripting needs a bypass to happen. Don't use &lt;code&gt;bypassSecurityTrust*&lt;/code&gt; with user data, keep HTTP calls on &lt;code&gt;HttpClient&lt;/code&gt; so its XSRF support works, and set a Content Security Policy.&lt;/p&gt;

&lt;h2&gt;
  
  
  13. Keep up with releases
&lt;/h2&gt;

&lt;p&gt;Since Angular 22, a new major version comes out every 12 months (it was every 6 months before), and each one gets about 24 months of support: 12 months of active support, then 12 months of long-term support (see &lt;a href="https://angular.dev/reference/releases" rel="noopener noreferrer"&gt;versioning and releases&lt;/a&gt;). Upgrade at least once a year; skipping several versions is how a routine &lt;code&gt;ng update&lt;/code&gt; becomes a project. &lt;a href="https://angular.dev/cli/update" rel="noopener noreferrer"&gt;&lt;code&gt;ng update&lt;/code&gt;&lt;/a&gt; runs migrations for you, one major version at a time, and Angular's &lt;a href="https://angular.dev/update-guide" rel="noopener noreferrer"&gt;Update guide&lt;/a&gt; lists the manual steps for each version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What are the most important Angular best practices in 2026?
&lt;/h3&gt;

&lt;p&gt;Standalone components, signals for state, the built-in control flow with &lt;code&gt;track&lt;/code&gt;, &lt;code&gt;@defer&lt;/code&gt; and route lazy loading, OnPush change detection, and typed or signal-based forms. The basics still apply: small files, Prettier and ESLint, and presentational components.&lt;/p&gt;

&lt;h3&gt;
  
  
  Should I still use NgModules?
&lt;/h3&gt;

&lt;p&gt;Not for new code. Existing NgModule apps are supported and can move to standalone gradually with &lt;code&gt;ng generate @angular/core:standalone&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do signals replace RxJS?
&lt;/h3&gt;

&lt;p&gt;No. Use signals for state the template reads and RxJS for async streams such as HTTP calls and user input. &lt;code&gt;toSignal()&lt;/code&gt; and &lt;code&gt;toObservable()&lt;/code&gt; connect them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need NgRx?
&lt;/h3&gt;

&lt;p&gt;Usually not at the start. Signals in components and services cover most business apps. Consider NgRx Signal Store when shared state grows across many screens or teams.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often should we upgrade Angular?
&lt;/h3&gt;

&lt;p&gt;At least once a year. A new major version comes out every 12 months and is supported for about 24 months, and &lt;code&gt;ng update&lt;/code&gt; upgrades one major version at a time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is it worth migrating an existing Angular app to signals and standalone?
&lt;/h3&gt;

&lt;p&gt;Yes, gradually. The CLI migrations do most of the mechanical work; plan the time for testing. Start with the screens you're already changing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;SharpQuest builds Angular front ends for business applications and upgrades older Angular apps, including AngularJS, to current versions. More on that in our &lt;a href="https://www.sharpquest.com/application-modernization/?utm_source=devto&amp;amp;utm_medium=referral&amp;amp;utm_campaign=blog-angular-2026" rel="noopener noreferrer"&gt;application modernization&lt;/a&gt; work.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>typescript</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
