We dedicated the second part of this series to backend and server optimizations. In this article, we’ll take a look at other optimizations that apply to the frontend.



Images, along with scripts, usually pose the biggest problems with website speed.



We can achieve image responsiveness by using the srcset attribute.

srcset="img-300.jpg 300w, 
img-800.jpg 800w, 
img-1300.jpg 1300w" 
sizes="(max-width: 600px) 300px,
 (max-width: 1200px) 800px,

Srcset allows us to define one source image in multiple sizes. The sizes attribute defines how much space a given image takes up at different screen sizes.



The WebP format allows us to reduce the size of images without compromising their quality. Support for WebP images is high in browsers, but for IE and older Safari it’s necessary to use the picture element, where we define the webp image source as well as the jpg image source for unsupported browsers.

  <source srcset="img.webp" type="image/webp"> 
  <source srcset="img.jpg" type="image/jpeg"> 
  <img src="img.jpg" alt="alt"> 


For images that we use directly in css using background-image, we can use Modernizr https://modernizr.com/download?setclasses&q=webp, which detects what features are available in a browser. If a browser doesn’t support webp, the no-webp class is added to the html element and subsequently it’s possible to use the image in the basic format in these cases.


Lazy Loading

<img loading = ”lazy”

The loading attribute with a lazy value allows us to delay the loading of images until the images need to be loaded (the user scrolls the page and is getting closer to the image). If we apply this attribute to images that we don’t need to load as soon as possible (such as a banner image that appears at the top of the page), this technique can speed up page loading and reduce the overall amount of data transferred. The attribute is available in most browsers except Safari.


CSS Styles

CSS styles are a fairly large part of websites. What usually helps us with CSS optimization are various tools or frameworks that prepare optimized CSS files for us when creating a production build.


Removing Unnecessary CSS Code / Code Splitting

To reduce the total amount of CSS code, it’s a good idea to remove generally unused CSS. In addition to the generally unused CSS, we’ll probably also use CSS specific to one subpage. In this case, it’s advisable not to load this CSS on the home page and to load it only after going to the subpage that will use the given code. Of course, it also depends on the size of the particular code, if it’s a small amount, then there is no need to split the CSS.



By minification we can reduce file size by removing redundant characters, unifying common classes without changing the functionality of that CSS. It’s possible to minimize CSS automatically, for example with Webpack, using mini-css-extract-plugin or css-minimizer-webpack-plugin.



The biggest problem and the thing that slows down today’s pages is JavaScript. Therefore, this section can bring us the biggest performance improvements.



We can use two attributes for <script> tags in HTML:

  • async – if the browser recognizes the async attribute when parsing HTML, it downloads the content of the script in parallel and at the same time continues to parse the page, after the download is completed, it stops parsing and executes the downloaded JavaScript
  • defer – the download works similarly to async, but before executing JavaScript, the parsing of the whole HTML is completed first


It’s good to use async for third-party scripts. It’s good to test the defer attribute to determine whether scripts work correctly with this attribute. With our website’s scripts, there may be a ranking issue with async loading, as the scripts may be of different sizes, so the script that was originally supposed to be executed later will be executed before another script. That’s why it’s better to use the defer attribute for larger applications.


Bundle Analysis

The first step in optimizing JavaScript is bundle analysis. We can perform the analysis using the webpack-bundle-analyzer utility, which will create a comprehensible list of used libraries and the sizes of these libraries.

Ukážka interaktívneho stromu vytvoreného pomocou webpack-bundle-analyzer
Example of an interactive tree created using a webpack-bundle-analyzer


Duplicates in the Resulting Bundle

In case we find duplicate libraries in a bundle, we can solve these duplicates using yarn resolutions:

Využitie yarn resolutions
Utilization of yarn resolutions


Lazy Loading

Similarly to the pictures, lazy loading can help us delay loading. An example of such loading might be loading an external library only after clicking on the button when the library is first used:

Načítanie externej knižnice až po kliknutí na tlačidlo
Loading an external library only after clicking on the button


Code Splitting

It often happens that the JavaScript used on the homepage is different from the category code. Therefore, splitting the code into smaller bundles by subpages or by visible content and content below the visible content can be a great optimization. E.g. for react-router, we can use lazy for such a division in React:


Code-splitting v React využitím lazy
Code-splitting in React using lazy



Finally, I offer 5 points that can be used to achieve the best possible optimization in the shortest possible time:

  • Minify CSS and JS + gzip
  • Images – use loading = “lazy” and WebP format
  • Use async for external scripts and, if possible, defer as well
  • Code splitting – split CSS and JS based on individual subpages
  • Upgrade of used external libraries, removal of libraries


Resources and further reading