Angular 19 Standalone Components: Build Faster, Simpler Apps Without NgModules


TL;DR: Angular 19 empowers developers with standalone components, eliminating the need for NgModules. This simplifies application structure, improves maintainability, and reduces complexity. In this blog, you’ll learn standalone components, how they work, and how to migrate your existing Angular apps using best practices. You’ll also explore real-world examples that make adoption easier for teams of any size.
Why Standalone components?
Standalone components are Angular 19’s new way to build applications without NgModules. They simplify the app structure by allowing components, directives, and pipes to operate independently, improving maintainability and reducing boilerplate code.
In this blog, we’ll explore standalone components, why they matter, and how you can migrate your existing apps seamlessly. Whether you’re an Angular veteran or just getting started, this guide will show you how to unlock the full potential of Angular 19.
Key benefits
Standalone components represent a shift from the traditional NgModule-centric approach. They offer:
Simplicity: No NgModule management required.
Explicit dependencies: Components declare their own dependencies.
Better reusability: More portable across projects.
Incremental adoption: You can mix standalone and NgModule-based components.
Creating Standalone components
Basic example
A simple Standalone component that displays a greeting message:
import { Component } from "@angular/core";
@Component({
selector: "app-greeting",
template: `<h2>Hello, {{ name }}!</h2>`,
})
export class GreetingComponent {
name = "Angular 19";
}
Component with dependencies
This example shows how a standalone component can directly declare its dependencies in the imports array and use the new control flow syntax:
import { Component } from "@angular/core";
@Component({
selector: "app-conditional",
template: `
@if (show) {
<div>Visible Content</div>
}
`,
})
export class ConditionalComponent {
show = true;
}
Bootstrap application
The bootstrapApplication function is a modern way to launch your app with a standalone root component, without NgModule. It takes your main component (AppComponent) and sets up the application, making the startup process simpler and more efficient.
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app.component";
bootstrapApplication(AppComponent);
Standalone directives and pipes
Angular 19 lets you create standalone directives and pipes. Here’s how you can make and use them:
Standalone directive example
This directive uses ElementRef, which is Angular’s way to access the DOM element directly. ElementRef provides a wrapper around a native DOM element inside the directive, allowing you to manipulate it directly (via the nativeElement property). In this case, we’re using it to change the background color of the element.
import { Directive, ElementRef, inject, Input, OnInit } from "@angular/core";
@Directive({
selector: "[appHighlight]",
})
export class HighlightDirective implements OnInit {
@Input() appHighlight = "yellow";
private el = inject(ElementRef);
ngOnInit() {
this.el.nativeElement.style.backgroundColor = this.appHighlight;
}
}
Standalone pipe example
A pipe that shortens long text and adds an ellipsis:
import { Pipe, PipeTransform } from "@angular/core";
@Pipe({
name: "truncate",
})
export class TruncatePipe implements PipeTransform {
transform(value: string, limit = 25): string {
return value.length > limit ? value.substring(0, limit) + "..." : value;
}
}
HTTP interceptors
This example demonstrates Angular’s functional HTTP interceptor approach. The HttpInterceptorFn is a modern way to intercept HTTP requests in Angular, allowing you to modify requests before they’re sent and responses before they’re processed.
This interceptor adds an authentication token to outgoing requests and is registered using the withInterceptors function in the app’s bootstrap configuration.
import { HttpInterceptorFn } from "@angular/common/http";
export const AuthInterceptor: HttpInterceptorFn = (request, next) => {
const headerToken = localStorage.getItem("authToken");
if (headerToken) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${headerToken}`,
},
});
}
return next(request);
};
// main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
import { provideHttpClient, withInterceptors } from "@angular/common/http";
import { AuthInterceptor } from "./app/interceptor/http.interceptor";
bootstrapApplication(AppComponent, {
providers: [
// other existing providers
provideHttpClient(withInterceptors([AuthInterceptor])),
],
}).catch((err) => console.error(err));
Advanced features in Angular 19
Lazy loading with Standalone components
The simplest way to lazy load a standalone component is with the loadComponent property in your routes configuration:
import { Routes } from "@angular/router";
export const routes: Routes = [
{
path: "dashboard",
loadComponent: () =>
import("./dashboard/dashboard.component").then(
(m) => m.DashboardComponent
),
},
];
In this example:
When a user navigates to /dashboard, Angular will dynamically import the DashboardComponent.
The import() function returns a Promise that resolves to the module.
The .then() chain extracts the actual component from the module.
Until navigation occurs, this code isn’t loaded into the main bundle.
Lazy loading with guards and resolvers
You can protect lazy-loaded routes with guards and preload data with resolvers:
// auth.guard.ts
import { inject } from "@angular/core";
import { Router } from "@angular/router";
import { AuthService } from "./auth.service";
export const authGuard = () => {
const authService = inject(AuthService);
const router = inject(Router);
if (authService.isLoggedIn()) {
return true;
}
// Redirect to the login page
return router.parseUrl("/login");
};
// app.routes.ts
import { Routes } from "@angular/router";
import { authGuard } from "./auth.guard";
import { adminDataResolver } from "./admin-data.resolver";
export const routes: Routes = [
{
path: "admin",
loadComponent: () =>
import("./admin/admin.component").then((m) => m.AdminComponent),
canActivate: [authGuard],
resolve: { dashboardData: adminDataResolver },
},
];
Preloading strategies
Angular offers preloading strategies like smart loading plans for your app. While lazy loading waits to load a component until a user needs it, preloading loads these components quietly in the background after your app’s first page is ready. This gives you the best of both worlds, fast initial loading and quick navigation later.
The PreloadAllModules strategy is Angular’s built-in solution that automatically downloads all lazy components in the background once your main app is up and running.
import { bootstrapApplication } from "@angular/platform-browser";
import { AppComponent } from "./app/app.component";
import {
provideRouter,
withPreloading,
PreloadAllModules,
} from "@angular/router";
import { routes } from "./app/app.routes";
bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes, withPreloading(PreloadAllModules)),
],
}).catch((err) => console.error(err));
Integrating Standalone components with state management
State management is crucial for scalable Angular applications. Standalone components in Angular 19 work seamlessly with popular state management solutions like NgRx, Akita, or even simple RxJS-based services.
Example: Using NgRx with Standalone components
A user profile component that loads user data from the store:
// user.actions.ts
import { createAction, props } from "@ngrx/store";
export const loadUser = createAction(
"[User] Load User",
props<{ id: number }>()
);
// user.reducer.ts
import { createReducer, on } from "@ngrx/store";
import { loadUser } from "./user.actions";
export const initialState = { user: null };
export const userReducer = createReducer(
initialState,
on(loadUser, (state, { id }) => ({ ...state, loading: true }))
);
// user.selectors.ts
import { createSelector } from "@ngrx/store";
export const selectUser = (state: any) => state.user;
// user-profile.component.ts
import { Component, inject } from "@angular/core";
import { Store } from "@ngrx/store";
import { selectUser } from "./user.selectors";
import { loadUser } from "./user.actions";
@Component({
selector: "app-user-profile",
template: `
<button (click)="load()">Load User</button>
<pre>{{ user$ | async | json }}</pre>
`,
})
export class UserProfileComponent {
private store = inject(Store);
user$ = this.store.select(selectUser);
load() {
this.store.dispatch(loadUser({ id: 1 }));
}
}
You can import StoreModule and EffectsModule directly into your bootstrap or feature configuration without NgModules.
// main.ts
import { isDevMode } from "@angular/core";
import { bootstrapApplication } from "@angular/platform-browser";
import { provideStore, provideState } from "@ngrx/store";
import { provideEffects } from "@ngrx/effects";
import { provideStoreDevtools } from "@ngrx/store-devtools";
import { AppComponent } from "./app/app.component";
import { userReducer } from "./app/user.reducer";
bootstrapApplication(AppComponent, {
providers: [
provideStore(),
provideEffects(
// add your effects here
),
provideState({ name: "User_Reducer", reducer: userReducer }),
provideStoreDevtools({ maxAge: 25, logOnly: !isDevMode() }),
],
}).catch((err) => console.error(err));
Migrating to a Standalone in Angular 19
Migrating an existing Angular project to standalone components, directives, and pipes is now streamlined with official Angular schematics. The process is incremental, safe, and designed to minimize manual intervention, though some manual fixes may still be required.
Prerequisites
Ensure your project uses Angular 15.2.0 or later.
The project should build without compilation errors.
All work should be saved and committed on a clean Git branch.
Migration steps
- Convert declarations to standalone
- Run the schematic:
ng g @angular/core:standalone
- Select “Convert all components, directives, and pipes to standalone”. The schematic will remove standalone: false and add dependencies to the imports array for each declaration.
Note: The schematic ignores NgModules
that bootstrap a component at this step; these are handled in a later step.
- Remove unnecessary NgModules
- Rerun the schematic to remove modules you no longer need:
ng g @angular/core:standalone
- Select “Remove unnecessary NgModule classes”. The schematic deletes module declarations and as many references as possible. If it cannot remove a reference automatically, it leaves a TODO comment for manual cleanup.
- Switch to Standalone Bootstrapping API
- Run the schematic a third time to switch to the new bootstrapping API:
ng g @angular/core:standalone
Select “Bootstrap the project using standalone APIs”.
This step converts the usage of bootstrapModule to the new bootstrapApplication API and removes the root NgModule.
Any providers or imports from the root module are copied into the new bootstrap call as much as possible.
- Manual cleanup and verification
Remove any remaining NgModule declarations or references that the schematic could not handle automatically (look for TODO comments).
Run the project’s unit tests and fix any failures.
Run code formatters and linters to fix new warnings or errors.
Ensure all files are included in your tsconfig.json so the schematic can analyze them.
For more details about migration modes and limitations, see the official Angular migration guide.
Best practices for Standalone components in Angular 19
While designing an application using a standalone component, prefer to follow the following best practices:
Organize by feature: Group related components, directives, and pipes in feature folders.
Use barrel files: Create index.ts files to manage exports and simplify imports.
Minimize imports: Only import what you need in each component to optimize bundle size.
Document dependencies: Comment on why certain dependencies are imported.
Leverage CLI tools: Use Angular CLI’s new migration and scaffolding commands for consistency.
Test in isolation: Use explicit imports to write focused, fast unit tests.
Bundle analysis: Use ng build –stats-json for size optimization.
Performance considerations
Standalone components enable more effective tree-shaking and lazy loading, resulting in smaller bundles and faster startup times.
Angular 19’s improved change detection and dependency tracking to boost runtime performance.
Common pitfalls and how to avoid them
Missing imports: Always include dependencies in the imports array.
Legacy libraries: Check for standalone-compatible third-party libraries.
Overusing providers: Be mindful of provider scopes to avoid unexpected singleton or multi-instance behavior.
Conclusion
Angular 19’s Standalone components mark a significant step towards more modular and maintainable application development. By embracing this approach, developers can simplify their codebases, enhance performance, and future-proof their applications. Ready to transition to a more streamlined Angular architecture? Our team is here to assist you every step of the way.
The Syncfusion® Angular UI components library is a comprehensive suite that provides everything you need to build an application. It includes over 90 high-performance, lightweight, modular, and responsive UI components, all in a single package. You can try our 30-day free trial to check its features.
You can also contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!
FAQs
Q1: Can I mix standalone components with NgModules in the same app?
Yes! Angular 19 fully supports incremental adoption. You can mix standalone and NgModule-based components during migration or while building hybrid apps.
Q2: Do I need to change my entire app to use standalone components?
No. Migration is incremental. You can convert components, directives, and pipes gradually and mix them with existing NgModules without breaking the app.
Q3: Are standalone components better for performance?
Yes! Standalone components support better tree-shaking and lazy loading, reducing bundle size and improving startup time.
Q4: Can I use Angular features like routing, guards, or resolvers with standalone components?
Absolutely. Angular 19 allows full routing configurations, guards, resolvers, and lazy loading—all compatible with standalone components.
Related Blogs
Syncfusion Angular UI Kit Updated with 39 Powerful New UI Blocks
Mastering SOLID Principles in Angular: Build Scalable Apps with Clean Code
This article was originally published at Syncfusion.com.
Subscribe to my newsletter
Read articles from syncfusion directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

syncfusion
syncfusion
Syncfusion provides third-party UI components for React, Vue, Angular, JavaScript, Blazor, .NET MAUI, ASP.NET MVC, Core, WinForms, WPF, UWP and Xamarin.