Freshworks Switcher- Behind the Scenes

Kavyapriya Sethu
The Freshworks Engineering Blog
8 min readAug 6, 2018

Using Web Components and GlimmerJS:

Currently, Freshworks has seven offerings in its product portfolio and we aim to continue building more awesome products. Simultaneously, our engineering teams are also growing rapidly.

The rapid proliferation of engineering artefacts demands that we build internal platforms and services to promote code reusability and maintainability. It is imperative that teams do not reinvent the wheel and essential services like authentication, forms, chat, etc. are centrally shared between each other.

Some services like Freshchat are offered as UI widgets that can be embedded inside a host product for easier and efficient communication with the customers. However, there are a few challenges with the distribution of embeddable UI widgets.

This article is a case study of our own usage of web technologies to build the UI widgets that are being used across our product suite.

The Freshworks Switcher is an embeddable widget that is placed in the sidebar of each of our products. It is a hub that lists all our products and accounts that a user is associated with. It offers a convenient way to switch between accounts across our products. A user can also sign up for a new product in our suite right away from the Freshworks Switcher.

Design Constraints:

  • Our widget should be framework agnostic.

Teams may use different frameworks or might be running different framework versions. We also need to be open to teams adopting new frameworks.

  • Make integration easier and less time-consuming.

The widget will be integrated by multiple teams.

An optimised integration surface area will save both the development and project management cost every time a team integrates the widget.

  • Decouple development and deployment cycle of the UI widget and the host product.

Web Components:

Web Components are a set of specifications which supports creating custom elements with their own behaviour. These new custom elements provide us the familiar abstraction of components.

Components have been a key abstraction in all the popular UI frameworks like React, Ember, Angular, Vue, etc.

It helps us to build smaller UI primitives like <UserAvatar/>, <LoginForm/>, and <Button/> and then use them to compose our entire UI. Here, we might use the components to build a login form or a user profile.

Web components were introduced with the hope that we will be able to extend the web with custom components like <data-picker/>, <tabs/>, etc. This would save us from re-implementing common UI elements in every new framework. Also, we will not have to wait for the web standards to introduce much-needed default UI elements.

The custom element specification is where it all begins. It enables us to create new custom HTML elements which can then be distributed and used in a DOM environment.

Because web components is a standard which is embedded into the browser, it is framework agnostic. It works like any other HTML tag, with any existing framework. This made using web components an intuitive choice for us.

Thus, we started out to create a <freshworks-switcher/>custom element.

Freshworks Switcher as a Web Component:

The <freshworks-switcher/> is simple in design. It resembles a slide out menu which opens over a fullscreen overlay.

Fig 1: Freshworks Switcher as a Web Component

Here’s how our Freshworks Switcher web components look like:

<freshworks-switcher
data-switcher-environment=”production”
data-switcher-region=”us”
data-switcher-product=”freshdesk”
>

The data-switcher-* attributes are treated as initial configuration. For example, data-switcher-product states the host product in which the Freshworks Switcher is being loaded.

Lazy Loading Web Component:

Freshworks Switcher is powered by API calls, and we did not want it to impact the initial rendering performance of the host app. So, we wanted to load the Freshworks Switcher post the initial render of the host app.

The custom element specification supports element upgrades. It allows you to use custom element tags in your HTML without defining them first.

This enables us to author a <freshworks-switcher/> in the sidebar template of the host app before we load the script for the widget. But when your script loads and it registers your web component, the custom HTML tag gets endowed with the provided definition.

There are other advantages to having a separate script for Freshworks Switcher and not bundling it with the host apps. Freshworks Switcher release cycle will be independent of the host app. And it will not impact the bundle size of the host app.

Caveats - Polyfills:

Modern browsers still have not reached a point of adopting web components. Therefore, one has to use polyfills to support web components. There are some nuances to using polyfills with the Shadow DOM specification, but we did not have to worry about it since our implementation mainly concerned custom elements.

Web Components as Compilation Targets:

Although the web components bring the abstraction of the components to the DOM, it makes developers write imperative code which means going back to manipulating the DOM. That would mean we will be throwing out all the best practices that we have adopted with unidirectional data binding.

But there exist frameworks that are bringing together the best of both worlds. And some are choosing to compile components written in existing frameworks into web components. Web components have become a compilation target.

GlimmerJS:

For the most part, we are an Ember shop. When we read that GlimmerJS components could be compiled into Web components, we wanted to give it a try. With GlimmerJS, we could utilize the benefits of Ember’s rendering engine and CLI in isolation.

The simplicity of the Freshworks Switcher widget afforded us to experiment with GlimmerJS. The project is still in its early phase, though.

