Standalone by Default (v20+)

📚 What Changed in v20?

Starting with Angular 19/20, standalone: true is the default:

  • No need to declare: standalone: true is implicit
  • To opt-out: Must explicitly set standalone: false
  • Cleaner decorators: Less boilerplate in @Component
  • NgModules still work: For legacy code compatibility

📅 Standalone Evolution

v14

Introduced as opt-in

Required explicit standalone: true

v17

Default for ng generate

CLI generates standalone by default

v19+

Language default

standalone: true is implicit, no declaration needed

🎯 Interview Questions

  • Q1: Do you need to write standalone: true in Angular 20?
  • A: No! It's the default. Only write standalone: false if you need NgModule-based component.
  • Q2: Can you still use NgModules in Angular 20?
  • A: Yes, NgModules still work. Set standalone: false on components that need to be declared in modules.
  • Q3: What imports do standalone components need?
  • A: Declare dependencies in the imports array of @Component decorator - other components, directives, pipes.
  • Q4: How do you provide services in standalone apps?
  • A: Use providedIn: 'root' in services, or provide in app.config.ts providers array, or component providers array.

🔄 Before vs After v20

v14-18 (Explicit)

@Component({
  selector: 'app-example',
  standalone: true,  // Required!
  imports: [CommonModule],
  template: '...'
})
export class ExampleComponent {}

v20+ (Implicit) ✅

@Component({
  selector: 'app-example',
  // standalone: true is implicit!
  imports: [CommonModule],
  template: '...'
})
export class ExampleComponent {}

🎯 This Component

Notice this component's decorator - there's no standalone: true!

@Component({
  selector: 'app-standalone-default-demo',
  changeDetection: ChangeDetectionStrategy.OnPush,
  template: `...`
})
export class StandaloneDefaultDemoComponent {}

In Angular 20+, this component is automatically standalone.

💻 Standalone by Default Examples


// ===== Angular 20+ Component (standalone by default) =====

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-component',
  // No standalone: true needed!
  imports: [SomeDirective, SomePipe],
  template: `<p>Hello World!</p>`
})
export class MyComponent {}

// ===== Directive (also standalone by default) =====

@Directive({
  selector: '[appHighlight]',
  // No standalone: true needed!
})
export class HighlightDirective {}

// ===== Pipe (also standalone by default) =====

@Pipe({
  name: 'myPipe',
  // No standalone: true needed!
})
export class MyPipe implements PipeTransform {
  transform(value: string): string {
    return value.toUpperCase();
  }
}

// ===== Opting OUT to use NgModule =====

@Component({
  selector: 'app-legacy',
  standalone: false, // Explicit opt-out!
  template: `<p>I belong to a module</p>`
})
export class LegacyComponent {}

// Then declare in NgModule:
@NgModule({
  declarations: [LegacyComponent],
  exports: [LegacyComponent]
})
export class LegacyModule {}

// ===== Full Application Structure (v20+) =====

// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { App } from './app/app';
import { appConfig } from './app/app.config';

bootstrapApplication(App, appConfig);

// app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient } from '@angular/common/http';
import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(),
    // provideAnimations(),
    // other providers...
  ]
};

// app.ts (root component)
@Component({
  selector: 'app-root',
  imports: [RouterOutlet, HeaderComponent, FooterComponent],
  template: `
    <app-header />
    <router-outlet />
    <app-footer />
  `
})
export class App {}

// app.routes.ts
export const routes: Routes = [
  { path: '', component: HomeComponent },
  { 
    path: 'feature',
    loadComponent: () => import('./feature/feature').then(m => m.FeatureComponent)
  }
];