A new beast in SharePoint Framework development: library component

SharePoint Framework 1.8 is out and gives us a lot of new things. Check out SPFx 1.8 release notes to learn more. Among different generally available features, we also received some items in "beta" mode. One of them is a library component type.

Library component currently is in preview and most likely will be generally available in SharePoint Framework 1.9

Let's find out what is that library component, when and how to use it. This post is not a step-by-step tutorial (you can find tutorials in the links section of the post in the very bottom), but rather an explanation of why and when we should use library components, why they were added to SharePoint Framework. The original entry in SharePoint User Voice received a lot of votes, thus this feature is long awaited.

The problem 

Imagine that you designed a home page for your communication web site with a few (let's say 4) spfx web parts in it. All of your web parts use a set of utility classes, classes to talk to SharePoint REST API, some reusable React components. We can consider this code as shared because it's used by all web parts.

If you use the default process of bundling your solution (gulp bundle && gulp package-solution), all of your web parts will have shared code included in their code base. Which basically means that on a home page you load this shared code four times! However, you need it loaded only once and immediately available for all web parts on the page. It's not very efficient to load the same code multiple times. It affects page loading performance. Also, it's a common programming pattern to move reusable pieces of code into separate libraries. 

Let's fix it!

Solution without a library component 

Move shared code out of SharePoint Framework solution

Programming patterns suggest us to move it into a separate library so let's do that! 

You should create a new folder outside of your SharePoint Framework solution. Move all shared code into that folder. Now you should create a reusable javascript package out of your shared code. 

First issue: you should setup TypeScript compilation, webpack bundling, tslint, and all other aspects of code packaging on your own. 

Imagine I have below shared code

console.log('Running inside shared component!');

export function getTime(){
    return (new Date()).toString();
}

To create a package from this component I should add webpack configuration:

var path = require('path');

module.exports = {
  entry: './index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'shared-library-component.js',
    library: 'shared-library-component',
    libraryTarget: 'umd'
  },
  mode: 'production'
};

to create a package out of my code.

Deploy the code to CDN

We want to reference our shared code inside every web part somehow. For that purpose, we should host our package globally to make it available for all of our web parts. CDN is a very good choice here. Office 365 CDN probably the best choice, because it's included for free in your tenant. However, you can use Azure CDN as well or any other CDN options available online. 

In order to enable the Office 365 CDN for your tenant, run a few PowerShell commands: 

Connect-SPOService -Url https://contoso-admin.sharepoint.com
Set-SPOTenantCdnEnabled -CdnType Public

Probably you are also interested in creating your own CDN origin for hosting shared code:

Add-SPOTenantCdnOrigin -CdnType Public -OriginUrl */SharedComponents

This piece of PowerShell makes all files under /SharedComponents path (for example in a document library with the same url) available under CDN path as well. When I say "all files" I mean all files supported by CDN policy. 

Learn more about Office 365 CDN here

Go ahead and create a library called SharedComponents in any site collection, tenant app catalog will be a good choice for such library. 

Second issue: you have to manage the process of uploading your shared javascript package to the target library (in order your files to be later available on CDN) on your own. If you want the process to be automated somehow - you have to do that on your own.

Let's say we uploaded our shared-library-component.js to SharedComponents library. How to get the CDN url of my file? Very simple, the pattern is below:

https://publiccdn.sharepointonline.com/<organisation>.sharepoint.com/<relative path to a folder/<your file>.js

For example, in my case, it will be

https://publiccdn.sharepointonline.com/mastaq.sharepoint.com/sites/appcatalog/SharedComponents/shared-library-component.js

Update your SharePoint Framework solution to make it aware of your shared component

Now in your SharePoint Framework solution open config/config.json and add new externals entry: 

"externals": {
    "shared-library-component": "https://publiccdn.sharepointonline.com/mastaq.sharepoint.com/sites/appcatalog/SharedComponents/shared-library-component.js"
  },

Why externals entry is so important? Because it prevents our shared code to be included in the web part's code base. In other words, we say that "please, don't include this dependency in the generated bundle, instead load it separately from url".

If we now try to import our shared-library-component, we will receive an error, because our SharePoint Framework solution knows nothing about this module.

There is a trick to make it aware with the help of npm link feature. In your folder with shared code, initialize a new npm project by running npm init. Give package a name exactly the same as your shared javascript package, i.e. shared-library-component. Run npm link inside the shared library project. This command will create a "shortcut" to this module from your global npm packages folder. Go to your SharePoint Framework solution folder and run npm link shared-library-component. This code will add a dependency on shared-library-component to your SharePoint Framework solution. If you go to node_modules/ you will find a shortcut to shared-library-component folder inside. 

