You are building an Angular web-app which has some common core functionalities to be used by multiple of your clients or brands, however, each of those brands require to have their own labelling, logos, and general look and feel applied to their website. So what you probably want to do is to have one Angular app, which can have multiple themes where you can deploy it with a specific theme by applying it during build time. In this article, I present a solution to re-brand/white label your Angular app by sharing one codebase and be able to build and deploy it with multiple themes.

Having a single repository and sharing the same codebase across your rebranded websites saves you a good deal of time and effort, which in turn boosts productivity. And this code sharing process promotes code maintainability and reusability, which are two of the most important characteristics of good code.

Angular project structure:

1) Styles

As we mentioned earlier, each of our brands will have it's own unique theme, and when we talk about themes, we mean CSS styles. So what we are going to do is create one _variables.scss stylesheet per brand, where we will store the brand's distinct colors, font stacks, and any other css value (like backgrounds, borders...), and we will store those values in SASS Variables which are very simple to use, yet very powerful.

$primary-font: #14152D;
$secondary-font: #14152D;
$primary-background: #F9F9FB;
$confirm-button-background: #14152D;
$confirm-button-text: #F9F9FB
$cancel-button-text: #14152D;
$cancel-button-background: #F9F9FB;
_variables.scss

Then we will create a styles directory, which is organized to contain multiple subdirectories, each for a brand, where each subdirectory will contain the respective _variables.scss sheet per brand. This process will make our lives easier as it will enable us to add a new theme and deploy our website for new brands in a matter of minutes by adding it's subfolder which contains its variables stylesheet.

src/
    styles/
        brand-a/
            _variables.scss
        brand-b/
             _variables.scss
        ...
The styles directory structure

This process will allow us to access those SASS variables anywhere across our app, and all we have to do is simply import this stylesheet in our components' and/or global stylesheets by adding one line: @import 'variables';, and our code will be able to identify which variables file to import after we build our app with the brand's specific configuration. I will tell you how we will do this in the next steps.

2) Configuring the Angular Build:

Now we need to find a way to tell our angular app to use the specific css values for each brand i.e: for brand A, use the variables sheet located in src/styles/brand-a/_variables.scss. Therefore, we will create new build configurations for each brand, and assign the variable sheet needed for each build. And we will also add the commands needed by Angular to serve those builds separately. So, we will modify our angular.json file which is located in our project's main directory and add a new configuration for the new theme under build -> configurations and serve -> configurations (Eg below), which basically specifies to the complier which stylesheets to use when building and running the app.

...
"architect": {
        "build": {
           ...
          "configurations": {
            ...
            "brand-a": {
              "stylePreprocessorOptions": {
                "includePaths": [
                  "src/styles/brand-a/"
                ]
              }
            },
            "brand-b": {
              "stylePreprocessorOptions": {
                "includePaths": [
                  "src/styles/brand-b/"
                ]
              }
            }
        },
        ...
        "serve": {
          ...
          "configurations": {
            "production": {
              "browserTarget": "our-angular-app:build:production"
            },
            "brand-a": {
              "browserTarget": "our-angular-app:build:brand-a"
            },
            "brand-b": {
              "browserTarget": "our-angular-app:build:brand-b"
            }
        },
     }
     ...
angular.json

Building and running the app with a specific theme:

Now all we need to do to build and serve the app for a particular brand is to pass in the configuration argument to the build/serve ng commands (Eg: ng serve -—configuration=brand-a, ng build —-configuration=brand-b)

3) Adding brand specific logos & icons:

Along with the css theme, each brand will have its own icons, labels, and logos. And we are writing up a solution that will be applied during build time, where each brand's app will be built and deployed separately. So evidently, each of our brand's sites will be hosted on its own domain. The reason I am mentioning this now is because we will leverage the use of those different domain names in order to let our app know which icons and images to display.

First, we will need a place to store our assets (icons and images). Of course, we can store them in an assets folder in our project and divide it into subfolders like we did for our stylesheets, however, this is not efficient at all, as the number of assets will grow with each newly added brand, and in turn this will increase our served bundle sizes dramatically, so we will not do that. Hence, we will create a simple http server that will have only one mission, which is to serve us those assets. Our files will be stored in a static assets directory, which includes multiple subdirectories, one for each brand. The name of a subdirectory should be exactly the same as its respective brand's domain name (this is important as you will see in the next step where we retrieve those asset files in our app). ex: brand-a will have its assets stored in assets/brand-a.

assets/
    brand-a/
            main-logo.svg
            loading-screen.png
             …
    brand-b/
            main-logo.svg
            loading-screen.png
             …
our assets directory

The brand's subdirectory would contain all of the icons and logos used for each brand and our files should be named in simple, generic, and self-explanatory names ex: main-logo.svg.

Retrieving the assets in our Angular app:

Okay so now we need to think of a way to retrieve those assets in our angular code based on the current active domain name. So let's build a global method that would take as input the current domain name and the desired asset (ex: give me the main-logo.svg of brand-a.com), and returns the full path to this asset (https://our-assets-server.com/assets/brand-a/main-logo.svg). Wait you said global method? which does some basic string manipulation and concatenation? Say no more, when it comes to building a tool that could transform strings easily and globally in Angular, pipes are your best friend. It is one of my favorite tools of choice in our arsenal as Angular developers. That being said, let's build our pipe!

import { Injectable, Pipe, PipeTransform } from '@angular/core';
import { environment } from '../../../environments/environment';

@Pipe({
  name: 'assetsSource'
})
@Injectable({
  providedIn: 'root'
})
export class AssetsSourcePipe implements PipeTransform {

  private subDomain: string;
  
  // This will be the url to our assets server.
  // Ex: https://our-assets.com/assets
  private assetsURL: string;

  constructor() {
    this.subDomain = window.location.hostname;
    this.assetsURL = environment.urls.assets;
   }

  transform(imgName: string): string {
    return `${this.assetsURL}/${this.subDomain}/${imgName}`;
  }

}
assets-source.pipe.ts

Basically, what this simple pipe does is take in the name of the asset we want (imgName), detects the current domain name (our brand), and transforms the image name into the full path that leads to our desired asset for the respective brand. And here is how we are going to use it. This way, we can retrieve our assets easily by using our pipe in any view in our app, eg. below.

<div>
  <img class="loading-screen"
       [src]="'loading-screen.svg' | assetsSource">
</div>
loading-screen.component.html

Conclusion:

I hope this article is helpful enough for anyone who is intending to re-brand/white label their angular app and is looking for a feasible solution to handle multiple themes shared by the same codebase.


If you have a problem and no one else can help. Maybe you can hire the Kalvad-Team.