 Welcome to my talk on handling images on the web. I'm Marco Ottavito, founder of Simplabs. We're a digital product development consultancy working with international clients helping them build web apps for all kinds of purposes from banks to a train ticket sales portals and online learning platforms and beyond and Whatever we and everybody else does on the web images play an important role in that Ever since the first image was uploaded in 1992, which allegedly was this picture It shows the band Lizorribles Serenet, which was formed by four Seren employees Like with everything else relating to the early internet the first image Also comes from CERN, of course, which is where the web itself was invented back in the day You will recognize the band's initials are LHC Of course these happen to also be the initials of the large hydrant collider which is that huge machine that the CERN uses to research some Fundamental aspects of physics, which is likely a very interesting topic and also something I have absolutely no knowledge of And it's not what I'm going to talk about here So since that first image was put on the web Nothing really has changed fundamentally in the way we use images on the web We use an image tag Set it source attribute and this shows the reference image on the website So that seems easy enough, but with a Change in progress the web has seen since 1992 the possibilities in terms of what we can do with images on the web Have changed and evolved as well and so have user expectations To cater to those a bunch of extensions have been added to and around that simple image element So these days The highly optimized equivalent of rendering the same image on a website might look something like this 15 lines of html with a bunch of stuff in them that can seem pretty overwhelming perhaps And there's even an entirely new kind of image format that can go right in the DOM, which is svg SVGs can even be styled with CSS and be modified and run time so that the content of the images itself can change So images Have changed from something relatively simple into a complex topic that sometimes can be hard to navigate and make sense of And while we all spend quite a bit of time and effort optimizing the javascript that goes into our applications Images often do not receive that same level of intention Although they make up around 60 of the total number of bytes that a user needs to load for the average website or web app And decisions we make around images have a real impact on our users' experiences. So in this talk I want to look at how images even end up in our applications. What decisions we can make when adding images to our apps What the consequences of these decisions are and what best practices are to be aware of And I want to do that by looking at three typical scenarios for images And usually each image you will be adding to your app will fall into one of those The first scenario is images that are used as an essential part of the UI, which are mostly icons Like in this example, which is the main navigation menu from the linkedin app Then there are static images that Like in this example from linkedin landing page Other examples would be images used on blog posters or This kind of image would typically just make a site nicer visually Or support other data or content but not really be essential itself And finally there's images used as part of the data that deals with like in this example Which is opposed on the linkedin feed that also includes an image These kinds of images would usually be part of some data loaded from an API and therefore only be discovered at runtime So let's look at each one Of these in more detail starting with the first category images used as an essential part of the UI These are typically icons as I said are some kinds of status indicators And icons are used everywhere on websites and web apps alike They add additional meaning to otherwise purely textual your elements Or sometimes even the only way we assign meaning to elements like with a classic hamburger menu icon So what are the characteristics of such images? Most sites and apps would have lots of icons typically every functional element in an app Would have some sort of icon associated to it Icons must only be Must also be displayed immediately We would certainly not want to lazy load our icons so that the UI first renders without them And they would only pop in as the images get loaded from the server Without the icons the app would appear broken and some functionality might even be completely inaccessible Icons also adapt to application state in this case signaling that there's a connection request we haven't seen So what options do we have to add icons to our apps? We could add the icon image via CSS as a background image This is the easiest solution probably but doing this means the image will Be loaded only once the tag has been rendered and the browser passes the respective style rules So it pops in with a delay only Most of the time the delay might not even be noticeable But connections aren't always reliable and fast and sometimes loading the image might even fail completely So the UI remains incomplete indefinitely And we can also not style these images to reflect changes in the app state So we need additional classes with additional image to reflect additional states So how can we improve this? We could include the image right in the CSS as a base 64 encoded string So the image will show right away and not pop in after a delay Again for every additional state we need an additional class in the CSS Each with its own additional image But in this case each additional image for each additional state isn't only an additional background image style with a separate url But each of these images ends up in our CSS in its entirety, which of course grows the CSS substantially over time So the problem is, CSS is a render blocking resource Meaning that before the browser can render our Web app it needs to load and pass all of our app CSS So the larger that CSS becomes the longer the time it takes for your app to render will be So you'd really want to keep the CSS as small as possible, which is a goal that Adding a whole bunch of images as base 64 encoded strings even if each individual image is relatively small doesn't make easier to achieve So what other options do we have? We could use an icon font like font awesome With this approach you would load a custom font file and then use special elements in the html that use that font and instead of Normal characters show icons that are embedded in the font This approach suffers from a similar problem like we saw before with specifying image urls in CSS though Until the font is loaded Nothing can be rendered as the browser does not know the font natively And the icon only pops in once the font has been loaded Also, we cannot easily reflect changes in the application state via changes to the icon We need a separate icon for every single state And in this example, we'd like to have to roll our own icon font even so we can have icons for all of the application states we'd want to reflect in the ui Finally icon fonts are also bad for accessibility as screen readers will treat these icons as text since that's what they are Which of course can make it much harder for some people to use our sites and apps So are there only bad options then luckily the answer is no because we still have svg Since svg can appear right in the DOM and we can simply put the graphic right While we want the icon to show the icon displays immediately with the rest of the eye with no delay Also svg's vector format suits most icons well since the shapes we're typically using for them are relatively simple And cater well to a vector format. So that means we'd usually end up with relatively small svg's We can also style svg's with css And we can even make the svg itself dynamic and show our height elements depending on the application state So in this case, we can simply put the number of pending contact requests that we can only know at runtime right in the svg and thus the graphic itself However, as great as all this is, there's one downside to be aware of Any new icon we're adding right in the template grows the template and those are our javascript button And just like we don't necessarily want to grow our css that's render blocking the same applies to javascript In fact, in the case of javascript, it's even a bit worse because javascript is parser blocking even What that means is that when the browser receives an HTML document and starts parsing that as soon as it encounters a script tag It will block load the script execute it Before it can even continue parsing the html The reason for that being that the script could potentially modify the html Also our javascript of course contains the app we're building So everything that makes loading parsing and executing the javascript slower delays the startup of The startup of our app So we wouldn't want to lightheartedly grow it with a bunch of svgs In the worst case, we could end up in a situation like this This is a tree map visualization showing how the total number of bytes in the javascript bundle of an app Is distributed among the individual modules The app in total is roughly 3.2 megabytes of javascript pre Gdip But 83 percent of that are made up by svgs This can of course be completely reasonable if these svgs are somehow essential to your app But it's also kind of easy to end up with a bundle like this by accident when you're not paying close attention As you're adding svgs into your templates and just copy paste what the designer gives you for example So it's good to have a bit of tooling in place to prevent that One thing we can do is lindar templates using emma template lens We could add a rule that prevents svgs beyond a particular size from being added to templates Or one that enforces a maximum total size of all svgs embedded into all templates However strict rules like that don't always work very well What's more important is awareness of the consequences of what we're doing which Starts with surfacing data that would otherwise not be easily accessible A great tool for that is ember asset sizes action Which is a github action that the ember learning team uses to build the ember website What it does is it compares asset sizes of production build of the master branch with the asset sizes of production build of a feature branch and posts the diff as a comment On the respective poor request so with something like this It's easy to see when you're accidentally embedding a huge svg Into a template and adding a small button with an icon ends up growing your javascript bundled by 50k for example That is something you was most likely miss in review because nobody's going to look at An svg in a template line by line and check how big it is It's also kind of hard to even know how much a particular svg would grow your javascript bundle even if you did look at it line by line So I would strongly recommend everyone using something like this To summarize the topic of icons, let's look at all of the presented options We have for adding icons into our apps at once we can use image URLs and css We can use images as base 64 strings in css We can use icon fonts and we can put svg right in the dome I considered as base 64 in css or svg in the dome will display immediately with the rest of the url They do add some extra weight to our css and javascript Though we're using image URLs and css or icon fonts Do not really add any relevant weight The only approach that allows us to freely adapt icons to our application state is using svg Which we can modify entirely dynamically So typically using svg is going to be the go-to choice here But you want to make sure you have proper tooling in place to prevent Exidentally growing your javascript bundle disproportionately as explained before So leaving icons and indicators behind us now, let's look at the next kind of image We're typically dealing with which is static emitters That includes photos or Illustrations that make apps more appealing visually or support other content like text But aren't really necessary to enable functionality or so Most often those would be used on sites rather than apps, of course, where there's little functionality anyway For example landing pages like this one also from linkedin So let's look at this kind of image in a bit more detail First of all, there's usually relatively few of these certainly fewer than the number of icons You'd typically be using but they are big in terms of visual size and often Also in terms of number of bytes They are also typically not really essential for the site or app to make sense or be functional As discussed before even in the case of some sort of visualization in a blog post the image would mainly Support the text but not render the text useless while absent So that means you wouldn't want to Assign a lot of priority to displaying this kind of image as soon as possible And you would almost never be willing to pay a price for them in terms of growing your css or javascript As you would be for icons as we saw before Also, these images would typically not adapt to application state but remain static, of course So how do we add these kinds of images to our apps? We just use an image tag Of course, when doing that we would want to apply all of the optimization techniques we have at our disposal We want to lazily load the images only when they scroll into the viewport Or actually what the browser will do is it will load it slightly before that happens So ideally loading the image has already completed when it actually enters the viewport We want to load the smallest image possible for the current viewport to avoid Loading unnecessary bytes. We can use the image tag sourced And sizes attributes that allow us to specify different sources for different media queries for that And that way we load the smallest image possible with the least number of bytes for the respective viewport size of the user We'll also want to decode the image asynchronously so that that work can be moved off of the main thread And doesn't affect other things running there like our applications javascript We'd also want to show a placeholder for the image that is injected right in the DOM As a base64 encoded string and that will be shown by the image And that will be shown by the actual image loads Typically you'd be using a very low resolution blurred version of the real image here That would only have a very small file size and thus Be possible to be encoded in a relatively short base64 string so that it adds Minimal additional weight to the html or the template that Generates that html And when loading off the image completes the image will basically be put above the background and title Last we want to explicitly define the image's dimensions so that the browser reserves the space for the image element While the actual Image is still loading and doesn't relay out the entire page when the image finishes loading And doing all that we get a pretty good loading experience for the image with minimal impact on the performance characteristics Of the rest of the site or app In this case, you see we're using a jpeg for the image and I want to take a few minutes talking about image encodings here since What encoding you use for an image will also have a substantial effect on its size and thus its impact on loading performance Most of the time when talking about encodings, we're really only talking about two different ones There's png which is a lossless encoding that supports transparency And it's typically good for things like logos diagrams and similar Images with relatively little detail, but it results in huge files for photos as it's lossless And then there's jpeg which is lossy and does not support transparency But works well for images with lots of detail particularly photos There is another one though that's still relatively new Which is webp. It was originally designed by google and combines the best of png and jpeg It supports lossless as well as lossy encoding It supports transparency and therefore works well for low detail graphics like logos diagrams and so on as well as for photos However, there's one tiny drawback While webp is relatively well supported in modern browsers And the lack of support in i11 is likely not a huge problem for anyone anymore The problem now is safari only supports it on macOS Big Sur and the latest iOS Which probably for the next one or two years isn't going to be good enough for most of us There is a fallback mechanism however in the form of the picture tag What that tag does is it allows you to list a bunch of sources And the browser will choose the best source and use that instead of what's in the source attribute of the rep image tag In this example all browsers that support Webp will choose that source and all the ones that don't support it will just use the jpeg from the image tag source attribute as a fallback And you can also combine this with media queries on the sources And use it as a replacement essentially for the source set and sizes attributes on the image tag So with this fallback mechanism available you can really make webp your standard image format of choice As it results in significantly smaller file sizes This example shows a photo from our website where we needed a transparent background Which results in a pretty huge file size with png And only around one tenth of that with webp Of course, this is an extreme example and savings and less extreme scenarios won't typically be as big But a rough percentage to keep in mind is about a 30 reduction in file sizes across the board, which is still pretty substantial So to summarize what I said for static images You'll pretty much always want to use webp with the picture element to allow for specifying a fallback for older versions of safari You'll want to load differently sized images for differently sized viewpoints So you don't end up loading huge photos on mobile devices, for example, where an image half the size or less would be sufficient You'll also want a lazy load images since not using bandwidth for For loading images outside of the view Outside of the viewport means you have more bandwidth and resources available for those that are inside of the viewport You'll want to asynchronously decode images so that that work is done off of the main thread and doesn't compete for resources with higher priority tasks Like running your apps javascript You'll want to show a placeholder that can display immediately while the actual image is loading And you'll want to explicitly specify the images dimensions on the image tag in order to prevent a layout shift On the page once the image loads And doing all that you get a pretty decent user experience with minimal impact on the On the performance of the rest of your site or app So leaving studying images behind now, let's look at the third and last scenario, which is images that are used as data And by data I mean something that is dynamic and that we get from some sort of API usually This example shows the LinkedIn feed for the simple apps page All posts are pieces of data and the images that are shown there are attributes in their data So what are the characteristics of these kinds of images? Well, the main characteristic is really that these images are only discovered at runtime Since we get them from an API regarding most other aspects. They are similar to static images really you're dealing with similar image sizes You wouldn't usually want to change them in any way to reflect changes in the application state And most of the time these images aren't really critical resources where the UI would remain in some kind of invalid state Until the images have loaded So looking at the example of the LinkedIn feed again A typical way of Implementing a feed like this looks something like this We're loading post resources and in the response receive a number of posts with attributes like title text and pictures We'll then render the post in a component which could load something like this And what this results in is relatively bad loading experience for the user though Because the browser can only start loading the image after we render the Image tag with the source attribute pointing to the image And while the image is loading nothing can be shown and since we don't even know the image's dimensions Uh when rendering the image tag, we can also not set its width and height attributes So the browser can also not reserve space for the element until the image is eventually loaded There means displaying such a post will look something like this first we see the post First we see the post without the image and as soon as the image is loaded It pops in and triggers a relay out of all subsequent elements shifting them down visually Which is not particularly nice Of course, especially for chronological feed where items are ordered vertically And layout shifts make it very hard for the user to keep Track of where they even are This can relatively easily be improved however so what if the eye What if the api response didn't only include the images url, but also additional information What if we included everything that we need to apply the optimization techniques We saw earlier for static images differently sized images for different Viewport sizes, which are webp images a fallback jpeg a placeholder to show why the image is loading as well as the image's dimensions Having these pieces of information we can use them in the component that renders the post Using the same techniques we saw when talking about static images earlier the picture element with different Sources for a differently sized viewport A fallback jpeg source for all asafari versions setting the images dimensions explicitly As well as enabling async encoding off of the main thread lazy loading of images that are maybe further down the feed And thus outside of the viewport anyway, as well as a blurry placeholder to show why the image loads Doing that we get the same nice loading experience. We saw before for the static image, which is obviously a huge improvement One other thing we could be doing that goes even a step further As we could preload the image in the respective routes model method In this example We are first loading the post records and then preload all of the posts images before returning the model And that way we remain in emboss loading state Until the records as well as the images have loaded Of course, it's questionable whether we'd be willing to delay rendering anything until All images have loaded and in most cases we likely wouldn't but there are cases where the images are such a critical piece of data That we might be willing to do this Imagine what you're building is some kind of event registration app that uses qr codes for registration Confirmations it might well be reasonable to remain in the loading state for the For that route until the qr code has loaded instead of rendering the routes Template earlier, but then the qr code would only load with a bit of delay leaving the eye and sort of an essentially useless state Since the qr code is the one essential piece of data here So to summarize for images used as data loaded from an api We want to make sure to include additional information about the image beyond only its url At least that is the dimensions of the image so we can explicitly set width and height for the image So that the browser can reserve space for the element while the actual Image is still loading and thus prevent a layout shift once that finishes Ideally we'd also have separate url's for differently sized versions of the same image so we can load the smallest image possible for the user's device We could also include a blurry placeholder to make the experience even nicer Of course the simpler alternative to that is to just use a generic placeholder for all images that we could define in rlcss for example And if the images are really a critical piece of data for the app There's even the possibility of loading them in the ember routing layer. This is of course not something I generally advise to do but good to keep in mind as an option for special situations So looking at these kinds of Different images we're typically dealing with we saw there's a lot of aspects to consider And there's even a whole bunch of things. I haven't really touched on in this talk htp caching server worker Service worker caching and pre-loading CDNs and specialized image CDNs animations and moving images handling user uploaded images on the server encoding optimized versions Calculating blurry placeholders and much more So what started with a simple image tag that was Used to put a photo of les orribles cernet as the first image on the web Has developed into a pretty complex topic and a topic that would deserve More attention than it often gets and also a topic by doing things right or wrong can have significant impact on our user's experiences And thus being aware of the relevant aspects to consider and possibilities We have to choose from in different scenarios as well as the limitations and consequences can prevent us from running into Bad surprises when dealing with images And that's all I have. Thank you