Written in December 07, 2025 by Antonio Cárdenas
Frontend Developer & GDE
How the New Folder Structure Changes Everything
If you haven't updated to version 20 yet, the first and second parts of this guide can help you understand what's changing and how to update.
Part 1: The Update Itself 🛠️
First, let's solve the basics. 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 to commit all your current changes to 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:
1ng update @angular/cli @angular/coreIf you use Angular Material:
1ng update @angular/cli @angular/core @angular/materialThe update process will run, but you'll likely hit your first hurdle immediately.
Part 2: What Breaks Immediately
Unlike previous updates, Angular 20 introduces a significant change that will stop your build.
1. The Full Story Behind Karma Removal
The first thing you'll notice is that ng test will fail. This isn't just an error; 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 towards faster, modern test runners like Vitest and Jest, and Karma had become a bottleneck.
The Temporary Solution:
To get your tests running without migrating everything today, you must manually reinstall the old build tool. This forces the CLI to use the old compiler that is still compatible with Karma.
1npm install @angular-devkit/build-angular --save-devThis 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 minor detail that might surprise you. 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 for a more modern, explicit, and scalable architecture.
1. Standalone Components by 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.
What does this change imply?
- Cleaner, Defined Architecture: You know exactly what each component needs.
- Improved tree-shaking: Leads to smaller, optimized bundles.
To migrate your existing projects, you can run the standalone migration schematic:
1ng generate @angular/core:standalone2. 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 emphasizing the official Angular style guide, as its recommendations are based on years of community experience. This philosophy, detailed in their file structure reference page, is often summarized with the acronym LIFT (Locate, Identify, Flat, Try to be DRY):
- Locate your code easily.
- Identify what a file does at a glance.
- Flat: Keep structure as flat as you can.
- Try to be DRY: Don't Repeat Yourself.
The main takeaway is organize by feature, not by type. Instead of a components folder and a services folder, create folders for features like user-profile or product-list.
Let's use a practical example for an e-commerce application:
1src/app/
2├── core/
3│ ├── auth/ # Authentication logic used everywhere
4│ │ ├── auth-store.ts
5│ │ └── auth-interceptor.ts
6│ └── layout/ # The application shell: navbar, footer
7│ ├── navbar.ts
8│ └── footer.ts
9│
10├── features/
11│ ├── products/ # Everything for browsing products
12│ │ ├── product-list.ts
13│ │ ├── product-details.ts
14│ │ └── product-search.ts
15│ │
16│ └── cart/ # The shopping cart feature
17│ ├── cart-store.ts
18│ ├── cart-view.ts
19│ └── add-to-cart.ts
20│
21└── shared/ # Reusable "dumb" components and utilities
22 └── ui/
23 ├── button.ts
24 ├── spinner.ts
25 └── price.pipe.tsWhy does this work?
- 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 know nothing about the features they are used in (
Button,Spinner). They are imported by feature modules.
3. The New Naming Convention
Angular 20 introduces a new official naming convention that removes traditional suffixes like .component.ts or .service.ts.
| Old Naming | New Naming | Intent | | :-----------------------: | :--------------------: | :-------------------------------: | | user-profile.component.ts | user-profile.ts | User Interface 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 intent of the file rather than its technical type. A class that handles state is a "store", and one that makes HTTP requests is an "api". This makes the purpose of your code much clearer, especially in a feature-based folder structure.
Part 4: New Tools and Syntax
Finally, let's look at the new tools that will improve your daily development.
1. Control flow is more than syntactic sugar
The new @for block replaces *ngFor and is a major improvement.
Old syntax:
1<div *ngFor="let item of items; trackBy: trackItemById">
2 {{ item.name }}
3</div>New syntax:
1@for (item of items; track item.id) {
2 <div>{{ item.name }}</div>
3} @empty {
4 <div>No items to show.</div>
5}Key improvements:
trackis mandatory, enforcing a performance best practice that was often forgotten.- The built-in
@emptyblock cleans up templates by removing the need for a separate*ngIf.
You can use the CLI to automatically refactor your templates:
1ng generate @angular/core:control-flow2. Zoneless: Escaping "Change Detection Magic"
Although 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.
1mySignal.set(newValue);This line tells Angular directly 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 update requires manual intervention for testing, but pushes the framework toward a more modern, explicit, and high-performance future. By adopting 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.