Third issue: even with npm link you can't use the local workbench, because it will not be able to download your Office 365 CDN hosted files. Only hosted workbench will work. 

Fourth issue: you will have troubles with making changes in the shared code. Changes will not be reflected in consumer web parts until you upload changed code to CDN. It adds a lot of inconveniences.

Finally, in every web part, you can write something like this:

import * as sharedComponent from 'shared-library-component';
....
sharedComponent.getTime();

A few important things to consider here:

  • SPFx build pipeline doesn't include our shared code into the resulting bundle, because we added it into the "externals" section
  • As soon as a page contains a web part with an external component, SPFx runtime engine will add it to the page (using url from "externals" section) prior running web part's code. Important: the shared module will be loaded only once, no matter how many web parts on a page request it

As a result, we solved our problem described at the beginning. Shared code isn't included in each individual web part code base and dynamically loaded once if needed. 

Solution with library component (in beta in SharePoint Framework 1.8)

In the solution without a library component, I mentioned a few issues. Those issues add complexity to the whole approach. Library component completely eliminates all mentioned issues. Check it out how it simplifies the process. 

Create a new library component

Run 

$ yo @microsoft/sharepoint --plusbeta 

or without --plusbeta, if you're running on SPFx >1.8. Select Library as a component type.

IMPORTANT: Do you want to allow the tenant admin the choice of being able to deploy the solution to all sites immediately without running any feature deployment or adding apps in sites? Select Yes. 

Now you have your shared component ready for development. TypeScript, webpack and all other things were configured by SharePoint Framework yeoman generator (we eliminated issue #1). 

Use your shared code in web part with ease

In order to do local development, you should perform the same npm link trick. However this time you're able to use the local workbench because the code is loaded dynamically by SPFx runtime engine from node_modules. We can also make changes in the shared code (while running gulp serve) and those changes will be available in a web part-consumer immediately. So we eliminated issues #3 and #4.

Deploy shared library component code

The process is also extremely easy. gulp bundle --ship && gulp package-solution is everything you need. Then upload your resulting SPFx package to app catalog and check the mark to make it available globally. You can also use app ALM to automate this process. That way we eliminated issue #2 as well. 

Interesting observation. With library component, you shouldn't add any externals section under SharePoint Framework solution configuration. Does it mean, that shared code will be included in all web parts? - No. SPFx build pipeline is very smart and checks if dependency comes from "Library" type. In that case, SPFx pipeline automatically adds this dependency into the externals section of webpack. For those who are curious, the file which does this job is under @microsoft\sp-build-core-tasks\lib\configureWebpack\ExternalsProcessor.js 

So why I gave you a solution "without library component"? Because library component works exactly the same under the hood. It packs your library into a javascript package, then when you deploy it, your javascript package will be hosted in an app catalog or in office 365 CDN (if enabled in your tenant). For your SharePoint Framework solution package, it adds all "externals" entries internally. 

Other observations on "shared code" approach

Be careful with shared code, because in all cases it adds a bit of complexity in your solution management process. 

  1. You have only one version of a shared library component at the time in a tenant. And there is a reason for it. Imagine that two different web parts on a page require different versions of the same package. A page can only contain one javascript module at the time. Of course, you can load two versions of the same module twice, but you either will have an error or will have "the last one wins" situation. Here I mean minor versions of a shared package. Read point #3 for other versioning aspects.
  2. Be careful with updates to the shared code. Remember, that it's possibly used by multiple web parts. As soon as you deploy a newer version to a tenant, all web parts will immediately receive an updated version. Ideally, you should test that your changes don't break all other web parts, which use the same shared library component. 
  3. Versioning. What if one of your web parts require version 1.x of shared code and the other one requires 2.x version? As you remember we have only one version available at the time. In that case, you should invent a way to resolve this issue. For example, for every new major version, you can create a library with a unique name, like shared-library@1.0.js. SPFx will generate separate files and no conflicts will occur. 

Conclusion

SharePoint Framework library component will come in the next version of SharePoint Framework and currently is available in preview (or beta). Library components significantly simplify the process of hosting shared web parts code. If you have pages with lots of web parts, moving shared code into separate library components might improve your page performance and response time. 

Links: