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.
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.
Standalone components represent a shift from the traditional NgModule-centric approach. They offer:
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";
}
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;
}
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);
Angular 19 lets you create standalone directives and pipes. Here’s how you can make and use them:
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;
}
}
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;
}
}
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));
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:
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 },
},
];
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));
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.
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 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.
ng g @angular/core:standalone
Note: The schematic ignores NgModules
that bootstrap a component at this step; these are handled in a later step.
ng g @angular/core:standalone
ng g @angular/core:standalone
For more details about migration modes and limitations, see the official Angular migration guide.
While designing an application using a standalone component, prefer to follow the following best practices:
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!
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.