Few months ago, I started working on a new project, the biggest problem for us, is the application is very big, so the bundle size.

NOTE: We were using partial _scss-variable everywhere. The partial scss will never be added to generated css.

The Struggle#

Believe me struggle to reduce the bundle size is something we all struggle with, it is the same with us, and I am crazy about optimizing things, and want my bundle to be as small as possible once my application is stable and in running state. This project was new as I started in Feb 20, and it takes sometime to adjust into a new project and team, so I don't wanted to directly jump into optimizing things. I did implemented lazy loading and upgraded to Angular 9 to reduce our bundle size upto some extent, but still the idle bundle size for was a dream.

Ray of Hope #

One thing you need to succeed is hope that you can do it, This is what I got while working on one of the issues, and thanks to Igor who introduced me to source-map-explorer for which I created an builder to use with angular.

If you add and run this builder in your application, it will give you size of each and every chunks, including services, components, directives, the angular framework all third party library size which is adding into your final bundle size.

The output of the analyzer looks like below:

Analyzer Output 1.0

The above statistics is for entire application, you can zoom out to see more details as they will be hidden.

Showing Each Component Size 1.1

The above image is what i got after a zoomed out and clicked on src folder, now i can see the size of each and every component in my main bundle, the builder gives you an option to analyze the lazy loaded chunks as well.

After I saw the same report for my application, I was surprised a bit, most fo the component was minimum of 90KB 😢. Huge right, considering we had around 160 components, you can consider the pain, specially when we add a new component. Considering now i know the size of components, I tried to analyze more, why they are so big.

Finding the culprit#

Now as I knew the size, next thing I had to figure out was who is the culprit?

While working on one issue, suddenly i found below code, you can notice there is nothing being used from scss-variables still it is imported.I decided to remove it, and i decided to analyze the bundle again, the size got reduced by 20KB, and this is one of the component, there were few more like this.

@import '../../../themes/scss-variables';

:host {
  .image,
  .placeholder,
  .spinner {
    display: none;
    width: 100%;
    position: relative;
  }

So I figured out somehow, we are not using scss properly which is causing this bundle size to increase.

Baby Steps for solution#

One thing which I have learned over the years is, take baby steps when you are working on a new project. I decided to take one step at a time, removing unused scss imports was the first step.

Remove Unused imports#

To give you an idea about our app, we are using material custom theme,so we have lots of scss files. We had custom-material-theme.scss where we had our custom colors, in few components I found we are importing custom-material-theme too.

@import '../../../../themes/custom-material-theme';
@import '../../../../themes/scss-variables';

:host {
  .documents-list {
    }
}

I realized this is totally unnecessary, but as it is imported, its responsible for increasing the bundle size, next step remove all imports for custom-material-theme.scss This exercise helped us to decrease the bundle size by 250KB in main bundle and around 250-300KB from lazy loaded module.

Before Removing imports
After Removing Imports

Setting Budget Size#

One good thing about Angular is it knows what you can do wrong, so it actually offers budgets to keep your bundle size in check. If you are not using it, please use it. When i joined the team, i added the anyComponentStyle check and build failed, to my surprise most of the components had styling of more than 65kb.

"budgets": [
  {
    "type": "initial",
    "maximumWarning": "2mb",
    "maximumError": "5mb"
  },
  {
    "type": "anyComponentStyle",
    "maximumWarning": "6kb",
    "maximumError": "10kb"
  }
]

Using ViewEncapsulation for Components#

I had used ViewEncapsulation in past while working on a component library with my previous employer. We had used ViewEncapsulation.None because we wanted the developers to override the component styling. Most of the component library use the encapsulation to ViewEncapsulation.None so it can be overwritten, what it does is, it writes the styling in global css file generated.

Let's write some code to Reproduce the issue, I am not going to talk about how to setup the custom theme in material, you can download the code from: https://github.com/santoshyadav198613/scssdemo

After downloading go ahead and run the below command.

npm run analyze

You can see the size of employee and department component, they are more than 90KB, same issue which we are facing.

Now let's get it fixed:

Open the employee.component.ts and add the below code, you can see we are using encapsulation: ViewEncapsulation.None and add the same property to department.component.ts

import { ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-employee',
  templateUrl: './employee.component.html',
  styleUrls: ['./employee.component.scss'],
  encapsulation: ViewEncapsulation.None
})

run the below command again to analyze the bundle.

npm run analyze

You can see the size of employee and department component is already reduced by around 25-30KB. This is for one component, consider the large code base with many components, it will reduce the bundle size in MB's.

But wait, we are still not done, using ViewEncapsulation.None adds the styling to global file, lets add <h1> in both the component.

// In employee component
<h1>
  Employee
</h1>

// In department component
<h1>
  Department
</h1>

Next add the below styling in employee.component.scss

h1 {
  background-color: $color-nordic-blue;
  color: $color-white;
}

and add the below styling in department.component.scss


h1 {
  background-color: $color-green;
  color: $color-white;
}

Run the application using ng serve you will notice the employee and department both having same styling, this is due to encapsulation set to None.

Employee View
Department View

Now rather than fixing and optimizing our application we introduced another issue. Don't worry let's fix this next.

NOTE: I have seen many projects using :host to wrap styling, see the below code and check if you can remove it.

Using HostBinding to wrap styling#

HostBinding let's you use DOM property as a host-binding property this is what we are going to do next.

Open employee.component.ts and add the below code:

 @HostBinding('class') class = 'app-employee';

and in department.component.ts add the below code:

  @HostBinding('class') class = 'app-department';

The above code will add class app-employee to EmployeeComponent and app-department class to DepartmentComponent let's inspect the element to see it.

NOTE: You can use the class name property to be anything, I prefer naming it to same as selector name so it is easy to remember if I want to override it.

Next let's wrap your classes into the newly added css property.

Open department.component.scss and add the below code to wrap all css inside app-department class.

.app-department {
  h1 {
    background-color: $color-green;
    color: $color-white;
  }

  .full-width-table {
    width: $full-width;
  }
}

Do the same for employee.component.scss and run the application now everything works. If you inspect and select the h1 tags and view the Styles tab you will notice something like below:

You can run the analyze command again to see the bundle size, there should not be any change.

When not to use this approach#

This approach adds the styling to global stylesheet, so if you don't want anyone to override the component styling, this approach is not for you.

Stats from my application#

Wondering what it did to my application, below is the stat, and let me tell you its still not completed.

Bundle Size

Conclusion#

The Bundle size of our application is big, and when i joined the team, it was also a concern for me, but initially i tried some optimization techniques like lazy loading, which helped, but it was not clear why still chunk size is big, understanding the size for each component helped me figure out, where to look for the issue.

We had used :host everywhere we replaced it with @HostBinding('class') and adding it with ViewEncapsulation.None helped us reduce the bundle size.

Also be strict while doing code reviews, check for the imports properly if they are correct of being used properly.

The optimized code is available in   https://github.com/santoshyadav198613/scssdemo/tree/feat--add-encapuslation

So if you are also struggling with such issue, do the similar exercise and share your stats with community.