DEV Community

Cover image for Angular 2025 Guide: Mastering Standalone Components
Ismael Ramos 🚀 for This is Learning

Posted on • Originally published at ismaelramos.dev

8 1 2

Angular 2025 Guide: Mastering Standalone Components

Introduction to the series

If you learned Angular back in the Angular 2–14 days, you’re not alone — and you're definitely not outdated. The framework has evolved quickly: standalone components, signals, zoneless change detection... sometimes it feels like a whole new Angular.

But you don’t need to start from scratch.

This series is here to help you catch up step by step, with real examples and no overwhelm. Whether you’re returning to Angular or diving in for the first time, you’ll learn how to level up with modern patterns, like local state management with signals.

Learning with real examples

We’ll be using angular-example-app as the base project for every post. This app showcases best practices, up-to-date features, and a structure ideal for beginners to learn from and professionals to build on.

new angular logo for 2025


🚀 Getting Started with Standalone Components in Angular

When you first start learning Angular, one of the biggest hurdles is understanding NgModules — how to declare them, import them, and keep everything in sync. But with the introduction of Standalone Components, Angular makes things much simpler, especially for beginners.

In this post, we’ll break down what standalone components are, how they work, and how they compare to NgModules.


What are Standalone Components?

Standalone components were introduced in Angular 14 to reduce the need for NgModules. A standalone component can import everything it needs on its own, making your Angular apps more modular and easier to understand.

In short:

  • You don’t need to declare it in any module.
  • It handles its imports (like CommonModule, RouterModule, etc.).
  • You can use it directly in routes or other standalone components.

standalone components meme


The Old Way: With NgModules

Traditionally, you’d need a module like this:

// old-school.component.ts
@Component({
  selector: 'app-old-school',
  templateUrl: './old-school.component.html',
})
export class OldSchoolComponent {}

// old-school.module.ts
@NgModule({
  declarations: [OldSchoolComponent],
  imports: [CommonModule],
})
export class OldSchoolModule {}
Enter fullscreen mode Exit fullscreen mode

This approach works fine, but it creates extra files and boilerplate, especially for small or isolated components.


The New Way: Standalone Components

Now check out how the HomeComponent is defined:

// home.component.ts
@Component({
  selector: 'app-home',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './home.component.html',
})
export class HomeComponent {}

Enter fullscreen mode Exit fullscreen mode

🧪 You can see this in action in the angular-example-app:
src/app/pages/home/home.component.ts


How Routing Works with Standalone Components

angular routing signal

Instead of loading a module in the router, you just point to the standalone component:

// app.routes.ts
export const routes: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./pages/home/home.component').then((m) => m.HomeComponent),
  },
];

Enter fullscreen mode Exit fullscreen mode

You’ll find this setup under src/app/app.routes.ts.
It’s simple, direct, and doesn’t need lazy-loaded modules anymore.

New app config

One of the coolest parts of using standalone components is how cleanly you can configure your whole app without AppModule. In the base project, this is all done in a single file: src/app/app.config.ts

It is the brain of the app — it defines all the global wiring (routing, HTTP, animations, images, environment...) in a single, easy-to-maintain file.

Here’s a breakdown of what this file does — and why it’s so powerful:

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: ENVIRONMENT,
      useValue: environment,
    },
    provideExperimentalZonelessChangeDetection(),
    provideRouter(
      appRoutes,
      withInMemoryScrolling(),
      withViewTransitions({
        onViewTransitionCreated: ({ transition, to }) => {
          const router = inject(Router);
          const toTree = createUrlTreeFromSnapshot(to, []);
          // Skip the transition if the only thing changing is the fragment and queryParams
          if (
            router.isActive(toTree, {
              paths: 'exact',
              matrixParams: 'exact',
              fragment: 'ignored',
              queryParams: 'ignored',
            })
          ) {
            transition.skipTransition();
          }
        },
      }),
      withComponentInputBinding(),
      withRouterConfig({ paramsInheritanceStrategy: 'always', onSameUrlNavigation: 'reload' }),
      withPreloading(PreloadAllModules),
    ),
    provideHttpClient(
      withFetch(),
      withInterceptors([authenticationInterceptor, cachingInterceptor]),
    ),
    provideAnimationsAsync(),
    provideCloudinaryLoader('https://res.cloudinary.com/ismaestro/'),
  ],
};

Enter fullscreen mode Exit fullscreen mode

Key Features

Let’s break down some of the powerful things this configuration enables:

provideExperimentalZonelessChangeDetection()

Removes Angular’s dependency on Zone.js, enabling better performance with modern reactive patterns. ⚠️ It's still experimental, you should avoid this for production apps!

provideRouter() with Extras

This router setup includes:

  • withInMemoryScrolling(): preserves scroll position on navigation.
  • withViewTransitions(): enables smooth transitions between views, and even skips them when nothing relevant changes.
  • withComponentInputBinding(): allows inputs to be set via route params.
  • withPreloading(PreloadAllModules): preloads all lazy-loaded routes in the background for faster navigation.

provideHttpClient() with withFetch()

Switches Angular to use the Fetch API instead of XMLHttpRequest. Also registers interceptors.

provideAnimationsAsync()

Uses Angular’s modern async animations system. For more information, follow the official animations guide.


Ties Everything Together

This config file is passed to bootstrapApplication in main.ts, like so:

bootstrapApplication(AppComponent, appConfig)
  .catch(err => console.error(err));

Enter fullscreen mode Exit fullscreen mode

main.ts This replaces the old AppModule entirely.


✅ What’s Next: Feature-First Project Structure

Standalone components are the foundation of modern Angular — they simplify your architecture, reduce boilerplate, and make your app easier to reason about. If you're just coming back to Angular, this is the best place to restart.

In the next post, we’ll explore how to structure your project from a feature-first perspective, taking a close look at how the angular-example-app organizes code in a scalable and maintainable way. :)

board and a hand organizing items

I hope you’ve learned something new from all of this. If you think this might help others, please hit the like button so more people can find it. ❤️

Got thoughts or questions? Drop a comment — I'd love to respond!

Top comments (0)