Hook — 05 : 45 a.m., Santa Marta (Colombia) → Cancún code-review
Caribbean dawn blurred my screen when Sofía, dialing from Cancún, shared a PR: “I added a tiny badge component but had to touch three NgModules—did I do it right?” The diff looked like a spaghetti western: declarations, imports, exports sprinkled across files. “Hold on,” I said, cracking open the CLI. We regenerated the badge as a standalone component, deleted 40 lines of module boilerplate, and her build passed in two minutes—before the street vendor below finished frying my arepa de huevo. That beach-side refactor is our roadmap today: you’ll learn why Angular’s standalone APIs matter, how to migrate an existing feature, and where common pitfalls hide—knowledge forged from shipping apps across the Dominican Republic, Brazil, Costa Rica, Panama, Mexico, and (of course) Colombia.


Why Standalone Components Matter Today

Pain pointClassic NgModule workflowStandalone escape hatch
BoilerplateDeclare ➜ import ➜ export in multiple filesOne decorator replaces it all
Onboarding juniors“Which module do I touch?” Slack pingsComponent usable instantly after ng g c
Bundle bloatEagerly pulled shared NgModulesFine-grained tree-shaking per component
Lazy loadingExtra routing modules, indirectionDirect loadComponent calls
Testing frictionConfigure TestBed imports per specShallow tests with importProvidersFrom()

Angular 18 makes standalone the default for ng new. Learning it now means less refactor later.


What Exactly Is a Standalone Component?

A standalone component is self-describing. Everything it needs—template, styles, providers, and dependencies—lives in its own decorator. No parent NgModule required.

tsCopyEdit@Component({
  standalone: true,
  selector: 'surf-badge',
  template: `<span class="badge" [ngClass]="color">{{ label }}</span>`,
  styles: [`.badge { padding: .25rem .5rem; border-radius: .5rem; }`],
  imports: [NgClass],                       // bring in directives you use
})
export class SurfBadgeComponent {
  @Input() label = '';
  @Input() color: 'primary' | 'warning' = 'primary';
}

That’s it. Drop <surf-badge> anywhere after importing the file and Angular’s new tree-shakable build graph handles the rest.


Walkthrough 1 — Creating a Standalone Component from Scratch

bashCopyEditng g component surf-badge --standalone --export

Use it in AppComponent:

tsCopyEditimport { SurfBadgeComponent } from './surf-badge/surf-badge.component';

bootstrapApplication(AppComponent, {
  providers: [
    importProvidersFrom(RouterModule.forRoot([])),
  ],
  // make it globally available
  standaloneComponents: [SurfBadgeComponent],
});

CLI pro-tip: Add "standalone": true in angular.json schematics to make this the new default for your team.


Walkthrough 2 — Migrating an Old NgModule Feature

Imagine dashboard.module.ts with five components. We’ll convert one—chart-card.

  1. Add standalone: true to chart-card.component.ts.
  2. Move imports used only by this component into its decorator:
tsCopyEditimports: [NgIf, NgOptimizedImage, TooltipDirective]
  1. Delete declarations entry in dashboard.module.ts.
  2. Route directly:
tsCopyEditconst routes: Route[] = [
  {
    path: 'charts',
    loadComponent: () =>
      import('./charts/chart-card.component').then(m => m.ChartCardComponent),
  },
];
  1. Run ng build—Webpack emits a smaller chunk because the previous shared module (and its unused dependencies) no longer load for other routes.

Remote-Work Insight 🏝️

In Costa Rica’s Osa Peninsula our coworking Wi-Fi throttled at lunch. Every kilobyte mattered. Migrating three components to standalone shaved 160 kB from our initial JS bundle—enough to keep FCP under Google’s “good” threshold even when the router rebooted mid-day (which happened more than we’d like).


Common Pitfalls & How to Dodge Them

SymptomRoot causeQuick fix
NG0500: Importing ... which is not a standalone componentForgot standalone: true on dependencyAdd flag or re-export via a standalone library
Circular imports between standalone componentsShared service imported twiceCreate a provideRoot() service or use signals instead
Global pipes/directives missingNot added in imports arrayImport from @angular/common or shared standalone
IDE auto-completes wrong selectorOld module still in pathRemove dead NgModule file after migration

Performance & Accessibility Checkpoints

  1. Bundle sizeng build --stats-json ➜ use ng build-analyzer to verify per-component chunks.
  2. First Contentful Paint — should drop as modules shrink.
  3. Change Detection — standalone components default to ChangeDetectionStrategy.Default; set to OnPush for heavy UI.
  4. ARIA consistency — each component owns its label associations; lint with @angular-eslint/template-accessibility.

Hands-On: Standalone + Signals + Lazy Loading

bashCopyEditng g component live-rate --standalone --skip-tests
tsCopyEdit@Component({
  standalone: true,
  selector: 'live-rate',
  template: `<span>{{ rate() | number:'1.2-2' }}</span>`,
  imports: [NgIf],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class LiveRateComponent {
  private _rate = signal(0);

  rate = this._rate.asReadonly();

  constructor(ws: WebSocketService) {
    ws.stream<number>('rate').subscribe(this._rate);
  }
}

Lazy route:

tsCopyEdit{
  path: 'rates',
  loadComponent: () =>
    import('./live-rate/live-rate.component').then(m => m.LiveRateComponent),
}

Now only users navigating to /rates download the component and its WebSocket code—perfect for mobile data plans along Mexico’s Mayan coast.


Call-Out Table — Key Standalone APIs

APIOne-liner purpose
standalone: trueDeclares component/directive/pipe independent of NgModule
imports arrayLocalizes dependencies (directives, pipes, other standalone comps)
bootstrapApplication()Boots app without root module
importProvidersFrom()Bring NgModules into standalone world (e.g., RouterModule)
loadComponent()Lazy-load standalone component in router

Essential CLI Commands

CommandWhat it does
ng new surf-shop --standaloneWorkspace defaults to standalone APIs
ng g c header --standalone --exportGenerate reusable header
ng g interceptor auth --standaloneCreate provider w/out module
ng build --configuration productionTree-shakes standalone graphs

SVG Diagram Idea

  1. Classic: Component ➜ NgModule A ➜ AppModule ➜ Bundle.
  2. Standalone: Component ➜ Router loadComponent ➜ Bundle (smaller, direct).

Arrows show fewer hops and chunk splits.


Wrap-Up

Standalone components free Angular devs from the ceremony that once pushed newcomers toward other frameworks. Generate, import, ship—it’s that simple. Start by flipping one leaf component, then a feature, and soon your entire codebase breathes easier, builds faster, and welcomes juniors without NgModule anxiety. Whether you’re coding from Colombian balconies or Panamanian coworks, that’s a win.

Drop questions or solo-component victories below—I’ll reply between layovers and late-night arepa sessions.


0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x