Angular Standalone Components: Panduan Migrasi dan Praktik Terbaik 2026

Panduan lengkap migrasi Angular standalone components. Langkah-langkah menghapus NgModules, mengaktifkan lazy loading, dan mengadopsi standalone API di Angular 21.

Angular Standalone Components Migration Guide 2026

Angular standalone components menghilangkan kebutuhan akan NgModules, mengurangi boilerplate, dan membuka kemungkinan lazy loading yang lebih granular di seluruh aplikasi. Sejak Angular 19 menjadikan standalone sebagai default dan Angular 21 memantapkan zoneless change detection, migrasi codebase berbasis modul menjadi proses yang mudah dan berdampak tinggi.

Poin Penting

Schematic resmi Angular CLI menangani sebagian besar migrasi secara otomatis dalam tiga tahap. Aplikasi enterprise pada umumnya dapat menyelesaikan konversi dalam satu sprint, dengan ukuran bundle yang berkurang 30-50% berkat lazy loading per komponen.

NgModules vs Standalone Components: Apa yang Berubah

NgModules berfungsi sebagai konteks kompilasi untuk komponen sejak Angular 2. Setiap komponen, direktif, dan pipe harus dideklarasikan di tepat satu modul, dan fungsionalitas yang dibagikan memerlukan orkestrasi import dan export modul yang cermat. Hal ini menciptakan coupling yang ketat antara fitur-fitur yang tidak terkait dan membuat tree-shaking menjadi sulit.

Standalone components membalikkan model ini. Setiap komponen mendeklarasikan dependensinya sendiri secara langsung di array imports pada decorator @Component. Tidak ada lagi registrasi modul, shared modules, atau barrel exports dari separuh aplikasi.

hero-list.component.tstypescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HeroCardComponent } from './hero-card.component';
import { SearchPipe } from '../pipes/search.pipe';

@Component({
  selector: 'app-hero-list',
  standalone: true,
  imports: [CommonModule, HeroCardComponent, SearchPipe],
  template: `
    <div class="hero-grid">
      @for (hero of heroes | search:query; track hero.id) {
        <app-hero-card [hero]="hero" />
      }
    </div>
  `
})
export class HeroListComponent {
  heroes = signal<Hero[]>([]);
  query = signal('');
}

Array imports menggantikan seluruh dependency graph NgModule. Bundler dapat melihat dengan tepat komponen, pipe, dan direktif mana yang dibutuhkan setiap file, sehingga memungkinkan tree-shaking yang presisi.

Proses Migrasi CLI 3 Langkah

Angular menyediakan schematic otomatis yang menangani migrasi dalam tiga tahap berurutan. Setiap langkah dibangun di atas langkah sebelumnya, dan proyek harus dapat dikompilasi dengan bersih di antara setiap tahap.

Langkah 1: Konversi Deklarasi ke Standalone

Tahap pertama memindai setiap komponen, direktif, dan pipe dalam proyek, menambahkan standalone: true, dan memindahkan import yang diperlukan dari NgModule induknya ke array imports masing-masing komponen.

bash
# Langkah 1: Konversi semua deklarasi ke standalone
ng g @angular/core:standalone --path=src/app

Pilih "Convert all components, directives and pipes to standalone" ketika diminta. Schematic menggunakan analisis statis untuk menyelesaikan dependensi, sehingga komponen dengan metadata yang tidak dapat dianalisis saat build time akan dilewati dengan peringatan.

Langkah 2: Hapus NgModules yang Tidak Diperlukan

Dengan semua deklarasi yang sudah standalone, banyak NgModules menjadi cangkang kosong. Tahap ini mengidentifikasi modul yang hanya mengekspor ulang deklarasi standalone dan menghapusnya.

bash
# Langkah 2: Hapus NgModules kosong
ng g @angular/core:standalone --path=src/app

Pilih "Remove unnecessary NgModule classes". Modul yang masih berisi providers, konfigurasi route, atau diimpor oleh beberapa modul lain akan dipertahankan dengan komentar TODO untuk tinjauan manual.

Langkah 3: Beralih ke Standalone Bootstrap

Tahap terakhir mengganti root NgModule dengan API bootstrapApplication dari Angular dan mengonversi root component menjadi standalone.

