Bootstrapping multiple Angular 2 applications on the same page

This post is just a simple proof-of-concept for me to experiment with running multiple Angular 2 applications in a single page. I never really questioned that it was possible, but wanted to quickly demonstrate that. In production apps I typically use a webpack-based process, but for this experiment I'm simply using SystemJS just like the Angular team does in their getting started guide.

The code for this simple example is available in my GitHub repository.

UPDATE - June 28, 2016 - Added some important considerations to know when running multiple apps in the same browser window

If you're familiar with Angular 2 then perhaps this isn't surprising, but there really isn't that much you need to do to load multiple apps on the same page. Angular 2 is based on the idea of component trees, so when you load an Angular 2 app into an HTML element it's expecting to own all the content below that element. The way you get that ownership set up is to bootstrap Angular 2. The bootstrapping process tells Angular where it should take control on the page and start doing its thing.

So when you want to run multiple Angular 2 apps, you just need to bootstrap each of them independently. The exact mechanism for how you do that depends on what tools you're using to write your Angular app, but the example below relies on SystemJS and transpilation right in the browser.

A helpful reader pointed out that there are some considerations to make when running multiple Angular 2 apps in the same browser window. Indeed, if you look at the Angular source code you'll find this comment block:

 * ## Bootstrapping Multiple Applications
 *
 * When working within a browser window, there are many singleton resources: cookies, title,
 * location, and others. Angular services that represent these resources must likewise be
 * shared across all Angular applications that occupy the same browser window. For this
 * reason, Angular creates exactly one global platform object which stores all shared
 * services, and each angular application injector has the platform injector as its parent.
 *
 * Each application has its own private injector as well. When there are multiple
 * applications on a page, Angular treats each application injector's services as private
 * to that application.
 *

So it'd be good to keep this in mind if you use this approach, and thanks to jkanczler for bringing this to my attention.

The code

In my example I created two very simple applications that don't really do anything but render an element that shows which one they are. I called them AppOneComponent and AppTwoComponent, and here's what one looks like:

import {Component} from 'angular2/core';

@Component({
    selector: 'app-one',
    template: '<h1>Hello from App One</h1>'
})
export class AppOneComponent { }  

Our main.ts file hasn't really changed except to be specific to this single app component:

import {bootstrap}    from 'angular2/platform/browser'  
import {AppOneComponent} from './app-one.component'

bootstrap(AppOneComponent);  

There's really nothing to them. To get these two components to load I only needed to make a small update the index.html file to tell SystemJS about these two applications, and then actually import the main.ts file that calls bootstrap on each:

<!DOCTYPE html>  
<html>

<head>  
  <title>Angular 2 QuickStart</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="styles.css">

  <!-- 1. Load libraries -->
  <!-- IE required polyfills, in this exact order -->
  <script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.0/es6-shim.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.26/system-polyfills.js"></script>
  <script src="https://npmcdn.com/angular2@2.0.0-beta.15/es6/dev/src/testing/shims_for_IE.js"></script>

  <script src="https://code.angularjs.org/2.0.0-beta.15/angular2-polyfills.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.26/system.js"></script>
  <script src="https://npmcdn.com/typescript@1.8.10/lib/typescript.js"></script>
  <script src="https://code.angularjs.org/2.0.0-beta.15/Rx.js"></script>
  <script src="https://code.angularjs.org/2.0.0-beta.15/angular2.dev.js"></script>

  <!-- 2. Configure SystemJS -->
  <script>
    System.config({
        transpiler: 'typescript', 
        typescriptOptions: { emitDecoratorMetadata: true }, 
        //
        // We need to define a package for each of the applications we want to 
        //  run on the page
        //
        packages: {
          'app-one': {defaultExtension: 'ts'},
          'app-two': {defaultExtension: 'ts'}  
        } 
      });

      // Now that the SystemJS configuration is ready we can import the 
      //  code and let them start up independently
      //
      System.import('app-one/main').then(null, console.error.bind(console));
      System.import('app-two/main').then(null, console.error.bind(console));
  </script>
</head>

<!-- 3. Display the application -->

<body>  
  <app-one>Loading app one...</app-one>
  <app-two>Loading app two...</app-two>
</body>

</html>  

When you load this in a browser you should see the following:

Pretty simple I know, but also pretty powerful to realize this is an opportunity should you want to use it. I was stuck in the mindset that an Angular 2 app needs to be a single page application, and it kinda does from a component tree POV, but it is possible to have multiple apps running.

Again, the code for this is available here.

As always, if you have any comments or feedback please let me know in the comments!

comments powered by Disqus