<?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: Abdul Rashid</title>
    <description>The latest articles on DEV Community by Abdul Rashid (@abdeltek).</description>
    <link>https://dev.to/abdeltek</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%2F2307236%2F17d43455-6737-4930-ab2b-a772667ab6cb.jpg</url>
      <title>DEV Community: Abdul Rashid</title>
      <link>https://dev.to/abdeltek</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abdeltek"/>
    <language>en</language>
    <item>
      <title>Complete guide to Angular lazy loading in 2026</title>
      <dc:creator>Abdul Rashid</dc:creator>
      <pubDate>Thu, 02 Jul 2026 11:35:18 +0000</pubDate>
      <link>https://dev.to/abdeltek/complete-guide-to-angular-lazy-loading-in-2026-m13</link>
      <guid>https://dev.to/abdeltek/complete-guide-to-angular-lazy-loading-in-2026-m13</guid>
      <description>&lt;p&gt;Lazy loading is one of the highest-leverage performance techniques in Angular. Done well, it can cut your initial bundle by 40–60%, dramatically improve Time to Interactive, and make your app feel fast even as it grows. Done poorly, it becomes a source of subtle bugs, missed splits, and false confidence.&lt;/p&gt;

&lt;p&gt;This guide covers everything, from the fundamentals of route-level splitting to &lt;code&gt;@defer&lt;/code&gt; blocks, preloading strategies, and bundle auditing, using the patterns that work in Angular 17+ with standalone components.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is lazy loading and why it matters
&lt;/h2&gt;

&lt;p&gt;When Angular compiles your app, it bundles everything into JavaScript chunks. Without lazy loading, every component, service, and library ships in one initial bundle. The browser must download, parse, and execute all of it before the user sees anything interactive.&lt;/p&gt;

&lt;p&gt;Lazy loading breaks the app into smaller chunks that are fetched on demand , when the user navigates to a route, or when a UI element enters the viewport. The browser only pays for what the user actually needs.&lt;/p&gt;

&lt;p&gt;The numbers matter. A 1-second improvement in load time improves conversion rates by roughly 2–5% on average. For mobile users on slower connections, the impact is even larger.&lt;/p&gt;

&lt;h2&gt;
  
  
  Route-level splitting: NgModule vs standalone components
&lt;/h2&gt;

&lt;p&gt;Route-level lazy loading is the most impactful place to start. It ensures that each feature of your app ships its own bundle, loaded only when the user navigates there.&lt;/p&gt;

&lt;h3&gt;
  
  
  The old NgModule approach
&lt;/h3&gt;

&lt;p&gt;Historically, Angular lazy loading required a dedicated NgModule per feature:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&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;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard/dashboard.module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DashboardModule&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;p&gt;This worked, but it added ceremony. Every feature needed a module wrapper purely to enable lazy loading, even when the module served no other purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  The modern standalone approach
&lt;/h3&gt;

&lt;p&gt;Angular 14+ introduced &lt;code&gt;loadComponent&lt;/code&gt;, and since Angular 17 all new projects scaffold as standalone by default. You can lazy load a component directly — no module required:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;:&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;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dashboard/dashboard.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DashboardComponent&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;p&gt;For feature areas with multiple routes, use &lt;code&gt;loadChildren&lt;/code&gt; with a standalone routes array instead of a module:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;settings&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&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;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./settings/settings.routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SETTINGS_ROUTES&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;p&gt;Where &lt;code&gt;settings.routes.ts&lt;/code&gt; exports a plain routes array:&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;const&lt;/span&gt; &lt;span class="nx"&gt;SETTINGS_ROUTES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&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="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SettingsLayoutComponent&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;profile&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;:&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;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./profile/profile.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ProfileComponent&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;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;billing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;loadComponent&lt;/span&gt;&lt;span class="p"&gt;:&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;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./billing/billing.component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BillingComponent&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;p&gt;This pattern is cleaner, more tree-shakeable, and gives the bundle analyser a clearer picture of what belongs to each feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring lazy routes with the Angular router
&lt;/h2&gt;

&lt;p&gt;A few router options are worth understanding when working with lazy routes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Preloading strategy
&lt;/h3&gt;

