How we created an ideal widget for external resources

Alexey Yanchuk
Travelpayouts
Published in
7 min readSep 9, 2019

An ideal widget looks cool. It’s easy to integrate on any website, and it doesn’t ruin the website performance or fail. We managed to make a widget of our own. In this article, you will learn about the challenges we’ve faced, how we solved them and why we decided to name our widget Mewtwo after a Pokemon from the famous anime show.

About widget

The most popular Travelpayouts widget is the search form for flight tickets and hotels. A webmaster can insert it anywhere on the website: in the middle of an article, in the sidebar, in the heading. The whole process takes up to one minute. All you need to do is copy the script and paste it where it counts.

Search form

Then, a user will indicate their departure and arrival cities and flight dates in the search form. By clicking on the “Search” button, a user will be redirected to the jetradar.com website through a deep link (that includes a unique ID of the webmaster and search parameters). Respectively, the webmaster receives a commission if the user purchases tickets or books a hotel.

In the beginning, we only made one search widget for JetRadar and Hotellook. Based on the experience with the Mewtwo widget, we’ve developed a versatile widget builder to help advertisers create search forms in a few clicks. Now, it takes just a couple of hours to create a new search widget, but we’ll talk about that in detail next time.

Kayak search widget in Travelpayouts

Why use a search widget

Even though links give the highest conversions, search forms still show a good level of conversion from simply interacting with the widget to opening the advertiser’s website among other affiliate tools. Search forms have one more advantage: they enrich the content and add interactivity. Thus, if a website visitor reads an article about Berlin, they’ll be able to choose the trip dates and check the price right away.

An ideal widget is:

  1. Light and fast

Users won’t spend much time on a website with a low loading speed. Webmasters speed up the performance of their websites with all of the forces, and widgets should not slow it down.

On average, websites with high loading speeds feature the 450KB total script weight on one page (approximately 25 js-files). You can’t just propose that a webmaster add one more script of 200KB.

2. Beautiful

We don’t know where on the page an affiliate will choose to place their widget. Thus, it should look nice and work properly even if it’s placed in a 150x150 pixel field at the very bottom of the page.

Affiliates constantly surprise us with their widget-placement ideas on their websites, but our widgets should be given some credit — they always look great.

3. Flawless

It goes without saying. Note that you won’t know where a widget will be placed in advance, and what code they have. You need to make widget codes in such a way that it won’t conflict with any page code a website may have.

How we minimized our widget weight

Our widget weighs 55 KB plus 7KB, if you add an advertiser’s logo to the form:

45 KB js + 10 KB CSS + 7 KB logo = 55 KB + 7 KB

We put logos in a separate file to optimize the bundle size. The logo can be switched off, and some affiliates use this option. We put the whole code for logos in a separate CSS and use it only if the settings are right.

To reduce the weight, we have given up frameworks. To be exact, we used the Node Package Manager (NPM) as a framework.

When working with dates, you can use date-fns / js-joda instead of moment.js, because they have a module system. But, we didn’t take additional libraries for Mewtwo to reduce the bundle size. We don’t have that much work with dates, so it was easy to use vanilla JS.

Every time you pick up a new package with NPM, consider how it might influence the final code. We use Monkberry.js for templates. It was developed by the Aviasales team and is now available publicly. It has a better speed in comparison to React template processors and only weighs 4KB. We reduced the code, but didn’t lose speed.

We always control the final size of the software distribution. To do so, we use Size Limit, Webpack bundle analyzer and Jsize to check the library size in NPM.

How to make a widget load on the page as fast as possible

We wanted to make our widget load faster than the main content and avoid slowing down the whole process.

At first, we tried out our initial idea. We made a separate loader of 1KB, which was being loaded on the page first and then we loaded the widget. It didn’t work because the widget script was being loaded at the end of the “waterfall,” after the other elements on the page.

What we did:

  1. We found the fastest loading solution — a solid js-file which we saved in the cache with config files.
  2. We added async so our widget wouldn’t interrupt the loading of the content on the page.

JS conflicts

We didn’t use React or other popular frameworks, so we didn’t encounter any conflicts of the libraries’ version or any code related to these libraries.

