DEV Community

Cover image for Behind the Scenes: Solutioning for React ChatBotify's Themes
tjtanjin
tjtanjin

Posted on • Originally published at tjtanjin.Medium

Behind the Scenes: Solutioning for React ChatBotify's Themes

Introduction

Switching Themes

If you've ever developed or interacted with chatbots on a website, you'll know that their aesthetics play a crucial role in enhancing the overall visual appeal of the site. However, theming isn't just about making things pretty; it's also about crafting a seamless and engaging user experience. In the past, customising chatbots with React ChatBotify required a fair bit of effort. Users had to style everything from the base template, which could be very time-consuming, especially when pursuing a drastically different design.

With this in mind, the idea for a theming feature emerged to streamline the customisation process. The goal - to provide users with a variety of ready-to-use themes that could be easily applied and refined to match specific requirements. This article takes you behind the scenes on the solutioning journey, where we explore 3 potential approaches to implementing themes in React ChatBotify. We will walk through the factors for consideration, analyse the pros and cons of each approach, before committing to a final solution.

The Problem

Prior to the introduction of themes in React ChatBotify, customising chatbots could involve a highly manual and tedious process. Users started off with a base template, which essentially consisted of the default settings and styles that came with the chatbot.

To achieve a unique look or to complement their websites, developers may have to comb through the documentation to meticulously override default styles. For example, let's consider the stark contrast between the base appearance and a more sophisticated "Terminal" theme:

Base Appearance vs Terminal Theme

To transition from the base appearance to the "Terminal" theme shown above, it involved numerous changes across 3 files (settings.json, styles.json & styles.css). Let us just take a quick look at the contents of the styles.json file:

{
  "headerStyle": {
    "backgroundImage": "linear-gradient(to right, #2c2c2a, #2c2c2a)",
    "alignItems": "center",
    "fontSize": "14px",
    "border": 0
  },
  "botBubbleStyle": {
    "textAlign": "left",
    "border": 0,
    "backgroundColor": "#070707",
    "color": "rgba(255, 255, 255, 0.87)",
    "padding": "10px 15px",
    "maxWidth": "none",
    "margin": 0,
    "fontSize": "14px"
  },
  "userBubbleStyle": {
    "textAlign": "left",
    "border": 0,
    "backgroundColor": "#070707",
    "color": "rgba(255, 255, 255, 0.87)",
    "padding": "10px 15px",
    "maxWidth": "none",
    "margin": 0,
    "fontSize": "14px"
  },
  "botOptionStyle": {
    "color": "rgba(255, 255, 255, 0.87)",
    "backgroundColor": "#070707",
    "padding": 0,
    "border": 0,
    "fontSize": "14px"
  },
  "botOptionHoveredStyle": {
    "color": "rgba(255, 255, 255, 0.87)",
    "textDecoration": "underline",
    "backgroundColor": "#070707",
    "padding": 0,
    "border": 0,
    "transition": "none",
    "fontSize": "14px"
  },
  "chatInputContainerStyle": {
    "borderTop": 0,
    "backgroundColor": "transparent"
  },
  "chatInputAreaStyle": {
    "minHeight": 0,
    "border": 0,
    "padding": "8px 15px",
    "backgroundColor": "#070707",
    "color": "rgba(255, 255, 255, 0.87)",
    "fontSize": "14px"
  },
  "sendButtonStyle": {
    "display": "none"
  },
  "chatWindowStyle": {
    "height": "490px",
    "paddingBottom": "10px",
    "backgroundColor": "#070707",
    "border": "1px solid grey"
  },
  "bodyStyle": {
    "paddingBottom": 0,
    "backgroundColor": "#070707"
  },
  "tooltipStyle": {
    "padding": "8px 12px",
    "borderRadius": "15px",
    "color": "rgba(255, 255, 255, 0.87)"
  },
  "chatHistoryLineBreakStyle": {
    "color": "rgba(255, 255, 255, 0.87)"
  },
  "chatHistoryButtonStyle": {
    "color": "rgba(255, 255, 255, 0.87)",
    "backgroundColor": "#070707",
    "border": 0
  },
  "chatHistoryButtonHoveredStyle": {
    "color": "rgba(255, 255, 255, 0.87)",
    "backgroundColor": "#070707",
    "border": 0,
    "textDecoration": "underline"
  }
}
Enter fullscreen mode Exit fullscreen mode

Notice that despite the great degree of flexibility for customisation, the amount of work can get rather significant if you are seeking out a very different design.

With that said, it was obvious that there was a huge room for improvement in the user experience. More specifically, if users could make selections from pre-configured settings and styles, it could potentially make their lives easier by reducing the amount of refinement work required. Thus, the concept of themes was born, which naturally led to the next question - how best to bring themes to users?

The Naive Solution: Pre-package Themes with the Core Library

A naive solution came to mind almost immediately. It is straightforward, and trivial to implement. Simply put, we could pre-make a couple of themes and include their settings and styles directly in the core library. When users specify the themes they desire, we simply fetch and load the relevant settings and styles. Pretty simple, isn't it?

Not only is this solution guaranteed to work, all the changes required to support this feature can be done within the core library itself. It's a tempting solution, but if we pause for a moment and ponder a bit deeper, we quickly realize that the ease of this approach comes with significant tradeoffs.

