Tuesday, August 8, 2017

SPFx bundle size when using Office UI Fabric React components

I was making some updates to a SharePoint Framework web part which utilized the React Office UI Fabric components today. Previously when I built the web part, the bundle .js size was about 10kb. When I built it today the size was suddenly close to 600kb.

image

The reason turns out to be how the build process works in SPFx. It now bundles Office UI Fabric components instead of re-using whatever SPO has installed. It actually makes sense to de-couple the dependency and ensuring your web parts won’t break if Microsoft decides to upgrade Office UI Fabric on their side.

Ideally I would like to externalize Office UI Fabric to a version which I control. Reading up on How to use the Office UI Fabric React in a Safe Way in Your Solution we’re told to put an explicit dependency in your project to Office UI Fabric, so you have control over the version. The biggest issue right now might be that can you really expect an average developer to hit that doc page? Hopefully the version will be added in packages.json in a future version of the SPFx generator. (See the end of this post for how to add the reference.)

It is however possible to reduce the footprint somewhat. My original code looked like this:

import { Dialog, DialogType, DialogFooter, Button, ButtonType, TextField } from 'office-ui-fabric-react';

A simple one-liner where I include all the components I need. The caveat is this way of referencing will include everything from office-ui-fabric-react. If I instead import each single component with an explicit path, only those components will be included. This is all documented at https://dev.office.com/sharepoint/docs/spfx/web-parts/guidance/office-ui-fabric-integration where it discusses static linking vs. dynamic linking.

import { Dialog, DialogType, DialogFooter } from 'office-ui-fabric-react/lib/Dialog';
import { Button, ButtonType } from 'office-ui-fabric-react/lib/Button';
import { TextField } from 'office-ui-fabric-react/lib/TextField';

This reduced my bundle down to 168kb, way better than 600kb, but nowhere as good as the 10kb.

The other drawback is that multiple web parts, all using the same version of the Office UI Fabric components, will each include their own version – significantly adding to the load time of a page.

..wait, there is more…

Office UI Fabric recently made a breaking change if you use their CSS styles for grids etc. I was also tapping into the fact that this CSS was pre-loaded, but it suddenly broke. I logged an issue about this at https://github.com/SharePoint/sp-dev-docs/issues/717, and the issue is currently resolved and Microsoft is working on a solution.

The proper way right now is to include your own version of Fabric CSS in your web part. I generated mine using a service from KWizCom, which let’s you pass in a prefix, and it will generate a file for you (https://apps.kwizcom.com/libs/office-ui-fabric-js/1.4.0/css/fabric.components.css?prefix=pzl). Next I replaced all the actual color codes with token as per Stefan Bauer’s post, to ensure proper theming, and referenced the file in my web part using:

<pre class="brush: js;">
require('./office-ui-fabric.css');
</pre>

This adds an extra 150kb to my part. Imagine doing this for multiple parts as well. Not the best page loading story. Of course, I could remove all CSS not needed to get the footprint down, but that’s not my kind of fun.

To sum it up

If you want to use Office UI Fabric Components in your SPFx part, add an explicit reference to it in your project – the docs says to use 2.0.0, but that release don’t exist, so use the latest 2.x release instead which is 2.34.2.

image

Secondly, use static references to the components you use, and not a blanket dynamic one.

If you want to use Office UI Fabric CSS, then build and bundle your own. Feel free to download my version from https://github.com/wobba/RandomFiles/blob/master/office-ui-fabric.css (v5.0.1) which include theme tokens to ensure colors are matching the themes of your tenant.

In the end my small intended change led me to a lot of work to make it proper, and the bundle went from 10kb to 315kb.