main.ts (setelah migrasi)typescript
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { appConfig } from './app/app.config';

bootstrapApplication(AppComponent, appConfig)
  .catch(err => console.error(err));
app.config.tstypescript
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { routes } from './app.routes';
import { authInterceptor } from './interceptors/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(withInterceptors([authInterceptor]))
  ]
};

Pola ApplicationConfig menggantikan array providers dan imports dari root module. Semua fungsi provider (provideRouter, provideHttpClient, provideAnimations) bekerja langsung tanpa pembungkus modul.

Migrasi Routing: Dari Modules ke loadComponent

Routing modules memerlukan perhatian manual karena schematic tidak secara otomatis mengonversi import loadChildren berbasis modul ke loadComponent atau loadChildren tingkat route dengan standalone routes.

Pola lama memuat seluruh feature modules:

app.routes.ts (sebelum)typescript
const routes: Routes = [
  {
    path: 'dashboard',
    loadChildren: () => import('./dashboard/dashboard.module')
      .then(m => m.DashboardModule)
  }
];

Pola standalone yang baru memuat komponen individual atau file route secara langsung:

app.routes.ts (sesudah)typescript
import { Routes } from '@angular/router';

export const routes: Routes = [
  {
    path: 'dashboard',
    loadComponent: () => import('./dashboard/dashboard.component')
      .then(c => c.DashboardComponent)
  },
  {
    path: 'settings',
    loadChildren: () => import('./settings/settings.routes')
      .then(r => r.settingsRoutes)
  }
];
settings/settings.routes.tstypescript
import { Routes } from '@angular/router';

export const settingsRoutes: Routes = [
  {
    path: '',
    loadComponent: () => import('./settings.component')
      .then(c => c.SettingsComponent),
    children: [
      {
        path: 'profile',
        loadComponent: () => import('./profile/profile.component')
          .then(c => c.ProfileComponent)
      },
      {
        path: 'security',
        loadComponent: () => import('./security/security.component')
          .then(c => c.SecurityComponent)
      }
    ]
  }
];

loadComponent melakukan lazy-load satu komponen. loadChildren dengan file route melakukan lazy-load seluruh area fitur. Keduanya menghasilkan chunk terpisah yang di-fetch browser sesuai kebutuhan.

Siap menguasai wawancara Angular Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Menangani SharedModules dan Dependensi Umum

SharedModules — modul catch-all yang mengekspor komponen, direktif, dan pipe yang sering digunakan — merupakan blocker paling umum selama migrasi. Schematic tidak dapat menghapusnya secara otomatis karena beberapa modul mengimpornya.

Solusinya: konversi deklarasi shared ke standalone satu per satu, kemudian hapus SharedModule setelah tidak ada yang mengimpornya lagi.

typescript
// Sebelum: SharedModule mengekspor ulang semuanya
@NgModule({
  declarations: [LoadingSpinner, TooltipDirective, TruncatePipe],
  exports: [LoadingSpinner, TooltipDirective, TruncatePipe],
  imports: [CommonModule]
})
export class SharedModule {}

// Sesudah: Setiap deklarasi adalah standalone, impor langsung
// loading-spinner.component.ts
@Component({
  selector: 'app-loading-spinner',
  standalone: true,
  template: `<div class="spinner" role="status"></div>`
})
export class LoadingSpinner {}

Konsumen sekarang mengimpor LoadingSpinner secara langsung alih-alih seluruh SharedModule. Bundler hanya menyertakan komponen spesifik yang dibutuhkan setiap route.

Menerapkan Pengembangan Standalone-Only

Setelah migrasi, mencegah NgModules baru masuk kembali ke codebase sangat penting. Angular menyediakan opsi compiler TypeScript untuk hal ini.

tsconfig.jsonjson
{
  "angularCompilerOptions": {
    "strictStandalone": true
  }
}

Dengan strictStandalone diaktifkan, setiap upaya untuk membuat komponen, direktif, atau pipe non-standalone akan menghasilkan error kompilasi. Ini memastikan arsitektur baru diterapkan di seluruh tim.

Peningkatan Performa: Ukuran Bundle dan Lazy Loading

