Crayons: Coloring web experiences, the Freshworks way

If you have worked on building user interfaces for a large project, you would probably have learned the importance of component libraries. They contain a set of ready-to-use components that help you build UI fast and also maintain consistency across the entire project.

Building a consistent and seamless user experience is hard. This is where component libraries can help. As a SaaS platform offering capabilities for web developers to build apps for our customers, we wanted to come up with a way for our developers to build great user experiences as part of these apps—which are also consistent with the experiences Freshworks products offer.

On the Freshworks Developer Platform team, we have built an SDK, offered PaaS capabilities, and powered an app marketplace to combine forces with developers and solve complex business problems together for our customers. Developers can quickly build various apps on top of our highly scalable platform and help businesses that use Freshworks products to customize these products to their needs. Our marketplace proudly lists over a thousand apps today built by developers from around the world.

As a platform, we want developers to be able to build an app with any web technology they want. Putting these developers at the forefront, we aim to provide them with tools and support that can make their app development process a breeze.

Life before Crayons: building UI for apps, the old way

Developers in Freshworks build apps that render in various placeholders within the product. To deliver a seamless product experience, the apps must look and work just like the product itself.

Screenshot of a placeholder app within the product using Crayons
The image shows that apps are rendered in specific locations within the product.

To solve this problem, we as a platform provided developers a customized CSS file containing classes that adhered to the design system of the product. This was mostly a customized style sheet based on Bootstrap. There was also one separate stylesheet specific to each product to account for particular terminology.

There were certain problems with this approach, as listed below:

  1. The stylesheets mostly solved the problem of how the UI components looked rather than how they worked or interacted with the user.
  2. As changes continued to inevitably occur within the products themselves, it became extremely tedious to keep the stylesheets updated. Additionally, the lack of versioned stylesheets meant that any update that we made to these stylesheets broke apps that were built with an earlier version.
  3. To make a UI component work and feel native to the product that the app is a part of, developers inevitably spent more time writing loads of CSS rather than solving the business problem at hand. This led to a poor developer experience.

We realized that we needed to address these challenges and help developers concentrate more on solving problems for the business rather than doing plumbing work just so that an app can look like it belongs to the same product or product family.

Voila, web components!

While we were trying to solve the issues listed above, Freshworks was moving in the direction of a unified style guide and design system. This created the opportunity of making this design system available through reusable components which also solves the issues listed above.

In any popular front-end framework, a component is a unit that combines UI, functionality, and user experience. Most frameworks, such as React, Vue, and Angular follow this principle of building user interfaces. Our app developers however choose to build their apps using a variety of frameworks and even vanilla Javascript. We did not want to force our developers onto a single framework for our convenience but, at the same time, we also did not want to maintain multiple variants of a component library in so many frameworks.

Weeks of research and experimentation on how to solve this problem led us to the eventual answer: Web components

Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps. Custom components and widgets built on the Web Component standards, will work across modern browsers, and can be used with any JavaScript library or framework that works with HTML, according to

The web components specification allows developers to build their own custom HTML elements, encapsulate them, and distribute them. This means that you can build custom HTML elements similar to <video>,<audio>,<input>, and so on, and use them the same way you use native HTML.

Web components were a perfect fit for our use-case as they are framework agnostic. They could be used in any framework or even with vanilla JavaScript.

The path to StencilJS 

Although web components can be built using vanilla Javascript, the developer experience of building a component library with it wasn’t that great. This time we are referring to the experience of building the library as engineers building the platform. 🙂

There are a lot of great frameworks that will help you build web components. A few of them are listed below:

  1. LitElement and lit-html (successor to Polymer).
  2. StencilJS
  3. Svelte
  4. Angular Elements
  5. Vue Custom Elements

We gave Polymer and Svelte a try. Polymer was a very popular framework for creating web components. However, it was more suited to create individual web components rather than a library. We liked Svelte, but the lack of support and documentation for creating web components in Svelte3 back then made us look into other options. Finally, we settled on StencilJS. We were amazed by the developer experience it offered with an end-to-end solution for developing web components. Some of the features that helped us select Stencil over other available frameworks: 

  1. It is specifically designed to build web-component libraries. It does not try to be a full-fledged framework. This makes it really easy to develop web components.
  2. It has great browser support. It automatically loads polyfills in older browsers that do not support web components.

    Polyfills Crayons StencilJS
    This image displays browser support for web components built with StencilJS
  1. You can build components with Typescript and JSX.
  2. It has out-of-the-box features, such as end-to-end testing, a live development server, and a great automatic document generator.

