Architecture Ui

Implementing Instagram's progress-bar with Ember.js

Michael Klein

· 6 min read

Instagram's web application has a very fancy way of telling the user that something async is happening in the background. When switching between different pages of the app the web application will display a nice looking progress-bar on the top of the screen. Here's how to rebuild it with Ember.js in your own application:

Often when implementing ambitious web applications it’s important to let the user know that the app isn’t broken when we need to do something async in our application (e.g. fetching data from the api server). The typical way to handle interactions like this is to display a loading-spinner that tells the users that they have to wait a little bit until the data has arrived. In the recent past though some discussion has been popping up around the extensive use of loading spinners on the web and if they actually hurt perceived performance and several applications have decided to take different approaches to show loading progress to their users.

Instagram’s web application for example has a very fancy way of telling the user that something async is happening in the background. When switching between different pages of the app the web application will display a nice looking progress-bar on the top of the screen. Here’s a small gif illustrating how this actually looks like.

instagram loading indicator on mobile
Instagram shows a loading indicator on top of the screen when data is loaded in the background

Super fancy right?! Here’s how to rebuild it with Ember.js in your own applications:

The progress-bar-component

The progress-bar component is relatively straight forward to implement. It just displays an element on the page that we position at the top of the page and animate in a certain way (I am using tachyons.css for my css styles):

This looks pretty nice:

a loading indicator similar to instagram's loading indicator
Our own loading indicator in action

So now that we got the boring part out of the way now to the fun stuff. To understand the presented solution we need to take a quick detour and look at how the default loading-states in Ember.js actually work.

Loading states in Ember.js

Ember’s router comes with loading states build in. There’s a little bit more to it but basically what Ember’s router will do when you return a promise from a route’s model-hook is to transition into the route’s parent-route’s designated loading-route and wait for the model-promise to resolve and then transition into the route that we wanted to transition to in the first place. While waiting the designated loading-template is rendered into the parent-route’s . Here’s a quick illustration of what happens:

a visual explanation of Ember.js' built-in loading states
A schematic overview how outlets and loading states work together in Ember.js

This works in certain situations but for what we are trying to achieve this does not work unfortunately.

The obvious first try

To implement a global progress-bar on top of the screen we most likely would try to make use of the default loading-behaviour and do something like this:

If we tried to use this approach and added a loading.hbs-template we would display the loading template in application.hbs's outlet while we are waiting for the profile-route’s model-hook to resolve. So far so good but this could actually pose a problem because we would need position the progress-bar at a completely different position in the UI via CSS. This might be annoying depending on how your UI is set up but the actual real problem is that we will display only the loading.hbs in the application-template’s {{outlet}} and nothing more. This means that we will loose the entire UI of the route that we were on before while waiting.

As mentioned before this might be what you want in certain situations but for displaying a progress-bar at the top of the whole screen this won’t do. We can’t simply stop showing content to our users while they are waiting and add a loading-bar on top of the screen. Also this approach would get super annoying in the future if we decided to create additional nested routes as we needed to add loading-templates for all nested routes again and again.

Application-route’s loading-action to the rescue

So the default loading-template behaviour won’t help us. Fortunately there’s also another (default) behaviour that we can use for implementing a fancy global progress-bar in our application.

Ember.js-Routes fire a loading-event when they wait for the model-hooks to resolve. This event will bubble up the route-hierarchy until it reaches the application-route of the application where we can handle it via the loading-action. This is the perfect place for us to tell the application-template to display the global progress-bar:

And then in application.hbs:

This gives us exactly the behaviour that we want. The gif shows that the existing UI still stays in place while we wait for data to get back to the user and after the model hooks resolves we transition over to the new route.

our final loading behavior matches instagram 100%
Our finished loading indicator while transitioning between routes

The nice thing about this approach is that this won’t break the default behaviour for subroutes. So if certain subroutes need to handle loading-behaviour the usual way this still works but for those that don’t implement a custom loading-ui we will show this global loading behaviour.

Overall I think this is a very clean solution to showing a global progress-bar in Ember.js-applications and a very fancy UI to indicate a loading state to your users.

Thanks for reading and as always just get in touch if you have questions. We are available for consulting work and would be happy to help you and your company with your Ember.js-projects.

Schedule a call

We are here to enable your team to deliver ambitious applications. Let's discuss how we can help.

European flag

© 2020 effective ember All rights reserved.