Standalone Components: The Right Way to Start a New Angular App
Standalone components killed NgModules. Here’s how the modern Angular setup actually works — bootstrap, routing, lazy loading — and how to migrate an older app.
If you learned Angular before 2023, you learned NgModules. Then you learned how to wire feature modules, shared modules, core modules, and a single root module that imported them all in the right order. Then you spent your career fighting the import graph.
Standalone components killed all of that. If you're starting a new Angular app today, this is the only setup that should be on the table.
What changed
A standalone component declares its own imports. No module. No declarations array. No exports. Just a component class that says what it needs.
``typescript @Component({ selector: 'app-blog-list', imports: [RouterLink, DatePipe], templateUrl: './blog-list.component.html' }) export class BlogListComponent { } ``
That's the whole declaration. The imports array lists every component, directive, and pipe this component uses. If you forget one, the compiler tells you which line broke.
Why this is better
- Less boilerplate. No module file, no
@NgModuledecorator, no shared module dance. The component owns its own dependencies. - Lazy loading is straightforward. Point a route at a component file and Angular code-splits it. No
loadChildrenreturning a module factory. - Tree shaking actually works. If a component isn't imported, it isn't in the bundle. With modules, anything in
declarationscame along whether you used it or not. - Easier to read. You open a component and see exactly what it depends on. You don't have to trace which module declared what.
Bootstrap a standalone app
The main.ts for a standalone app is a single function call:
``typescript bootstrapApplication(AppComponent, { providers: [ provideRouter(routes), provideHttpClient(), provideAnimations() ] }); ``
The provideX functions replace what used to live in imports: [HttpClientModule, RouterModule.forRoot(routes), ...]. Same idea, less ceremony.
Routing without module files
Each route can point directly at a standalone component, including lazy-loaded ones:
``typescript export const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'blog', loadComponent: () => import('./pages/blog/blog-list.component') .then(m => m.BlogListComponent) } ]; ``
The loadComponent form is what gets code-split into its own chunk. The Angular CLI handles the chunking automatically — no extra config.
What about shared things?
For shared UI components, just import them where you need them. There's no "shared module" to manage. If you find yourself listing the same five imports in every component, that's a smell — but a manageable one. A small SHARED_UI constant works fine:
```typescript export const SHARED_UI = [ButtonComponent, IconComponent, CardComponent];
@Component({ imports: [...SHARED_UI, RouterLink] }) ```
For services, they're injected via DI as always. Most services should be providedIn: 'root' — tree-shakable, app-wide singletons with no extra wiring.
Migrating from modules
If you have an existing module-based Angular app, you don't have to rewrite it. The Angular CLI has a migration:
``bash ng generate @angular/core:standalone ``
Run it three times — once for each phase (components, routes, bootstrap). It isn't perfect, but it gets you 90% of the way there. The remaining 10% is hand-fixing the edge cases.
What I do on new projects
Every new Angular component I generate is standalone. Every route is loadComponent. The only module-like file is the app.config.ts with the root providers. The whole app feels lighter, both in code and in the bundle.
Standalone components plus Signals are most of what makes modern Angular feel modern. They're also why the framework is lighter on the bundle than its reputation suggests — covered in Angular Performance.
If you're stuck on a legacy Angular app and trying to figure out whether to migrate or rewrite, send me a message. That conversation often saves the rewrite.
Want help shipping this on your own site?
Free 30-minute consultation. No pressure either way.