But, we faced a challenge when inserting widget code on pages with Mootools library. It redefines some native events, which was causing interruptions in our code performance. Version conflicts happen when the affiliate page uses an older version of the same library that the widget does. In such a case, the outcome is uncertain. Not only can the widget fail, but so can the whole website.

When it happened, we advised our affiliates to insert the widget into iframe. This would solve all the conflicts, even though it reduced the loading speed a little.

Problems with CSS

We faced three problems with styles, and we’ll discuss them in detail:

  1. A collision of class names
    It might happen that your class name coincides with the class name on the page where a webmaster has placed the widget.
  2. Overriding global settings
    If a webmaster added line-height in the body, for example, and you didn’t consider it, the row height in the widget will be bigger and it will eventually break down.
  3. Stronger selectors
    Let’s say a webmaster can write a setting for div with important.

A solution to all the problems includes isolating styles.

We considered two options that didn’t suit us for different reasons:

  • Iframe — Influences the loading speed and limits the area of interaction with the widget (for example, datepicker should fit within iframe size).
  • Shadow DOM — Only works in Chrome, Opera and Android.

CSS Modules and CSS-in-JS styled CSS can help solve the class name conflicts, and it is actually a good option, but we decided to find another one.

Our own way of isolating styles

  1. We isolated standard settings from the outer world.

We normalized the styles for all the tags we used inside the main wrapper of the widget. We resorted to CSS Normalize, complete with strings that we had to add to solve the conflict with different styles of affiliate websites.

2. We made our settings the strongest ones.

To receive the strongest selectors, we used PostCSS-importantly when assembling, which adds importance to all settings.

3. We got rid of collisions.

To receive unique selectors, we used gulp-class-prefix when assembling. We faced a problem of naming: which unique prefix should we use? Finally, we decided that we will be naming each widget after a Pokemon, so this one is called Mewtwo. And this name falls within a prefix.

This is what our final code looks like:

Problems with SVG

  1. SVG should have an option for color customization
    We defined SVG in templates and sent new colors with a JS update. It’s possible to customize through CSSX directly in JS.
  2. Logos
    We placed logos in a separate file of 7KB. When a webmaster doesn’t need to show a logo in the widget, it doesn’t load.

The correct code for insertion takes weight off of our customer support team

We didn’t want webmasters to ask our support team how to insert the widget every time, so the code needed to be very simple.

The most popular insertion type is script + div#id:

But, a webmaster would have to think of where to place div on the page and where to put the script itself.

For this reason, we are confined to the script and added a node for a widget inside. A webmaster will only have to place the script anywhere on the page.

We saved settings for the inside of the widget code with a hash. It is possible to redefine them with inline js. This step won’t let a webmaster correct the code manually and make a mistake.

This is what our final code looks like:

<script charset="utf-8" src="//www.travelpayouts.com/widgets/3a5305a01f98dbccc3cc693154dd68e0.js?v=1790" async></script>

Check-list: How to make an ideal widget

Follow our tips on to make a widget that can be easily inserted into any website:

  • Check the software distribution size
  • Isolate CSS
  • Avert the risk of a conflict with js-libraries versions
  • Develop widgets that are ready to perform right after they’ve been loaded
  • Remember about iframe and localization
  • Make your code as simple as possible for insertion

Resources

  1. Here is a great book on this topic: Third-party JavaScript
  2. https://date-fns.org/
  3. https://github.com/js-joda/js-joda
  4. https://monkberry.js.org/
  5. https://www.npmjs.com/package/size-limit
  6. https://www.npmjs.com/package/webpack-bundle-analyzer
  7. https://www.npmjs.com/package/jsize
  8. https://gist.github.com/MarySherstneva/c5b06c357d1ac362b8dbeb857550e401
  9. https://gist.github.com/MarySherstneva/2f10385cc7de2d9da3d00d619add6b4b
  10. https://gist.github.com/MarySherstneva/ce5d87c146e77a63290f1ee0e9ca963c

--

--

Alexey Yanchuk
Travelpayouts

Product Manager in Travelpayouts affiliate network