← Back to Articles         
How was Angular bootstrapped before standalone APIs?

How was Angular bootstrapped before standalone APIs?

Published on 02 Oct 2026     6 min read Angular
Older Angular vs Modern Angular

How was Angular bootstrapped before Standalone APIs?

1. What is Angular Bootstrapping?

Bootstrapping is the process Angular uses to start the application.

When an Angular application loads, Angular needs to know:

  • Which module is the root module
  • Which component is the root component
  • Which providers/services are available
  • Where the root component should be rendered in index.html

Before standalone APIs became the preferred approach, Angular applications were normally bootstrapped using an NgModule, usually AppModule.

The basic flow was:

index.html
    ↓
main.ts
    ↓
AppModule
    ↓
AppComponent
    ↓
Application starts

2. Traditional NgModule-Based Bootstrapping

A typical older Angular application contained:

src/
│
├── main.ts
├── index.html
│
└── app/
    ├── app.module.ts
    ├── app.component.ts
    └── app.component.html

The important difference is:

Before standalone Angular, main.ts bootstrapped AppModule, and AppModule bootstrapped AppComponent.


3. Full Simple Sample Program

index.html

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>My Angular App</title>
  <base href="/">
</head>

<body>

  <app-root></app-root>

</body>
</html>

The browser initially sees:

<app-root></app-root>

Angular later creates the root component and renders it here.


app.component.ts

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent {

  title = 'My Angular Application';

}

Notice that older Angular components did not normally use:

standalone: true

Instead, the component belonged to an NgModule.


app.component.html

<h1>{{ title }}</h1>

<p>Angular application started successfully.</p>

4. app.module.ts

This was the central file in traditional Angular applications.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';

@NgModule({

  declarations: [
    AppComponent
  ],

  imports: [
    BrowserModule
  ],

  providers: [],

  bootstrap: [
    AppComponent
  ]

})
export class AppModule { }

The @NgModule() metadata tells Angular how the application is organized.

declarations

declarations: [
  AppComponent
]

Components, directives and pipes belonging to the module were declared here.

For example:

declarations: [
  AppComponent,
  HomeComponent,
  ProductComponent
]

imports

imports: [
  BrowserModule
]

Other Angular modules required by the application were imported here.

For example:

imports: [
  BrowserModule,
  FormsModule,
  HttpClientModule,
  AppRoutingModule
]

providers

Services or other dependency-injection providers could be registered here.

providers: [
  ProductService
]

Although services commonly used:

@Injectable({
  providedIn: 'root'
})

bootstrap

This was especially important:

bootstrap: [
  AppComponent
]

It tells Angular:

AppComponent is the root component that should be created when this module starts.


5. main.ts

The application was started from main.ts.

import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';

platformBrowserDynamic()
  .bootstrapModule(AppModule)
  .catch(err => console.error(err));

This is the key traditional bootstrapping code:

platformBrowserDynamic()
  .bootstrapModule(AppModule);

Angular bootstraps the module, not the component directly.


6. Complete Bootstrapping Flow

Suppose the browser loads:

<app-root></app-root>

The startup process is approximately:

Browser loads index.html
        ↓
main.ts executes
        ↓
platformBrowserDynamic()
        ↓
bootstrapModule(AppModule)
        ↓
Angular reads @NgModule
        ↓
AppModule bootstrap: [AppComponent]
        ↓
Angular creates AppComponent
        ↓
Angular finds selector:
app-root
        ↓
Finds <app-root> in index.html
        ↓
Renders AppComponent
        ↓
Application starts

So there are effectively two bootstrapping steps:

main.ts
   ↓
AppModule
   ↓
AppComponent

7. What does platformBrowserDynamic() mean?

Consider:

platformBrowserDynamic()
  .bootstrapModule(AppModule);

platformBrowserDynamic()

It creates an Angular browser platform capable of running the Angular application.

bootstrapModule(AppModule)

It tells Angular:

Start the application using AppModule as the root NgModule.

Angular then examines:

@NgModule({
  bootstrap: [AppComponent]
})

and determines that:

AppComponent

is the root component.


8. Before Standalone vs Standalone Angular

This is the most important comparison.

Before Standalone

main.ts

platformBrowserDynamic()
  .bootstrapModule(AppModule);

app.module.ts

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule
  ],
  bootstrap: [
    AppComponent
  ]
})
export class AppModule { }

Flow:

