How the New Folder Structure Changes Everything
If you haven't updated to version 20, the first and second parts of this guide can help you understand what's changing and how to update.
Part 1: The Upgrade Itself π οΈ
First, let's get the basics out of the way. Before running any commands, make sure your environment is ready.
Prerequisites
- Node.js: v20.11.1 or later.
- TypeScript: v5.8 or later.
- Project Backup: Make sure you commit all your current changes in Git. Seriously.
The Update Command
Once you've confirmed your Node.js version, run the command that fits your project.
For a standard Angular project:
ng update @angular/cli @angular/core
If you use Angular Material:
ng update @angular/cli @angular/core @angular/material
The update process will run, but you're likely to hit your first roadblock immediately.
Part 2: What Breaks Immediately π
Unlike previous updates, Angular 20 introduces a significant breaking change that will stop your build.
1 The Full Story Behind Karmaβs Removal
The first thing you'll notice is that ng test
will fail. This isn't just a bug; it's a fundamental change in Angularβs build tools.
With Angular 20, the default build package changes from @angular-devkit/build-angular
to the new @angular/build
. This new package does not include Karma. The web ecosystem has moved on to faster, more modern test runners like Vitest and Jest, and Karma had become a bottleneck.
The Temporary Fix:
To get your tests running without migrating everything today, you need to manually reinstall the old build tool. This forces the CLI to use the old compiler that still supports Karma.
npm install @angular-devkit/build-angular --save-dev
This is a compatibility bridge. The message from the Angular team is clear: start planning your migration to Jest or Vitest soon.
2. browserslist and Browser Support
Here's a smaller detail that might catch you by surprise. Angular 20 officially no longer supports Opera. If you have "Opera" listed in your .browserslistrc
file, your build may fail or throw warnings. Remove it to resolve the issue.
Part 3: The New Architecture ποΈ
Beyond the breaking changes, Angular 20 pushes forward a more modern, explicit, and scalable architecture.
1. Standalone is the Default
New projects generated with ng new
are now standalone by default. This marks a fundamental architectural shift away from NgModules. By listing dependencies directly in a component's imports
array, each component becomes self-contained.
This change:
- Clarifies your architecture: You know exactly what each component needs.
- Improves tree-shaking: Leads to smaller, more optimized bundles.
To migrate your existing projects, you can run the standalone migration schematic:
ng generate @angular/core:standalone
2. A Folder Structure That Tells a Story
A good folder structure doesn't just hold filesβit tells you what the application does. It's worth mentioning the official Angular style guide, as its recommendations are built on years of community experience. This philosophy, detailed on their file structure reference page, is often summarized by the acronym LIFT:
- Locate your code easily.
- Identify what a file does at a glance.
- Flat structure for as long as you can.
- Try to be DRY (Don't Repeat Yourself).
The main takeaway is to organize by feature, not by type. Instead of a components
folder and a services
folder, you create folders for features like user-profile
or product-list
.
Let's use a practical example for an e-commerce app:
src/app/
βββ core/
β βββ auth/ # Authentication logic used everywhere
β β βββ auth-store.ts
β β βββ auth-interceptor.ts
β βββ layout/ # The app's shell: navbar, footer
β βββ navbar.ts
β βββ footer.ts
β
βββ features/
β βββ products/ # Everything for browsing products
β β βββ product-list.ts
β β βββ product-details.ts
β β βββ product-search.ts
β β
β βββ cart/ # The shopping cart feature
β βββ cart-store.ts
β βββ cart-view.ts
β βββ add-to-cart.ts
β
βββ shared/ # Reusable "dumb" components & utilities
βββ ui/
βββ button.ts
βββ spinner.ts
βββ price.pipe.ts
Why This Works:
- core ποΈ (Provide once): Services and components the app needs to run, loaded only once (AuthStore, Navbar).
- features β¨ (What your app does): The heart of your application. Each folder is a self-contained feature.
- shared β»οΈ (Reusable building blocks): "Dumb" components, pipes, and directives that don't know anything about the features they're used in (Button, Spinner). They are imported by feature modules.
3. The New Naming Convention
Angular 20 introduces a new official naming convention that drops traditional suffixes like .component.ts
or .service.ts
.
Old Naming | New Naming | Intent |
---|---|---|
user-profile.component.ts | user-profile.ts | UI Component |
auth.service.ts | auth-store.ts | State Management |
highlight.directive.ts | highlight.ts | Directive |
user-api.service.ts | user-api.ts | HTTP Client |
The goal is to focus on the file's intent rather than its technical type. A class that handles state is a "store," and one that makes HTTP requests is an "api." This makes your codebase's purpose much clearer, especially in a feature-based folder structure.
Part 4: The New Tools & Syntax π
Finally, let's look at the new tools that will make your day-to-day development better.
1. Control Flow is More Than Syntactic Sugar
The new @for
block replaces *ngFor
and is a major improvement.
Old Syntax:
<div *ngFor="let item of items; trackBy: trackItemById">
{{ item.name }}
</div>
New Syntax:
@for (item of items; track item.id) {
<div>{{ item.name }}</div>
} @empty {
<div>No items to display.</div>
}
Key improvements:
-
track
is mandatory, enforcing a performance best practice that was often forgotten. - The built-in
@empty
block cleans up templates by removing the need for a separate*ngIf
.
You can use the CLI to automatically refactor your templates:
ng generate @angular/core:control-flow
2. Zoneless: Escaping the "Magic" of Change Detection
While still experimental, the path to a zoneless Angular is becoming clearer. In a zoneless world, the UI only updates when you explicitly tell it to.
Signals are the primary tool for this.
mySignal.set(newValue);
This line directly tells Angular to update only the specific parts of the DOM that depend on that signal. Itβs a surgical, predictable, and high-performance approach that eliminates the overhead and unpredictability of Zone.js.
Conclusion
Angular 20 is a significant release. The upgrade requires manual intervention for testing, but it pushes the framework toward a more modern, explicit, and performant future. By embracing standalone components, a feature-based architecture, and the new control flow, you're not just updatingβyou're preparing your application for the next era of web development.
Happy coding!
Top comments (0)