Achieving 90+ Mobile Web Performance at Tokopedia

Tokopedia is one of the largest e-commerce platforms in Indonesia. Our users can virtually search for any products they might want to buy at Tokopedia. One thing we have noticed is that new users often found out about Tokopedia when they are searching through the web; sometimes, using their mobile devices. We have implemented various technologies to help users enter our mobile web with ease. One of our approach was to utilise PWA + AMP, you can read more about this at this article.

The previous approach worked well, so we thought about creating an AMP for our mobile web homepage that serves as a landing page, and as an entry point for new users to our PWA. We have used AMP for our Product Page and Hot List Page, but since our homepage is considerably more complex to build using limited JavaScript (AMP!), we held off on the task. Unfortunately, our home page was one of the pages that has the most traffics, we could not keep postponing the task. We needed to find a way, soon.

The Discovery

We stumbled across this interesting article written by Addy Osmani. Basically it’s about Netflix providing a simple landing page for non-logged-in users, shipped with vanilla JS. The landing page would then prefetch the required javascript needed by their PWA, so when user navigates further, the assets are ready in the browser cache. At first, we thought, “Well that’s cool, but there’s no way we could do that, there is too much logic on our mobile web homepage, even when not logged in! That would be very painful to develop in vanilla JS.” We dismissed the possibility of trying that approach, and left it at that.

After some time, a couple of our folks (Dendi Sunardi and Kelvin Mijaya) came back from Chrome Dev Summit 2018, all inspired and filled with knowledge to share. One of the things that we ended up pursuing and experimenting on further is SvelteJS. We found the idea very interesting, a framework that compiles to vanilla JS.

It’s basically as fast as vanilla JS, which makes sense because it is vanilla JS — just vanilla JS that you didn’t have to write. — Svelte blog

“Whoa, that sounds great!”

Svelte also supports server-side rendering, therefore eliminating our concerns regarding SEO. It is way less mature than React though. This also reflects in the community; it is not as massive as React, for sure. But for a landing page, we thought it would be great for the job. We then were faced with a task to decide whether to use AMP or Svelte for our landing page. Anton Saputro, our Product Owner, gave us the liberty to choose which technology to use for this purpose. After weighing the pros and cons, we ended up deciding to go with Svelte.

And thus begin the next phase…

The Experiment

We discussed and came up with a high-level design of how we would integrate this new landing page seamlessly with our already existing React app. We want users to visit this new landing page, for their first visit to Tokopedia. This new landing page should be blazing fast, while maintaining the look and feel of our existing app. It would also install service worker in the background, which will precache the assets needed for our React app, so when our user navigates further, they would seamlessly go into our React app.

With that done, we started exploring SvelteJS and tried to get a working homepage as a proof of concept. Creating and integrating this new service with our existing monorepo wasn’t that hard. We managed to have our minimum working new homepage ready in a little bit over 2 weeks. A lot of the time was actually spent on researching which technology to use, identifying parts where it’s possible to save on bundle size, meetings with stakeholders for product team, data tracking team (big thanks to Marthino Tri Yuda!), and SEO team and basically lots of trials and errors.

We ended up using Svelte to render our UI components, svelte/store (Svelte own Store implementation) for state management, and Emotion for our CSS-in-JS solution. The reason to use svelte/store was mainly to keep our bundle size small. Emotion was used because our engineers use react-emotion on a daily basis, so it helps to reduce the barrier to entry for further contributors to the service. We also dynamically load polyfills on the client side only if necessary. This helped us to reduce our bundle size further.

The Result

Side-by-side appearance of our main React app (left) and our new landing page (right)

You can see that in term of appearance, both look very similar. But, under the hood, we managed to get the amount of javascript shipped for initial load to whopping ~37kB gzip! This number only includes the javascript needed for the app above-the-fold initial load, not including tracking, analytic, and external scripts. In comparison, our main React app ships around ~320kB of javascript to load the homepage. If we are including polyfills, the numbers would be around~65kB and ~345kB, respectively. The small size also allows our mobile web to be interactive in 4 seconds.

Results from https://web.dev (after multiple tests, our average is around 93)

This is great because new users can get into Tokopedia in a very short time, while the service worker is precaching other assets in the background should the user choose to navigate further into the site.

Using Test My Site tool made by Google, we managed to achieve 0s load time on 4G connection! I am not sure why the rating doesn’t show up when we test our site though.

Results from Google’s testmysite

UPDATE: Apparently, this seems to be fixed already by Google, so it’s no longer showing 0 seconds.

Summary

It is safe to say that our experiment by adopting our AMP + PWA strategy, and then modifying it a little to use other tech stack has resulted in a huge improvement to our homepage load speed. We have never used SvelteJS before this experiment, but we are very impressed by how small the bundle size can be. During our development we could not find a reliable linting and formatting tools for Svelte components. We do hope this situation will be improved in the future.

Coming from React ecosystem, we were hoping to find a de-facto routing library for Svelte, but no such library exists. On Svelte’s own awesome list there are 3 routing library listed, one isn’t Svelte-specific, and the others aren’t particularly regularly maintained.

We also would like to see something like react-loadable along with SSR support for Svelte. Currently lazyloading chunk on client side can be achieved with dynamic imports. But, lazyloaded chunks could not be rendered on server side. Currently, `react-loadable` achieves SSR support with `Loadable.preloadAll()`.

Well, it seems like SvelteJS version 3 is in the works, and we would probably be looking into it when it is released and hope that it will bring more good stuffs to the table!