# Ways to bootstrap an application ## Approach 1 (Abandoned) ### main.ts ```ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppRoot } from './app'; import { providers } from './config'; const bootstrap = () => bootstrapApplication(AppRoot, { providers })); if (document.readyState === 'complete') { await bootstrap().catch(err => console.error(err); } else { document.addEventListener('DOMContentLoaded', () => bootstrap().catch(err => console.error(err)); } ``` ### main-server.ts ```ts! import { bootstrapApplication } from '@angular/platform-browser'; // It's akward that the server depends on the browser module import { AppRoot } from './app'; import { providers } from './config'; export default bootstrapApplication(AppRoot, { providers: [ ...providers, provideServerModule(), provideRouter(ROUTES), ... ], }); ``` ## Approach 2 (Abandoned) ### main.ts ```ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppRoot } from './app'; import { config } from './config'; const bootstrap = (extraConfig: ApplicationConfig) => bootstrapApplication(AppRoot, deepMerge(config, extraConfig))); if (document.readyState === 'complete') { await bootstrap().catch(err => console.error(err); } else { document.addEventListener('DOMContentLoaded', () => bootstrap().catch(err => console.error(err)); } export default bootstrap; ``` ### main-server.ts ```ts import { provideServerModule } from '@angular/platform-server'; import bootstrap from './main'; export default bootstrap({ providers: [ provideServerModule(), provideRouter(ROUTES), ] }); ``` ## Approach 3 (Abandoned) ### main.ts ```ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppRoot } from './app'; import { config } from './config'; const bootstrap = (appConfig: ApplicationConfig = config) => bootstrapApplication(AppRoot, appConfig); if (document.readyState === 'complete') { await bootstrap().catch(err => console.error(err)); } else { document.addEventListener('DOMContentLoaded', () => bootstrap().catch(err => console.error(err))); } export default bootstrap; ``` ### main-server.ts ```ts import { provideServerModule } from '@angular/platform-server'; import { config } from './config'; import bootstrapApp from './main'; const bootstrap = (extraConfig: ApplicationConfig) => bootstrapApp( AppRoot, deepMerge( config, { providers: [ provideServerModule(), provideRouter(ROUTES), ], }, extraConfig, ), ); export default bootstrap; ``` ## Approach 4 (Abandoned) `bootstrapApplication` code is generated internally in the CLI for both the client and server bundles. This is something that came too mind that I wanted to share. I personally do not think I'd go for this as it can feel too magical and might be harder to debug bootstrapping issues. ### main.ts ```ts import { AppRoot } from './app'; import { config } from './config'; const bootstrapAppConfig = { component: AppRoot, applicationConfig: config, }; export default bootstrapAppConfig; ``` ### main-server.ts ```ts import browserConfig from './main'; const serverConfig = { providers: [ provideServerModule(), provideRouter(ROUTES), ], }; const bootstrapAppConfig = [browserConfig, serverConfig]; export default bootstrapAppConfig; ``` ## New Approches Intro Potentially by default we might not even need to create the main-server file. Given that: * `provideServerModule` is added as a platform provider when rendering SSR/SSG. Maybe though this would cause more complexity for users when trying to figure out how to add a provider for server? * `withServerTransition` becomes redundant following https://github.com/angular/angular/pull/48253 * Handle app-shell completly differently. Currently app-shell is just another root on the server. Following some explorations I feel that we can just avoid adding additional routes instead just bootstrap the application using `AppShell` instead of `AppRoot` as component. This comes with a drawback of 1 extra file, but removed the need of having server specific routing. ## Approach 5 (New) ### main.ts ```ts import { Providers } from '@angular/core'; import { bootstrapApplication } from '@angular/platform-browser'; import { AppRoot } from './app'; import { config } from './config'; const bootstrap = (extraProviders: Providers[]|undefined, rootComponent = AppRoot) => bootstrapApplication(rootComponent, { ...config, providers: [ ...config.providers ...extraProviders, ] }); // Should `bootstrap()` be done by the CLI or not? // await bootstrap(); export default bootstrap; ``` ### main-server.ts ```ts import { Providers } from "@angular/core"; import { provideServerModule } from "@angular/platform-server"; import bootstrapApp from "./main"; const bootstrap = (extraProviders: Providers[] = []) => bootstrapApp([ provideServerModule(), ...extraProviders, ]); export default bootstrap; ``` ### app-shell.ts ```ts import { Providers } from "@angular/core"; import { provideServerModule } from "@angular/platform-server"; import bootstrapApp from "./main"; const bootstrap = (extraProviders: Providers[] = []) => bootstrapApp([ provideServerModule(), ...extraProviders, ], AppShell); export default bootstrap; ``` ## Approach 6 (New) Maybe we are trying to over thinking it and the simplest would to use `bootstrapApplication` directly. ### browser.ts ```ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppRoot } from './app'; import { config } from './config'; const bootstrap = () => bootstrapApplication(AppRoot, config); // Should `bootstrap()` be done by the CLI or not? // await bootstrap(); export default bootstrap; ``` ### server.ts ```ts // This is a bit odd that server depends on platform-browser // Maybe @angular/platform-server could have a bootstrapApplication that already provides the `ServerModule`? import { bootstrapApplication } from '@angular/platform-browser'; import { AppRoot } from './app'; import { config } from './config'; const bootstrap = () => bootstrapApplication(AppRoot, { ...config, providers: [ ...config.providers, provideServerModule(), ] }); export default bootstrap; ``` ### app-shell.ts ```ts import { bootstrapApplication } from '@angular/platform-browser'; import { AppShell } from './app-shell'; import { config } from './config'; const bootstrap = () => bootstrapApplication(AppShell, { ...config, providers: [ ...config.providers, provideServerModule(), ] }); export default bootstrap; ``` ## Approach 7 (New) ### main.ts ```ts! import { bootstrapApplication } from '@angular/platform-browser'; import { App } from './app'; import CONFIG from './app.config'; const bootstrap = () => bootstrapApplication(App, CONFIG); export default bootstrap; ``` ### main.server.ts ```ts! import { mergeConfig } from '@angular/platform-browser'; import { bootstrapApplication } from '@angular/platform-server'; import { App } from './app'; import CONFIG from './app.config'; // By default we don't need to add overrides. // This is because bootstrapApplication provides the ServerModule. // We might not even need to provide a mergeConfig in this case. const SERVER_CONFIG = {}; const bootstrap = () => bootstrapApplication(App, mergeConfig(SERVER_CONFIG, CONFIG)); export default bootstrap; ``` When using an app-shell the `SERVER_CONFIG` will be updated to: ```ts! // For the app-shell use-case we just add an additional route. // This is the same as `providerRoute([{ ... }])` which is deprecated. const SERVER_CONFIG = { providers: [{ provide: ROUTES, multi: true, useValue: [ { path: 'shell', component: AppShellComponent }, ], }], }; ``` ## Questions * Do we need to expose a way for users to override/set `platformProviders`? My 2 cents is no. * What are the cases that we need different routes on client and server? Currently there is only one case which is for the app-shell. * Should we internally call `renderApplication` from the exported `bootstrapApplication`? * Can we add additional providers after bootstrapping but before rendering such as `ɵSERVER_CONTEXT`, `INITIAL_CONFIG` etc? (If not probably Approach 3 would be needed to provide providers from the CLI/Universal * Benefits of app-shell as a route vs bootstrapped component.