What is an App Shell and Do You Need One?
Serving fast web pages is a combination of art, development best practices and sleight of hand. This means you don't always need to load and render the full page initially.
The app shell helps with making your website faster, adds rendering flexibility, improves maintainability and can be used for server-side, client-side and service worker rendering.
Of course, your primary goal with a webpage is to give the user what they want and speed if a huge factor determining if you reach that goal.
You can load the 'important' parts of the web page first, providing a good first impression, and then load the balance of the page. The balance being copy, images, styles, scripts, etc.
Of course you can lazy load page assets, but there is an additional technique you may want to employ, an 'App Shell'.
App shells are nothing new. They really originated with the rise of single page applications. You may have heard them called core sites, which is the term I typically used.
Today they are gaining additional attention with progressive web applications. Part of this is due to the misconception that PWAs are SPAs. This does not mean your progressive web app can't benefit or use the concept of an app shell, it can, but you need to determine if it is the right strategy for your site.
The challenge is to determine what should be loaded initially and what can be lazy loaded.
Which of the following describes an App Shell?
- It comprises of all page elements which are dependent of the specific page contents
- It discourages caching of content that overlaps across pages of the website
- It provides a fast first impression gracefully transitioning to a fully loaded page
- None of the above
The answer is it provides a fast, first impression gracefully transitioning to a fully loaded page.
The key there being 'gracefully transitioning' to the full page. You must be careful because some assets trigger rerendering and page refreshes. These are not graceful, they are jarring experiences.
The primary reason you want to utilize an app shell is to give the perception of a faster page load. I think the first thing you need to determine is does your web page need an app shell or not?
Does Your Website Need an App Shell?
App shells are about perceived speed. We know when a page takes too long to load visitors either leave or get anxious. Either way it is not good for you when your page is slow.
While many developers stop measuring page speed with time to first byte, the majority of time to render a page is after the TTFB.
The shell technique is way to reach a perceived rendered state as quickly as possible. Since the average web page takes 22 seconds or more to render today many sites might benefit from using an app shell.
I recommend measuring your page's actual speed and determine how long it takes to reach time to first interaction. This is when your page can be scrolled and interacted, which means the user is happy.
If the page takes longer than 5 seconds an app shell might be helpful. At the same time, use the reports provided by these tools to determine if there are other areas that should be addressed, like excess JavaScript.
If you are using a fast food framework like Angular and React you must use an app shell because these frameworks are so slow. So far, my experience with these frameworks is the way they are taught to developers is to use a overly empty application shell, which defeats the purpose of the shell rendering content fast.
The App Shell Skeleton
In its simplest form an app shell is the page or site's main header, footer and navigation. You can make it more encompassing, but that all depends on your site's layout.
The dynamic part of the app shell is what I call the page canvas, not to be confused with the CANVAS element. I call it that because it is simply a placeholder for the page's main content.
At some point in the page life cycle the primary content will be added to the rendered app shell to create the full-page experience.
But there is more to the core shell than just markup, There is also the hidden assets like CSS, scripts and images. For stylesheets and scripts I like to inline these to make them render really fast.
How An App Shell Helps Page Speed
Since the app shell should be very small, you can typically inline the required CSS and scripts along with the shell markup and be comfortably within the 14kb data packet size.
Why 14kb?
TCP works using a concept of 'slow start'. When you request a file using TCP/IP, like a web page, the content is returned in a series of data packets.
The first packet is 16kb, the second packet 32kb and this continues to increase up to roughly 4MB or so.
The first 2kb of the data packet is overhead data, which is not data or HTML in this example. So when you can contain your page's critical content within 14kb you only need a single packet and a single round trip. This means the network time is minimized as much as possible.
Gzipped HTML can compress tightly, which means you can even serve somewhat large files within this 14kb goal.
By inlining 'above the fold' CSS and scripts you are also eliminating extra file requests. Since more pages only use 2-5% of the CSS they load, think Bootstrap, you can optimize your shell's CSS using a tool like uncss.
I use this technique on every page in this site. It has really helped my page speed time and thus my SEO.
I won’t get into the details of applying the concept to JavaScript because that can get fairly complicated and spark a religious war LOL. But you can do that as well, it just takes planning.
Because a typical app shell can not only fit within the magic 14kb it provides the common user interface for the page or application the user see this render quickly and perceives the page is working and should relax and give you the extra seconds you need to complete the full page render.
What Does an App Shell Look Like?
Like I said you want to minimize the amount of markup and assets required to render an app shell. In its most basic form it would look something like this:
title
If you apply my inline technique I described in the previous section it would look like this:
title
The only difference is inlined styles and scripts.
I will note, I typically use the inlined script to dynamically or lazy load scripts and extra CSS. Sort of a controlled asynchronous loading mechanism.
This example shell is very barren. This is not where I typically leave the story. I also include basic header, footer and navigation markup.
title
What your shell includes is up to you. But I recommend carefully evaluating how different parts of your layout are used across multiple pages.
Here are some of my app shell best practices:
- Inline Primary CSS
- Include Primary Navigation
- Include common header and footer markup
- Use a pre-rendered layout with content placeholder
- Minimize JavaScript & CSS impact during page rendering cycle
App Shells and Progressive Web Apps
Many think progressive web applications must have an app shell. This is not true but can be beneficial.
I think the real key to determining if you need an app shell in your PWA is to decide how much dynamic application functionality does your app have?
Are you mostly static content, like my site, or are you dynamically updating the same area over and over in response to user activity. And is that dynamic content truly rendered from fresh data or is the content relatively static?
What I am getting at is don't think your PWA requires an app shell. Implementing a true app shell is not a trivial task.
You also need to realize progressive web applications are not single page applications. They can be, but do not have to be.
Single page application architecture benefits from the concept of an app shell and this is one of the reasons why they require so much client-side JavaScript.
But I tend to think PWAs and service workers can in essence replace the primary tasks SPAs require because the service worker can now be used to render web pages on demand, without accessing the network.
App Shells and Service Workers
A more advanced technique I use in some sites is to render a page in the service worker. I demonstrate this in my Progressive Web Application book and in the Philly Code Camp PWA.
The basic idea is to hold a cached copy of the page's app shell in the service worker cache. When the page is requested you can follow one of several workflows to compose or render the actual page within the service worker.
For the Code Camp application I used this technique to dynamically render the schedule and session details. This was a good choice because the event's schedule data was not very large, so it could also be cached when the application was first accessed.
self.addEventListener('fetch', event => { if (event.request.mode === 'navigate') { // Always respond to navigations with the cached app-shell.html, // regardless of the underlying event.request.url value. event.respondWith(caches.match('app-shell.html')); }});
You can think of this technique as moving the rendering pipeline from the traditional server engine like ASP.NET or PHP to the service worker.
Best practices with these type platforms is to maintain 'master pages' and 'includes' that contain common markup used by multiple pages. For developers this means there is a single source of code that needs to be maintained and you rely on the rendering engine to compose the final page markup.
The typical way pages are composed on the server is to retrieve data and then merge it into page templates, including merging page components together to create the final HTML returned to the browser.
That is essentially what the code camp app is doing, just inside the service worker. This means the request never needs to hit the network, making the PWA completely self-contained.
Is the App Shell the Best Solution?
As with all things technical, it depends.
I typically don't use app shells for content pages, like this blog. But for a real application, like a line of business application, it makes more sense.
I like to think of an app shell as a canvas to paint the interactive story that is the application experience. The shell provides the common accouterments required to make the application multi-dimensional and not limited to a single monolithic process.
Don't limit your shell scope to just navigation and logos, you need to think about possibly multiple shell combinations. Different parts of your application may have common tools or sub navigation features. What you need to think isolate is what is unique to a specific page and what is common across two or more pages.
Isolate the common functionality to layouts and components and use these to compose a shell. Maybe you have the top-level shell defined and render that first, then add layouts and common components in steps, as soon as they are available.
The only thing I recommended is testing your overall experience on low powered devices with network constraints. This will make sure your user experience is optimized and not janky. I recommend this for any web development, but when you are building the page in pieces this becomes more important.
Do not feel you must have an app shell. In fact, I would argue in the modern web, where a service worker is used, you can probably by pass this technique, at least for most websites. Instead you can use the service worker to compose the page and cache the response for future use.
If organic search traffic is important, then an app shell is not your friend. That's because Google wont bother executing the JavaScript it takes to render your page's content. You need the content to be instantly available, not just the app shell.
As always, the simplest solution is probably the best solution for most sites. App shells add complexity to websites, so you want to be careful before you use this technique.