Firstly, while delivering the initial feature is easy, its maintenance becomes a nightmarish experience for users. This is because every update to the themes - whether it's adding new ones or fixing bugs in existing themes - would necessitate a version bump for the core library. This would be confusing and extremely annoying for users who don't use themes, as they would have to check and update the library for changes irrelevant to their setup.

On top of that, the core library is bound to bloat up overtime as more themes are added. This could potentially lead to degradation in the library's performance, which is most certainly undesirable when trying to craft a seamless chatbot experience.

Advantages

  • Simple to implement (all changes kept within the core library)
  • Easy integration for users (themes can be simply applied via the themes prop)

Disadvantages

  • Poor user experience for maintaining the library in the long term (users may be confused and annoyed by irrelevant version bumps)
  • Library bloat (unsustainable as more themes are added overtime)

Comparing the advantages and disadvantages, above, it becomes clear that adopting the naive solution trades away scalability and ease of maintenance for a very short-term convenience. With that said, the idea of combining themes into the core library quickly lost its appeal. So, is there a better approach?

A Refined Solution: Create a Themes Extension Package

Drawing on the knowledge from exploring the naive solution, it is apparent that mixing themes with the core library is a terrible idea. So let's address that! In this second solution, we refine the initial idea by addressing its major disadvantages. To do so, we tackle the root of the problem - by keeping themes and the core library separate.

Thus, the idea of a themes extension package came to mind. With this improved approach, themes will be handled independently via a separate NPM package. This reduces the confusion and annoyance caused to users in the naive approach, and prevents the core library from bloating, which helps mitigate the issues faced previously. However, this refined solution is not without its drawbacks.

Firstly, there is now increased complexity in implementing this feature since changes are no longer localised to the core library. Rather, there are now 2 packages to work with, which also increases the effort required to maintain the project. Furthermore, users who wish to use the themes feature will also need to make an additional installation for the extension package and end up having more dependencies.

Advantages

  • Improved user experience (no confusion or unnecessary updates for those not using themes)
  • Mitigated library bloat (themes are separated from the core library)

Disadvantages

  • Additional implementation effort (changes across multiple packages to implement themes)
  • Additional maintenance effort (2 packages instead of 1)
  • Additional setup step required from users (installation of extension package required)

The second approach seems slightly better, as the advantages are now oriented to benefit the users while the disadvantages are largely absorbed by… me 🥹. Hmmm, maybe this isn't a terrific idea after all. Can we try something even better?

The Ideal Solution: Serve Themes with JsDeliver & GitHub

JSDelivr & GitHub

In the previous two approaches, we were weighing who should shoulder the seemingly inherent disadvantages that came with implementing themes - whether it was the burden on users or the additional maintenance effort for developers of the library. However, neither of the solutions felt satisfactory. Could we find a way to eliminate or minimize drawbacks for both users and developers?
As a platform engineer, I found inspiration in GitOps -a methodology that uses a Git repository as the single source of truth, with version control providing a clear history of changes. Themes, in the context of React ChatBotify, could be seen as a set-meal containing specific styles and settings (which were configurations stored in json and css files). This realisation led to the idea of hosting themes directly on GitHub, which not only decoupled themes from the core library, but also eliminated the need for maintaining an additional package.

Hence, the third solution explored was to host all theme files, such as settings.json, styles.json and styles.css on GitHub. To avoid rate-limiting issues, the core library would be modified to fetch these themes dynamically using a content delivery network (CDN) such as JSDelivr. This approach offered several very compelling advantages.

Firstly, it eliminated the need for maintaining additional NPM packages and avoided version bumps for the core library when updating themes. Secondly, it preserves the ease of use for users as themes would work out of the box for them. Finally as an added bonus, this solution fosters a more collaborative community environment, as the GitHub repository could be open sourced to welcome contributions from other developers!

Advantages

  • Improved user experience (no confusion or unnecessary updates for those not using themes)
  • Mitigated library bloat (themes are separated from the core library)
  • Easy integration for users (themes can be simply applied via the themes prop)
  • Encourages open source contributions from the community

Disadvantages

  • Additional effort required to curate and maintain a GitHub themes repository

Reviewing the above pros and cons, it's clear that this third solution provides a seamless experience for users and allows both themes and the core project to be maintained independently - all while fostering community involvement through the open-source themes repository.

The Winner

After weighing the three options, the third solution emerged as the clear winner due to its strong appeal and comprehensive advantages.
To further enhance the user experience with themes, a separate React ChatBotify Gallery Project was also launched. The Gallery essentially serves as a centralised platform for users to explore and select themes. More details about this was shared in a separate article detailing the release of React ChatBotify v2.

Conclusion

The journey to implementing a theming feature in React ChatBotify was both challenging and rewarding. By leveraging GitOps-inspired principles, the final adopted solution not only simplifies the user experience, it also encourages contributions from the broader developer community. The next step is to encourage creative individuals to craft and contribute themes to the community, but that's a challenge for another time.

With this, we are one step closer to making React ChatBotify a more versatile and customisable library for everyone. I hope you've found this solutioning journey interesting. As usual, if you have any feedback, suggestions, or thoughts about what's shared, feel free to leave a comment or reach out on discord. Thank you for reading! 😊

Top comments (0)