Let me show you how Glimmer improved our developer experience while we were building the Freshworks Switcher.

Unidirectional Data Binding @tracked

Freshworks Switcher, as mentioned earlier, is a customised slide out menu. Most of the logic can be reduced to key state variables like isSwitcherPaneOpen. This internal state powers the UI.

Glimmer’s tracked properties provide a succinct way to unidirectionally bind data to UI. Tracked properties are analogous to Ember’s computed properties. In addition to being concise, the performance is better for both initial render and re-render.

These @trackedproperties are not limited to only Glimmer components. They can detect changes on any objects. Therefore, changes to the store can also be tracked.

import Store from ‘./utils/store’;export default class FreshworksSwitcher extends Component {
@tracked public products: Product[] = [];
private initStore(): void {
this.store = new Store();
this.store
.getProducts(this.config)
.then((products: Product[]) => (this.products = products))
}
public didInsertElement(): void {
// …

this.initStore();
// …
}
@tracked(‘products’)
get hasProductsLoaded() {
return this.products.length > 0 ? true : false;
}
@tracked(‘products’)
get productsAlreadySubscribed() {
// …
// return all freshworks product subscribed by the user
}
}

TypeScript:

Type systems have become popular in JavaScript land recently. Glimmer supports TypeScript out of the box. The editor integrations and TSLint makes the effort of learning Typescript worthwhile.

Major benefits of using Typescript:

  • TSLint raises alarms on unexpected use of any entity. This gets even more helpful when more than one person is working on the project.
  • Editor Integration autocompletes the methods exposed on an object.
  • It neatly documents your components much like React PropTypes.

Portal:

Freshworks Switcher primarily constitutes two UI elements. The trigger button placed inside the host app sidebar and the actual pane when it is opened.

UI Element: Trigger Button
UI Element: Switcher Pane

The Freshworks Switcher pane takes up the full screen and introduces an overlay on top of the host app. This modal like behaviour is easily achieved if the Freshworks Switcher pane is rendered as a sibling to the app container.

<body>
<div id=”freshworks-overlay-background”></div>
<div id=”freshworks-switcher — pane-container”></div>
<! — Your App Container →
<div id=”app”>
<div class=”app-sidebar”></div>
<div class=”app-container”></div>
</div>

But we are placing our <freshworks-switcher/> component inside the host app sidebar. So, how do we render the Freshworks Switcher pane as a sibling to the app container?

With Glimmer, you can bypass the component tree hierarchy. You can tell the Glimmer render engine to render the component in any DOM container instead of rendering it inside the parent component. This pattern is called Portal. React users may be familiar with it.

// templates/switcherPane.hbs{{#in-element modalElement}}
<div class=”switcher-pane-container”>
<! — Switcher Pane Content →
</div>
{{/in-element}}
// components/switcherPane.tsexport default class SwitcherPane extends Component {
public modalElement: HTMLElement = document.getElementById(
‘freshworks-switcher — pane-container’
);

// …
}

Economic Build Size:

Glimmer is just the view layer of Ember. So, it compiles to a significantly smaller build size. Our entire app costs around 45KB (compressed + gzipped) including the polyfills.

If the size is a concern, you can use Preact and then use either SkateJS to compile it or Svelte, the magical disappearing UI framework.

We were happy with the cost of Glimmer bundle size since the Freshworks Switcher script is not blocking the initial loading of the host app. Most of the time, it will be served from the browser cache.

Caveat — Glimmer’s support for Web Components:

Glimmer’s support for web component is fairly trivial. Whenever we load our Glimmer component, it searches for the <freshworks-switcher/> custom element in the DOM, treats it as a placeholder to initiate the Glimmer application and then removes the placeholder.

Also, the attribute passed to the web component gets assigned to the Glimmer app container. This is how we support our configuration as web component attributes.

This is enough for our use case since the configuration passed to the Freshworks Switcher does not change after the initial load. If your use case is to re-render the web component whenever its attribute change, Polymer or other similar frameworks is better suited.

Caveat — Glimmer lacks testing utilities:

As mentioned before, Glimmer is still in the making. The utilities for testing Glimmer components is not comprehensive.

Further Web Components:

  1. PolymerJS
  2. SkateJS
  3. Svelte

About The Authors

Arijit Bhattacharya is a UI Developer in Platforms team at Freshworks. Connect with him on Twitter.

Kavyapriya Sethu is a storyteller who writes about stuff that happen inside Freshworks. Say hello to her on LinkedIn.

--

--

Kavyapriya Sethu
The Freshworks Engineering Blog

I am full of untold stories. Now I just have to find the right words and make them sing.