main.ts
   ↓
AppModule
   ↓
AppComponent

9. Modern Standalone Bootstrapping

Standalone Angular removes the requirement for a root AppModule.

A modern application commonly uses:

import { bootstrapApplication } from '@angular/platform-browser';

import { App } from './app/app';

bootstrapApplication(App)
  .catch(err => console.error(err));

Now the flow becomes:

main.ts
   ↓
bootstrapApplication(App)
   ↓
App

There is no mandatory:

AppModule

in between.


10. Modern Example with app.config.ts

A modern Angular application may have:

src/
│
├── main.ts
│
└── app/
    ├── app.ts
    ├── app.html
    ├── app.config.ts
    └── app.routes.ts

main.ts

import { bootstrapApplication } from '@angular/platform-browser';

import { App } from './app/app';
import { appConfig } from './app/app.config';

bootstrapApplication(App, appConfig)
  .catch(err => console.error(err));

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()
  ]

};

app.ts

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

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
    <h1>Welcome to Angular</h1>
  `
})
export class App {
}

Now Angular directly bootstraps:

bootstrapApplication(App, appConfig);

11. Side-by-Side Comparison

Traditional NgModule Standalone
AppModule required Root AppModule not required
bootstrapModule() bootstrapApplication()
platformBrowserDynamic() bootstrapApplication()
Components declared in declarations Components can be standalone
Modules imported into imports Dependencies imported by standalone components or configured with providers
Root component specified by AppModule Root component passed directly
AppModule → AppComponent Directly bootstrap root component

The key code difference is:

Old

platformBrowserDynamic()
  .bootstrapModule(AppModule);

Modern

bootstrapApplication(App, appConfig);

12. What Happened to AppModule?

Before standalone APIs:

main.ts
   ↓
AppModule
   ↓
AppComponent

With standalone APIs:

main.ts
   ↓
App

Configuration that traditionally lived in AppModule can now be expressed using standalone components and provider APIs such as:

provideRouter(routes)
provideHttpClient()

and application-level configuration:

ApplicationConfig

13. Easy Way to Remember

Think of the old architecture as:

main.ts
   ↓
Start Module
   ↓
Module starts Component
bootstrapModule(AppModule)

Modern standalone Angular:

main.ts
   ↓
Start Component
bootstrapApplication(App)

That is the fundamental change.


Key Points

  • Traditional Angular applications were organized primarily around NgModules.
  • AppModule was normally the application's root module.
  • main.ts bootstrapped AppModule.
  • AppModule identified the root component through:
bootstrap: [AppComponent]
  • The traditional startup API was:
platformBrowserDynamic()
    .bootstrapModule(AppModule);
  • Components were normally registered under:
declarations: []
  • Standalone Angular removes the requirement for a root NgModule.
  • Modern applications can directly bootstrap a standalone root component:
bootstrapApplication(App, appConfig);

Interview memory line

Before standalone APIs, Angular typically bootstrapped the root AppModule using platformBrowserDynamic().bootstrapModule(AppModule). The AppModule then identified the root component through its bootstrap array. With standalone APIs, Angular can directly bootstrap a standalone root component using bootstrapApplication().


Interview Questions and Answers

1. How was Angular bootstrapped before standalone APIs?

Before standalone APIs, Angular normally used an NgModule-based bootstrap process. main.ts called:

platformBrowserDynamic()
  .bootstrapModule(AppModule);

AppModule then specified the root component using:

bootstrap: [AppComponent]

2. What was the root module normally called?

Usually:

AppModule

defined in:

app.module.ts

3. What was the purpose of bootstrapModule()?

It started Angular using a root NgModule.

bootstrapModule(AppModule)

4. How did Angular know which component to start?

Through the root module's bootstrap array:

@NgModule({
  bootstrap: [AppComponent]
})
export class AppModule {}

5. Where was AppComponent registered?

Usually in both the declarations and bootstrap metadata:

@NgModule({
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule {}

6. What is the modern replacement?

Standalone Angular commonly uses:

bootstrapApplication(App, appConfig);

instead of:

platformBrowserDynamic()
  .bootstrapModule(AppModule);

7. Is AppModule mandatory in modern Angular?

No. Standalone Angular applications can operate without a root AppModule.


8. What is the main difference in one sentence?

Old Angular bootstrapped a module, which bootstrapped the root component; standalone Angular can bootstrap the root component directly.

← Back to Articles