Standalone Without Turning NgModules Into a Villain

Standalone changes the architectural default, but it does not make every existing NgModule an emergency. Components are standalone by default since Angular v17 and NgModule has been optional since v19. The official standalone migration docs are explicit: existing apps can adopt incrementally without breaking changes.

The better question

In a mature Angular app, the standalone conversation should not start with 'how do we remove every module?'. Start with a smaller question: where does this feature's boundary live today, and would standalone make that boundary easier to read, load, and test?

Angular components are standalone by default in current Angular. That changes the default for new code. It does not mean a stable internal library, a third-party integration, or a legacy feature module must be rewritten before the next release.

I would keep a stable LegacyPaymentsModule if it wraps a third-party SDK, exposes a known public API, and is not blocking lazy loading, ownership, or tests. That is not nostalgia; it is scope control.

The decision table

SituationDefault moveEvidence to require
New route or featureUse standalone components and route-level providersImports are explicit and the route can own the feature facade.
Module that only exists for declarationsMigrate when the area is touchedBuild and feature smoke test pass without broad import churn.
SharedModule with everythingSplit by real usageA dependency list shows which imports each feature actually needs.
Stable library moduleKeep it unless there is a release reasonThe module still expresses a public API or third-party contract.
Third-party SDK wrapperKeep or isolate behind a routeThe migration would add risk without improving ownership.
Root providers everywhereMove feature state closer to the routeNavigation, reuse, and teardown behavior are understood.

A route is a better boundary than a slogan

The useful standalone migration is often a route migration. A route can lazy-load a component, scope providers, and become the place where the team sees the feature boundary without opening a module file first.

If every provider stays at root and every component imports a giant shared bucket, the code is not meaningfully more modular. It only removed a layer of ceremony.

Route-owned feature boundaryts
export const routes: Routes = [
  {
    path: 'billing',
    providers: [BillingFacade],
    loadComponent: () =>
      import('./billing/billing-page.component')
        .then(m => m.BillingPageComponent),
  },
];

What to avoid

Avoid a migration branch whose only promise is 'remove NgModules'. That kind of branch tends to touch too many files, create review fatigue, and produce little product value.

The better move is narrower: use standalone as the default for new code, migrate modules that block lazy loading or hide dependencies, and leave stable modules alone until the team has a reason to touch them.

Reusable artifact

Standalone adoption rule

  • Use standalone by default in new Angular code.
  • Use routes as feature boundaries, not SharedModule as the first design tool.
  • Keep NgModules when they still express a stable integration boundary.
  • Migrate touched modules only when the diff improves ownership, loading, or reviewability.

Sources checked

Modern Angular Playbook

This article is one play.

The Modern Angular Playbook collects the diagnostic, the adoption matrix, eleven plays, and the 30-day plan. Free, in English and Portuguese.

You get both PDFs by email, through the Dojo IA list.

Open playbook →

André Ramosavailable for remote roles, UTC−3Get in touch →

Related

Guide · 8 min

Modern Angular for Production Teams

A senior map for production Angular modernization: where the current Angular model helps, where experiments need isolation, and where a rewrite is the wrong answer.

Read article →

Guide · 9 min

Facade Pattern in Angular: When Components Know Too Much

A production Angular guide to the Facade Pattern: when to create one, what it should own, what it should not hide, and how to test the boundary.

Read article →

Guide · 9 min

Adapter Pattern in Angular: Isolating APIs and Browser-Only Libraries

A production Angular guide to the Adapter Pattern: where to translate external contracts, how to keep components clean, and when a wrapper is not worth creating.

Read article →