&lt;p&gt;By default, Angular loads lazy chunks only when the user navigates to them. You can instruct the router to preload chunks in the background after the initial load:&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="nf"&gt;bootstrapApplication&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&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;withPreloading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PreloadAllModules&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;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;PreloadAllModules&lt;/code&gt; preloads every lazy chunk after the initial bundle is stable. This is a reasonable default for most apps. For more control, write a custom preloading strategy (covered below).&lt;/p&gt;

&lt;h3&gt;
  
  
  Router initial navigation
&lt;/h3&gt;

&lt;p&gt;Set &lt;code&gt;withRouterConfig({ initialNavigation: 'enabledBlocking' })&lt;/code&gt; when using SSR to ensure the first navigation completes before the app hands off to the client. Without it, you can get a flash of blank content during hydration.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;@defer&lt;/code&gt; blocks: sub-route and UI-level lazy loading
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@defer&lt;/code&gt; was introduced in Angular 17 and is one of the most significant performance primitives Angular has ever shipped. It brings lazy loading down to the template level — individual UI blocks can be deferred independently of routing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Basic usage
&lt;/h3&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-heavy-chart&lt;/span&gt; &lt;span class="na"&gt;[data]=&lt;/span&gt;&lt;span class="s"&gt;"chartData"&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-skeleton"&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;
} @error {
  &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Failed to load chart.&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;Angular automatically code-splits the &lt;code&gt;app-heavy-chart&lt;/code&gt; component into its own chunk. The chunk is not fetched until the trigger fires.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in triggers
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Trigger&lt;/th&gt;
&lt;th&gt;When it fires&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on viewport&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Element enters the visible viewport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on idle&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Browser reports an idle period&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on interaction&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;User clicks or focuses the element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on timer(2s)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After a fixed delay&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;on immediate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;As soon as possible after render&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;when condition&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When a signal or expression becomes truthy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Practical patterns
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Defer below-the-fold sections.&lt;/strong&gt; If a page has a hero section and then analytics charts, defer everything below the fold with &lt;code&gt;on viewport&lt;/code&gt;. Users see the hero instantly; charts load as they scroll.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Defer dialog content.&lt;/strong&gt; A dialog component and all its dependencies do not need to be in the initial bundle. Wrap the dialog content in &lt;code&gt;@defer (when dialogOpen())&lt;/code&gt; where &lt;code&gt;dialogOpen&lt;/code&gt; is a signal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Combine with signals for on-demand loading:&lt;/strong&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="k"&gt;protected&lt;/span&gt; &lt;span class="nx"&gt;showEditor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;button&lt;/span&gt; &lt;span class="na"&gt;(click)=&lt;/span&gt;&lt;span class="s"&gt;"showEditor.set(true)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Open editor&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;

@defer (when showEditor()) {
  &lt;span class="nt"&gt;&amp;lt;app-rich-text-editor&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 editor bundle is not fetched until the user explicitly asks for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preloading strategies: PreloadAllModules vs custom
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PreloadAllModules&lt;/code&gt; is convenient but blunt — it preloads everything regardless of whether the user is likely to visit those routes. For large apps, a custom strategy that preloads based on user behaviour or route metadata is worth the investment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Custom preloading strategy
&lt;/h3&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;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&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;SelectivePreloadingStrategy&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;PreloadingStrategy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;:&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;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&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;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;?.[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&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;p&gt;Mark routes you want preloaded:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reports&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;loadChildren&lt;/span&gt;&lt;span class="p"&gt;:&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;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./reports/reports.routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;REPORTS_ROUTES&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;Register the strategy:&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="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;withPreloading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SelectivePreloadingStrategy&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you per-route control. Preload the routes users commonly visit next; leave the rest on-demand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Analysing bundle splits with Angular DevTools
&lt;/h2&gt;

&lt;p&gt;Writing lazy routes is only half the job. You need to verify that splits are actually happening and identify what is inside each chunk.&lt;/p&gt;

&lt;h3&gt;
  
  
  Angular DevTools network tab
&lt;/h3&gt;

&lt;p&gt;Install the Angular DevTools browser extension. Open DevTools, navigate to the Angular panel, and watch the network tab as you navigate. Each lazy route should produce a separate network request for a JavaScript chunk.&lt;/p&gt;

&lt;p&gt;If a route you expected to be lazy loads as part of the initial bundle, the most common cause is a direct import somewhere in your eagerly loaded code. A single stray &lt;code&gt;import { FeatureComponent }&lt;/code&gt; in an eager component will pull the entire feature into the main bundle.&lt;/p&gt;

&lt;h3&gt;
  
  
  Source map explorer
&lt;/h3&gt;

&lt;p&gt;After building with source maps enabled, &lt;code&gt;source-map-explorer&lt;/code&gt; gives you a visual treemap of every bundle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--source-map&lt;/span&gt;
npx source-map-explorer dist/app/browser/&lt;span class="k"&gt;*&lt;/span&gt;.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unexpectedly large chunks&lt;/li&gt;
&lt;li&gt;Third-party libraries appearing in feature chunks instead of the shared vendor chunk&lt;/li&gt;
&lt;li&gt;Components appearing in the wrong bundle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bundle budgets
&lt;/h3&gt;

&lt;p&gt;Set hard limits in &lt;code&gt;angular.json&lt;/code&gt; to catch regressions in CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"budgets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"initial"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maximumWarning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"400kb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maximumError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"500kb"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"anyComponentStyle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maximumWarning"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4kb"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"maximumError"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8kb"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a budget is exceeded, the build fails. This is the safest way to prevent bundle regressions from sneaking into production.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-world case study: 40% bundle reduction walkthrough
&lt;/h2&gt;

&lt;p&gt;Here is a typical audit pattern from a mid-size Angular app that was not using lazy loading strategically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Starting state:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial bundle: 820 kb (gzipped)&lt;/li&gt;
&lt;li&gt;All routes eagerly loaded&lt;/li&gt;
&lt;li&gt;Angular Material imported via a shared &lt;code&gt;MaterialModule&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lodash&lt;/code&gt; imported as &lt;code&gt;import _ from 'lodash'&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Lazy load all feature routes (saved 210 kb)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The admin panel, reports section, and settings area were all loading eagerly. Converting them to &lt;code&gt;loadChildren&lt;/code&gt; with standalone routes arrays removed 210 kb from the initial bundle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Switch to lodash-es with tree shaking (saved 65 kb)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Replacing &lt;code&gt;import _ from 'lodash'&lt;/code&gt; with individual imports from &lt;code&gt;lodash-es&lt;/code&gt; reduced the bundle by 65 kb. The standard lodash build is not tree-shakeable; &lt;code&gt;lodash-es&lt;/code&gt; is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Replace MaterialModule with per-component imports (saved 48 kb)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A shared &lt;code&gt;MaterialModule&lt;/code&gt; re-exported every Angular Material module. Switching to per-component imports so each component only imported the Material modules it actually needed removed 48 kb.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 4: Defer analytics and charts (saved 95 kb from initial)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Three Chart.js-powered components on the dashboard were moved into &lt;code&gt;@defer (on viewport)&lt;/code&gt; blocks. The Chart.js library moved out of the initial bundle entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final initial bundle: 402 kb&lt;/strong&gt; — a 51% reduction, achieved through incremental audit steps over two days.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checklist: auditing your app's lazy loading
&lt;/h2&gt;

&lt;p&gt;Use this as a starting point for your own audit:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Routes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Every major feature area uses &lt;code&gt;loadChildren&lt;/code&gt; or &lt;code&gt;loadComponent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] No stray direct imports of lazy components in eager code&lt;/li&gt;
&lt;li&gt;[ ] Lazy chunks are visible in the DevTools network panel on navigation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;@defer&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Heavy components (charts, editors, maps) are wrapped in &lt;code&gt;@defer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Below-the-fold sections use &lt;code&gt;on viewport&lt;/code&gt; trigger&lt;/li&gt;
&lt;li&gt;[ ] Dialog and modal content is deferred until opened&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;lodash&lt;/code&gt; replaced with &lt;code&gt;lodash-es&lt;/code&gt; or individual function imports&lt;/li&gt;
&lt;li&gt;[ ] &lt;code&gt;moment.js&lt;/code&gt; replaced with &lt;code&gt;date-fns&lt;/code&gt; or native &lt;code&gt;Intl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Icon libraries use individual SVG imports, not full icon sets&lt;/li&gt;
&lt;li&gt;[ ] Angular Material uses per-component imports, not a shared module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Build config&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Bundle budgets set in &lt;code&gt;angular.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Source map explorer run and reviewed&lt;/li&gt;
&lt;li&gt;[ ] CI pipeline fails on budget violations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Preloading&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] A preloading strategy is configured&lt;/li&gt;
&lt;li&gt;[ ] High-traffic routes are preloaded; low-traffic routes are not&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Lazy loading in Angular in 2026 is more capable and more ergonomic than it has ever been. The combination of standalone &lt;code&gt;loadComponent&lt;/code&gt;, &lt;code&gt;@defer&lt;/code&gt; blocks, and the esbuild-based build pipeline gives you fine-grained control over exactly what ships in each chunk.&lt;/p&gt;

&lt;p&gt;The key mindset shift is treating bundle size as a metric you actively monitor, not a side effect you occasionally think about. Set budgets, run source-map-explorer monthly, and treat an unexpectedly growing initial bundle as a bug worth fixing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this useful? Follow &lt;a href="https://dev.to/abdeltek"&gt;Abdul-Rashid&lt;/a&gt; for more mid-to-expert Angular content every week. Next up: a deep dive into &lt;code&gt;@defer&lt;/code&gt; triggers and combining them with Angular Signals for on-demand UI loading.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>performance</category>
      <category>lazyloading</category>
    </item>
    <item>
      <title>The Immortal Hack: Architecting Around ::ng-deep in Enterprise Angular Applications</title>
      <dc:creator>Abdul Rashid</dc:creator>
      <pubDate>Wed, 17 Jun 2026 10:37:55 +0000</pubDate>
      <link>https://dev.to/abdeltek/the-immortal-hack-architecting-around-ng-deep-in-enterprise-angular-applications-5am8</link>
      <guid>https://dev.to/abdeltek/the-immortal-hack-architecting-around-ng-deep-in-enterprise-angular-applications-5am8</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/abdeltek/the-undead-selector-why-ng-deep-refuses-to-die-in-modern-angular-3ml2" class="crayons-story__hidden-navigation-link"&gt;The Undead Selector: Why ::ng-deep Refuses to Die in Modern Angular&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/abdeltek" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2307236%2F17d43455-6737-4930-ab2b-a772667ab6cb.jpg" alt="abdeltek profile" class="crayons-avatar__image" width="800" height="1422"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/abdeltek" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Abdul Rashid
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Abdul Rashid
                
              
              &lt;div id="story-author-preview-content-3923091" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/abdeltek" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2307236%2F17d43455-6737-4930-ab2b-a772667ab6cb.jpg" class="crayons-avatar__image" alt="" width="800" height="1422"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Abdul Rashid&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/abdeltek/the-undead-selector-why-ng-deep-refuses-to-die-in-modern-angular-3ml2" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 17&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/abdeltek/the-undead-selector-why-ng-deep-refuses-to-die-in-modern-angular-3ml2" id="article-link-3923091"&gt;
          The Undead Selector: Why ::ng-deep Refuses to Die in Modern Angular
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/angular"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;angular&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/stylesheet"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;stylesheet&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/modernangular"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;modernangular&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/abdeltek/the-undead-selector-why-ng-deep-refuses-to-die-in-modern-angular-3ml2" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;4&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/abdeltek/the-undead-selector-why-ng-deep-refuses-to-die-in-modern-angular-3ml2#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              2&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>angular</category>
      <category>architecture</category>
      <category>css</category>
      <category>frontend</category>
    </item>
    <item>
      <title>The Undead Selector: Why ::ng-deep Refuses to Die in Modern Angular</title>
      <dc:creator>Abdul Rashid</dc:creator>
      <pubDate>Wed, 17 Jun 2026 10:36:40 +0000</pubDate>
      <link>https://dev.to/abdeltek/the-undead-selector-why-ng-deep-refuses-to-die-in-modern-angular-3ml2</link>
      <guid>https://dev.to/abdeltek/the-undead-selector-why-ng-deep-refuses-to-die-in-modern-angular-3ml2</guid>
      <description>&lt;p&gt;Style encapsulation is a core feature of Angular. It gives us the peace of mind that our component styles will not leak out and ruin the rest of the application. But what happens when you need to pierce that shield? For years, developers used ::ng-deep while feeling a pang of guilt because of the "deprecated" label next to it in the documentation.&lt;br&gt;
Then, Angular v18 arrived, and the framework team did something unexpected: they officially removed the deprecated status of ::ng-deep.&lt;/p&gt;

&lt;p&gt;This article explores the technical reality behind this decision, how the selector works at the compiler level, and the architectural design patterns you should use instead.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. The "Un-Deprecation" Event
&lt;/h2&gt;

&lt;p&gt;In early 2024, the Angular team accepted GitHub Pull Request #54219. This PR altered the official &lt;a href="https://angular.dev/guide/components/styling" rel="noopener noreferrer"&gt;Angular Styling Guide&lt;/a&gt; to remove all references to the deprecation of ::ng-deep. Instead, the text was rewritten to state that the API remains active exclusively for backwards compatibility.&lt;/p&gt;

&lt;p&gt;//Old Docs Warning:&lt;br&gt;
"::ng-deep is deprecated and will be removed in a future version."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyx48owag8bcfltm1jg3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foyx48owag8bcfltm1jg3.png" alt="angular 17 doc on ng-deep" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;//New Docs Reality:&lt;br&gt;
"The Angular team strongly discourages new use of ::ng-deep. &lt;br&gt;
 These APIs remain exclusively for backwards compatibility."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gtyh3n0nopym3licues.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1gtyh3n0nopym3licues.png" alt="angular 22 doc on ng-deep" width="727" height="250"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Did Angular Change Its Mind?
&lt;/h2&gt;

&lt;p&gt;The history of this selector comes down to web standards:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Original Plan: When Angular v4.3 introduced ::ng-deep, it was meant as a temporary patch. The W3C was working on native shadow-piercing CSS specifications like /deep/ and &amp;gt;&amp;gt;&amp;gt;.&lt;/li&gt;
&lt;li&gt;The Spec Failure: Browser vendors completely dropped those shadow-piercing specifications due to massive performance penalties.&lt;/li&gt;
&lt;li&gt;The Pragmatic Choice: Because the browser standards failed to deliver a native replacement, Angular developers had no clean way to style internal pieces of third-party user interface libraries (like Angular Material).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Angular team chose pragmatism over ideology. They acknowledged that removing the selector would completely break thousands of enterprise applications.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Under the Hood: How the Compiler Processes ::ng-deep
&lt;/h2&gt;

&lt;p&gt;To understand the risk of ::ng-deep, you must understand what the Angular template compiler does with your CSS files.&lt;br&gt;
By default, Angular uses Emulated View Encapsulation. The compiler modifies your HTML elements and CSS selectors by appending a unique host attribute id.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Component Code
&lt;/h2&gt;

&lt;p&gt;Imagine you write a component stylesheet targeting an item inside a child component:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq86t9lxrd22b3xi4wogn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq86t9lxrd22b3xi4wogn.png" alt="ng-deep usage example" width="772" height="482"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Compiled Output
&lt;/h2&gt;

&lt;p&gt;The Angular compiler turns that code into highly specific CSS:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqi9pzk35fbgbyzcdg7se.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqi9pzk35fbgbyzcdg7se.png" alt="ng-deep complied output" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because the .mat-select element inside the third-party library does not have that specific _ngcontent-c10 attribute on it, the style fails to apply.&lt;/p&gt;
&lt;h2&gt;
  
  
  Enter the Shadow Piercer
&lt;/h2&gt;

&lt;p&gt;When you append ::ng-deep, it acts as a special instruction to the CSS parser. It tells the compiler: "Stop appending attribute identifiers to anything after this token."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F600q3egtwjpxw53wt1w9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F600q3egtwjpxw53wt1w9.png" alt="example of ng-deep with host" width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The compiler evaluates this rule and outputs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqevabwt2cn0rbosm3n3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqevabwt2cn0rbosm3n3z.png" alt="complied output with host" width="800" height="339"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, any .mat-select that sits inside .custom-panel will get the style change, regardless of what component owns that HTML structure.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. The Architectural Risks of Style Bleeding
&lt;/h2&gt;

&lt;p&gt;The mechanics of the compiler show why using ::ng-deep carelessly introduces major bugs. Look closely at what happens if you forget to scope your selector:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjynw2ahde1o517ukqy4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkjynw2ahde1o517ukqy4.png" alt="careless example" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The compiler removes the scope completely and outputs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfi6j76mlon91dcrgvyd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgfi6j76mlon91dcrgvyd.png" alt="complied careless exmaple" width="772" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is now a pure global style rule.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When this component renders on your page, Angular appends this style tag directly into the  element of the document.&lt;/li&gt;
&lt;li&gt;If a user navigates away from this component to another section of the application, the style tag remains in the DOM.&lt;/li&gt;
&lt;li&gt;Every single dropdown component across your entire application turns ghostwhite, destroying your global style system.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  4. Modern Alternatives
&lt;/h2&gt;

&lt;p&gt;Senior developers should avoid using ::ng-deep for internal application components. Modern layout design offers cleaner patterns to bypass encapsulation boundaries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdyxe8frya1k79haklpb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frdyxe8frya1k79haklpb.png" alt="modern example" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Option A: CSS Custom Properties (The Gold Standard)
&lt;/h3&gt;

&lt;p&gt;CSS custom properties (variables) pass straight through emulated view encapsulation boundaries effortlessly. If you control the child component, design it to receive configuration variables from its parent components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhuju8xznyyce869a2um1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhuju8xznyyce869a2um1.png" alt="css child example" width="800" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, the parent component can cleanly re-theme the child component without piercing the encapsulation wall:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0m5hvtw57dpf99cjnua.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm0m5hvtw57dpf99cjnua.png" alt="css parent example" width="726" height="444"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Option B: Angular Material Design Tokens
&lt;/h3&gt;

&lt;p&gt;Modern UI libraries like Angular Material have abandoned deep CSS classes in favor of the W3C Design Tokens Community Group specification. Instead of overriding .mat-mdc-text-field-wrapper with a compiler hack, you provide themes directly through Sass mixins in your global stylesheets:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="c1"&gt;// styles.scss@use '@angular/material' as mat;&lt;/span&gt;

&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;theme-overrides&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
    &lt;span class="n"&gt;primary&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define-palette&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mat&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$blue-palette&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&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;h3&gt;
  
  
  Option C: Explicit Global Stylesheets
&lt;/h3&gt;

&lt;p&gt;If you must write a global style rule to target a dynamic overlay panel (like a modal popup backdrop), do not bury it inside a component CSS file. Place it inside your main styles.scss array or a dedicated overrides.scss module. This ensures that your global overrides stay highly visible and well-organized in one central place.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Strict Rules for Using ::ng-deep
&lt;/h2&gt;

&lt;p&gt;If you hit a legacy roadblock where you absolutely must use ::ng-deep to meet a feature deadline, follow these safety guidelines:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Always Prepend :host: Never leave ::ng-deep at the root of a selector rule. Always scope it with :host to prevent styles from bleeding out across the rest of your app.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="c"&gt;/* Correct Scope */&lt;/span&gt;
   &lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="nd"&gt;::ng-deep&lt;/span&gt; &lt;span class="nc"&gt;.third-party-widget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="c"&gt;/* Global Leak Danger */&lt;/span&gt;
   &lt;span class="nd"&gt;::ng-deep&lt;/span&gt; &lt;span class="nc"&gt;.third-party-widget&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Add a Technical Debt Comment: Explain exactly why the alternative approaches failed so team members can refactor it later.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt; &lt;span class="nd"&gt;:host&lt;/span&gt; &lt;span class="nd"&gt;::ng-deep&lt;/span&gt; &lt;span class="nc"&gt;.vendor-chart-line&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="c"&gt;/* TODO: Remove once vendor library supports CSS variables */&lt;/span&gt;
     &lt;span class="py"&gt;stroke&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#00ff00&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;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The removal of the "deprecated" label from ::ng-deep in &lt;a href="https://blog.angular.dev/angular-v18-is-now-available-e79d5ac0affe" rel="noopener noreferrer"&gt;Angular v18&lt;/a&gt; is not an invitation to write sloppy, unencapsulated global CSS. It is simply an acknowledgment of the technical realities of modern web development. Use it strictly as a specialized tool for targeting third-party components, and rely on CSS custom properties for your internal application layout architecture.&lt;/p&gt;

&lt;p&gt;Reference: &lt;br&gt;
&lt;a href="https://angular.dev/guide/components/styling#ng-deep" rel="noopener noreferrer"&gt;Angular ng-deep&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/angular/angular/issues/19494" rel="noopener noreferrer"&gt;Angular Github Issues&lt;/a&gt;&lt;/p&gt;

</description>
      <category>angular</category>
      <category>webdev</category>
      <category>stylesheet</category>
      <category>modernangular</category>
    </item>
  </channel>
</rss>
