In this post we are going to see how an Angular 2 component library can be built and then consumed using both SystemJs and Webpack. Let's go over the following topics:
- Choosing a format to publish an Angular 2 library - CommonJs
- Setting up the Typescript compiler
- Publish typings and source maps
- exposing the library public API
- How to publish component CSS
- Isolating CSS via component encapsulation
- Overriding CSS if needed
- publishing a library to npm
- Consuming a library using SystemJs
- Consuming a library using Webpack
Choosing a format to publish an Angular 2 library
During the alpha stage of Angular, the Angular bundles where initially published in SystemJs format, but now they are published in CommonJs (see here). This happened for a couple of good reasons:
- CommonJs is easily consumable by existing popular tools such as Browserify or Webpack
- CommonJs can also be easily consumed by SystemJs
- CommonJs is the node.js module format, which allows for components to be more simply used in server-side rendering
- the size of CommonJs bundles is smaller (in the case of Angular the gain was around 20%)
What does a published library look like?
Lets take a look at what the sample library (named angular2-library-example) looks like when published to npm:
The following are the different components of the library:
components.d.tsdefine the public API of the library.
- the lib folder contains the bulk of the library, in this case the Angular 2 component named
Why publish unbundled files?
Most applications consuming Angular 2 libraries will probably have a frontend build based on some combination of gulp, Jspm, SystemJs or Webpack.
For those cases its best if the consumer of the library itself does the bundling. This ensures that bundlers like webpack will give optimal results for the end user, and that they won't throw warnings.
Should bundled and minified files also be published?
Its a good idea to also publish the library as a single concatenated file, together with a minified version of it. This will allow library consumers that don't use a frontend build to still be able to use the library.
Setting up the Typescript compiler
Since version 1.5, Typescript now allows to compile the ES6 module syntax into any module format. For example, this is the standard way of importing a component using ES6 syntax:
This can then be transformed by the compiler into different module syntaxes depending on the target module format. For example this is the output for CommonJs:
We just need to configure the Typescript compiler to output CommonJs modules. Let's go over the whole Typescript configuration of the sample library:
This config ensures the following:
experimentalDecoratorsenable the use of annotations such as
module: 'commonjs'means that each file will be exported as a separate CommonJs module. for example
HelloWorld.tswill be converted into an isolated module
moduleResolution: 'node'ensure that the import paths will work exactly the same way in the browser as in a node runtime
outDiris where the output of the Typescript compiler is saved
sourcemap: truetriggers the generation of source maps
declaration: truetriggers the generation of the typings
*.d.tsfiles, that enable IDE Intellisense
Creating the public API of a library
The sample library only contains the
HelloWorld component, importable from the
components barrel. Each library should provide at least one import barrel in order to:
- simplify the import of library components via IDE Intellisense
- Make it clear which components of the library are meant to be used publicly, and which are just internal implementation details
Publish components via CommonJs
As we saw all components of the library will be inside the lib folder. To make them publicly visible in the
angular2-library-example/components barrel, we need to create a
components.js in the root of the module:
components.js barrel file, we export using the CommonJs
exports syntax any component that we want to make public. In this case only the
HelloWorld component is published.
Informing Typescript of library content
components.js would be sufficient to use the
HelloWorld component, but to enable IDE Intellisense we also need to publish
This will allow auto-completion when importing
HelloWorld. Next lets see how we can publish the CSS of a component.
How to publish CSS of an Angular 2 component
Angular 2 components work by default in emulated view encapsulation mode. This means that by default, the styles of Angular 2 components are isolated and don't affect the rest of the page. Instead those styles will (in almost all cases) affect only the component itself.
How does emulated CSS encapsulation work?
Take for example the
If we run one of the examples of the sample library, we can see that the
color:blue inline style is being applied in the following way:
_ngcontent-rgt-2 is an automatically generated attribute that Angular assigned to the outer most element of the template:
So this is how emulated encapsulation works! Its a simple trick applied at runtime to ensure that the component styles all have at least one attribute selector. This increases a lot the specificity of the component styles, making it unlikely that they ever get overridden.
How to override component CSS if needed
Although the style of a component is well isolated, it can still be easily overridden if necessary. For that, we just need to add an attribute to the body of the page:
The name of the attribute can be anything. No value is needed and the name
override makes it apparent what its being used for. To override component styles, we can then do the following:
As we can see that although the component styles have a high specificity, they can still be easily overridden if its needed - and there is always some corner case for that.
How to publish component CSS then?
The current simplest way to write component styles is inline as shown above. Here are several reasons for this:
- writing the styles and templates inline encourages the components not to grow too large
- if the CSS (or the template) starts to grow big, its a sign that the component should be split in several components
- its very convenient to have everything related to the component in a single file (like in React)
- the Angular 2 template transforms mechanism is not yet ready (its scheduled for final). But when this is ready, templates and CSS will likely be inlined everywhere via plugins, like the case of templates and the various template cache plugins in Angular 1
The convenience of distribution, combined with the good isolation and the ease of override make the combination of inline styles and emulated encapsulation a good solution for publishing component CSS.
Publishing a library to npm
Once the library is built in the
lib folder, it's time to distribute it. You probably have configured
.gitignore to exclude the lib folder. But npm uses
.gitignore by default, so the lib folder won't be published!
We need to start by creating an
.npmignore file that says what should be ignored when publishing to npm:
examples node_modules src
In this case, all folders where ignored except the
Creating a build script
In the case of a component library, we can probably keep the build very simple. Using npm scripts is likely sufficient:
This script can be run using
npm run script, and it creates a
lib folder ready to publish. Tools like webpack can also be used effectively via npm scripts.
If the approach becomes cumbersome, then its better to introduce gulp. But for most components, probably npm scripts will be sufficient.
npm semantic versioning
When publishing new versions of the library to npm, its important to use the built-in npm version command line tool according to semantic versioning:
npm version [<newversion> | major | minor | patch]
This tool will update the
package.json version, and commit the change to git.The convention for versioning is the following:
- major: breaking changes
- minor: new features, backwards compatible to current major
- patch: no new features, only bug fixes (and still backwards compatible)
Publishing to npm
In order to publish to npm, first create an account in npmjs.com.
Then setup your username in password in your local npm:
When this is done, you can simply do:
This is the essential of how to publish component published to npm. Its important to follow semantic versioning to avoid breaking the builds of library users.
How to consume an Angular 2 library
Many library consumers will probably be using a module loader and a package manager.
Lets see how the sample library can be consumed using the two most commonly used tools: SystemJs and Webpack. In both cases, the first step is to install the library using npm:
npm install angular2-library-example
Consuming a library using SystemJs
See here for an example of how to load the sample library using SystemJs
SystemJs is the most used module loader in many of the Angular 2 examples currently available. See this post for an introduction to SystemJs and the associated Jspm package manager.
This is how we configure SystemJs to import Angular 2 and our own application:
And then bootstrap the app:
A good place to keep track of what is currently possible with SystemJs is to check the scaffolding of angular-cli here over time.
Consuming a library using Webpack
Here is an example of how to consume the sample library using Webpack
The Html needed for bootstrapping an Angular 2 app is very simple:
In the case of the sample library example, the bundle file does not exist in the file system, its in memory in the
This bundle is created according to the following webpack configuration:
This configuration does the following:
- sets up a typescript loader named
ts-loader, that allows for webpack to compile typescript files while loading them
- activates source maps
- defines the entry point of the application (
One note on using webpack to import a local version of a library using npm link: You will likelly run into
cannot find module issues. Check here for a solution.
With the set of practices presented above, its definitely possible to publish an Angular 2 library today. Check some early examples of libraries that where the basis for this post:
- ng2-pagination by @@michlbrmly - an Angular 2 port of the Angular 1 paginate directive
- ng2-translate by @OCombe - An implementation of Angular 1 ng-translate, for internationalizing Angular 2 applications
The best practices for publishing libraries will evolve over time, its best to check angular-cli from time to time, to keep track of best practices and see what scaffolding is available.
Want to Get Started With Angular 2 ?
If you enjoyed this article, we invite you to subscribe to the Angular University Newsletter (bellow).
In case you are just getting started with Angular, but also if you have some experience with it, you might want to have a look at our course on Youtube which is under development:
Looking for other Angular 2 Learning Resources ?
Check our top 10 list of Angular 2 Resources:
Other posts on Angular 2
If you enjoyed this post, here some other popular posts on our blog:
- Angular 2 Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular 2 Router - Extended Guided Tour, Avoid Common Pitfalls
- How to run Angular 2 in Production Today
- How to build Angular 2 apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular 2 Forms - Template Driven, Model Driven or In-Between
- Angular 2 ngFor - Learn all Features including trackBy, why is it not only for Arrays ?
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular 2
- How does Angular 2 Change Detection Really Work ?