Building Crayons

After we selected StencilJS as the framework for building our web component library, we started building the repository on Github. After a very extensive poll within our team, we named our component library Crayons! We wanted to make Crayons an open-source component library right from the beginning to enable Freshworks app developers all over the world and anyone in the open-source community to make contributions. 

Open source projects need a lot of maintenance and we wanted to automate most of the repetitive tasks so that developers can simply concentrate on building their components. We used multiple tools to achieve this. One of them is listed below:


Storybook is a great tool that provides an environment to develop a component in isolation. This means that the developers can fire up a development server, start building their component, and render it at the same time. Storybook also helps in documenting use cases of the component so that nothing is missed out. Automatic accessibility tests can also be added to it to help developers build accessible components.

Standard commits and automatic version bumps

We wanted to use ‘semantic versioning’ for Crayons. With semantic versioning, a version is specified with 3 numbers separated by a dot[.].
The numbers are in the format — <MAJOR_VERSION>.<MINOR_VERSION>.<PATCH_VERSION>.

With a given version number MAJOR.MINOR.PATCH, increment the:
MAJOR version when you make incompatible API changes,
MINOR version when you add functionality in a backwards compatible manner, and
PATCH version when you make backwards compatible bug fixes.

Adhering to this format can become difficult to do when done manually. This is why there are tools to automate this.

We use Semantic-Release and Commitzen to automate the entire process. Commitzen helps in generating standard commit messages by using git-hooks whereas Semantic Release analyzes these standard commit messages to automatically bump the version as necessary. You can read more on how to set this up in the Automate package releases with semantic-release and commitizen blog: 

Automated documentation

We use VuePress to automatically generate documentation from the files generated by StencilJS. We can also add custom content to it, as required.

CI/CD pipeline

Automatic software delivery is an absolute necessity these days. We wanted to leverage the power of a CI/CD pipeline to automate the process of building the component library and pushing it to NPM. As we wanted the project to be as transparent as possible, we went forward with Github Actions to build a CI/CD pipeline that will trigger on every commit. We configured two actions as follows:

  1. Whenever a new PR is raised, an action is triggered that runs a linter on the code and then runs all the tests. The results of these are posted to the PR itself. Only if a PR has no errors in these steps, it is allowed to merge with the default branch.
  2. Whenever the code is committed to the default branch, it runs the linter, the test cases, and the build. After the build, it runs semantic release to bump a version and push to NPM if necessary.

With Github actions in place, along with other tools described above, maintenance of the repository became a breeze. A developer can easily contribute to the library and after a reviewer reviews the code, they simply merge the code to the default branch. The rest is taken care of by the automation in place.

Crayons — now and the future

We released Crayons in March 2020. Since then we have seen a lot of interest from our developers in using this component library to build apps. A lot of our partners have built amazing apps using Crayons. We have also received a few open-source contributions.

With Crayons, you can quickly build a UI that adheres to the Freshworks Design System.

A sample form built with Crayons
A sample form built with Crayons

The UI shown in the diagram above can be built simply by using the following markup:

Developers no longer need to import a stylesheet and write custom CSS to make apps look seamless. All you need to do is add the following lines to your app: 

<script type=”module” src=”<version>/dist/crayons/crayons.esm.js”>


<script nomodule src=”<version>/dist/crayons/crayons.js”>


One added advantage with Crayons is that we can push updates to components and this will reflect automatically to all apps. We also follow a semantic version release, which will help in handling breaking changes.

Currently, Crayons comprises around 20 components based on the Freshworks Design System. Here’s the entire list here:

If you would like to dive deeper — for example, build an app or even better, contribute! — here are some handy links to explore.


Sample apps:

Github repository:

Contribution guidelines:


We actively look forward to contributions from the community!

What the future holds for Crayons

We want Crayons to grow into a comprehensive and all-powerful component library. You can expect some really cool features in the future:

  1. More components: We plan to come up with more components and make this a more mature component library.
  2. Super’ components: Expect new components that do much more than basic UI components. These will be fully functional components that can perform API calls, fetch data, and a lot more. The possibilities are endless.
  3. Crayons as a standalone design system: We want to make Crayons powerful enough for it to be used not just in Freshworks apps but everywhere. 

We have just started and are super excited about the future! We hope you are too!