Manfaat performa utama dari standalone components berasal dari lazy loading yang granular. Dengan NgModules, lazy loading beroperasi di tingkat modul — mengimpor satu komponen dari sebuah modul akan menarik semua deklarasi yang diekspor modul tersebut. Standalone components memutus coupling ini.

Benchmark pada aplikasi enterprise menengah (200+ komponen) menunjukkan hasil berikut:

| Metrik | Berbasis NgModule | Standalone | Peningkatan | |--------|-------------------|------------|-------------| | Bundle awal | 485 KB | 218 KB | -55% | | Chunk lazy terbesar | 142 KB | 38 KB | -73% | | Time to Interactive | 3.2 detik | 1.8 detik | -44% | | Waktu build (esbuild) | 12.4 detik | 8.1 detik | -35% |

Angka-angka ini berasal dari penghapusan overhead resolusi modul dan memungkinkan bundler mengeliminasi export yang tidak terpakai di tingkat komponen, bukan di tingkat modul.

Pengujian Standalone Components

Unit test menjadi jauh lebih sederhana dengan standalone components. Konfigurasi TestBed tidak lagi memerlukan import seluruh modul untuk memenuhi dependensi sebuah komponen.

hero-list.component.spec.tstypescript
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HeroListComponent } from './hero-list.component';
import { HeroService } from '../services/hero.service';

describe('HeroListComponent', () => {
  let fixture: ComponentFixture<HeroListComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [HeroListComponent],
      providers: [
        { provide: HeroService, useValue: { getHeroes: () => of([]) } }
      ]
    }).compileComponents();

    fixture = TestBed.createComponent(HeroListComponent);
  });

  it('should render hero cards', () => {
    fixture.componentRef.setInput('heroes', mockHeroes);
    fixture.detectChanges();
    const cards = fixture.nativeElement.querySelectorAll('app-hero-card');
    expect(cards.length).toBe(mockHeroes.length);
  });
});

Komponen langsung dimasukkan ke array imports dari TestBed.configureTestingModule. Semua dependensi yang dideklarasikan sudah diselesaikan melalui imports komponen itu sendiri, sehingga tidak diperlukan import modul tambahan.

Kesalahan Umum dalam Migrasi

Circular imports antar standalone components. Ketika komponen A mengimpor komponen B dan B mengimpor A, compiler TypeScript akan melempar error circular dependency. Solusinya: ekstrak interface yang dibagikan ke file terpisah atau gunakan forwardRef() sebagai solusi sementara saat melakukan refactoring rantai dependensi.

Library pihak ketiga yang masih menggunakan NgModules. Banyak library telah bermigrasi ke standalone, tetapi beberapa paket legacy masih mengekspor NgModules. Impor modul-modul ini langsung di array imports standalone component — Angular mendukung pencampuran import standalone dan berbasis modul.

Provider hilang setelah menghapus AppModule. Service yang sebelumnya disediakan di array providers root module harus dipindahkan ke ApplicationConfig di app.config.ts atau menggunakan providedIn: 'root' di decorator @Injectable. Service dengan scope route harus menggunakan array providers di konfigurasi route.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Kesimpulan

  • Schematic resmi Angular CLI mengotomatisasi 80-90% migrasi melalui tiga tahap berurutan: konversi deklarasi, hapus modul, ganti bootstrap
  • Routing modules memerlukan konversi manual dari loadChildren dengan NgModules ke loadComponent atau file route standalone
  • SharedModules merupakan blocker utama — konversi setiap deklarasi shared ke standalone satu per satu, kemudian hapus modulnya
  • Aktifkan strictStandalone di tsconfig untuk mencegah NgModules baru diperkenalkan pasca-migrasi
  • Ukuran bundle berkurang 30-55% berkat tree-shaking tingkat komponen dan lazy loading granular dengan loadComponent
  • Unit test menjadi jauh lebih sederhana — impor standalone component langsung di TestBed tanpa konfigurasi modul
  • Zoneless change detection Angular 21 dan signal-based reactivity berpadu secara alami dengan arsitektur standalone untuk performa maksimal

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Tag

#angular
#standalone components
#migration
#tutorial

Bagikan

Artikel terkait