Early this year, the retail company where I work (https://www.garbarino.com) decided to create a PWA.

It was a great moment for a web technologies fan like me, being part of it. Argentina has not reliable mobile connections, our clients might not want to install the native app and, fortunately, 80% of our mobile traffic is from chrome. So, let’s do it! 💪

The key objective

To improve the experience on mobile.

Project setup

This was the perfect time to face an expected redesign on our mobile user experience.

We made ourselves some questions like the following (and I expect to answer all of them before the end of the article):

  • Which metrics will we measure in order to know if we are performing better?
  • Fulling the home with product carousels is the best approach in order to sell more? I think no, but the first version has the same components than the mobile web and we couldn’t do anything different. A business decision. 🤷
  • Should we use our existing web applications and progressively build on top of them? Or should we create a new app? As we wanted a new design and a SPA approach, we opted for the second option. That implies more work but gives us more flexibility.
  • Which trendy framework will we use? Vuejs? React? Preact? We opted by react because more people in the company has previous experience on it. We love some Vue features but we are happy with react either way. 👍

Technical approach

As the project evolved, the team became bigger, we added more features, the stack grew up and we ended with a first version which has:

  • Basic offline support (custom screen instead of the downosaur)
  • Add to home screen support
  • Server side rendering
  • SPA approach
  • Static files caching
  • Route based code splitting
  • Same URLs than desktop site
  • And much more is coming…

The elected stack was:

  • Node.JS for the server
  • React for the frontend
  • And many useful libraries like material-ui

Project challenges

Doing SSR is more than saying: we use react and node, both are JS. What can fail? We named this requirement: the dark side of server side and it should be the title of a talk in the future. Meanwhile, you can read this excellent post if you want to have an idea: https://reactjsnews.com/isomorphic-react-in-real-life

If you tried SSR with react, surely you saw this message more than once:

Basically it’s saying that your markup on the server is different than the one on the client and, with that, you are heavily affecting the performance of the site. You would say, it will never happen if I’m using the same components on both sides. Well, check the previous link or imagine this simple situations:

  • We have daily offers, with a countdown of the time that lefts for each offer. If you render the countdown on the server, when it arrives to the client, the remaining time will be different (because of the network) so, the markup will be different. The solution, calculate the time after the component is mounted only on the client side.
  • We use a dependency that choices incremental ids for the component, the counter will be absolute different on the server and the client, so, also the id and the markup. The solution: fortunately that dependency accepts an instanceId prop and we can generate an unique id with the same algorithm on both sides.
  • We are using redux, all the API calls are async. But the server doesn’t know it. So, how to say to the server that it must wait until the response arrives in order to render the markup and only on that moment send it to the client? Dan Zajdband had the answer! Dispatch a "serverReady" action, update your state when you have the data and wait for it before rendering the app on the server. Something like this:
const unsubscribe = store.subscribe(() => {
if (store.getState().serverSide.serverReady) {
response.render('index', {data: yourData}

Other challenges

AB Testing

Well, it’s not so easy...

  • We want to separate Google analytics measurements on a new view but without losing the whole picture on mobile.
  • We want to measure conversion rate but cart and checkout are outside the PWA.
  • We have the same URLs for both sides of the experiment.
  • I’d be good if we don’t mess the URL with weird query parameters.
  • People who see A, should continue seeing A and people who see B, should continue seeing B.

What did we do?

For the last three points, we sent all the traffic to the PWA. There, a random draw decided if you continue there or if you are redirected to the mobile site.

We stored that choice in a cookie “forever” if you see the PWA and for a day if you see the old mobile site. (This cookie helped us to know which user came to the cart page from which version).

And, gradually we increased the A/B testing percentage starting from 10%, then 20%, 50% and finally 100% (removed the A/B testing).

Why did we decide that?


The following are average percentages during two weeks of A/B test.

Conversion Rate

Average ticket amount

Bounce Rate

Because it loads faster? Because it’s cutest? Because it’s easier to find what you are looking for? Who knows! But the number is great!

Returning visitor

Page Views per Session

Session Duration

In other words, during the same time, people sees 35% more pages. So, as the flow is the same, they are now more engaged with our site.


We have a lot more to do: test push notifications, add more sections of the site to the PWA, and a long backlog of improvements and new features.

It’s a great time for web development. More APIs and capabilities are coming and the results we can obtain are amazing.

For any question or message you can find me at: https://twitter.com/leopittelli

Software Engineer. Fan of web technologies. Google Developer Expert for Web and Cloud. FullStack JS Dev / Chapter Lead Web @olxtecharg. Prev: PWA TL @garbarino