Recently I got a message from google search console about a problem on photonics101.com. For a lot of pages. Cumulative layout shift (CLS) has been observed and I should fix it.

What is that?

Looking at a page by google I got the idea.

Some contents on the pages are loaded in a way that they push other elements "dynamically" to the bottom. This causes a layout shift. And since quite a few elements can do that, the effect is cumulative.

Ok, that's easy.

On the above mentioned site I tacke some educational material in electromagnetism. The site uses quite some math.

These formulas itself are rendered using MathJax.

I cannot express how I love MathJax. It's a piece of art to have TeX formatting on a website. I fell in love quite some time ago and my admiration continues.

However, have maybe a few thousand formulas on the site. And the formulas cause cumulative layout shift.

I need to find a solution such that Google likes my page again. In turn, this little contribution is about search engine optimization (SEO).

Not easy.

Visualization of cumulative layout shift

At first I was trying to understand the problem. I made some investigations and built a test case on this site.

The test is fairly simple: a website containing Maxwell's equations and an image below to show the shift.

Here's how it looks like to load the equations in real time (ok, maybe a bit slower):

MathJax cumulative layout shift visualization. Load of Maxwell's equations
MathJax test case to show cumulative layout shift: Load of Maxwell's equations on a website

Mr. Google, I get your point!

Solution to MathJax cumulative layout shift

I have tried a couple of solutions on the actual MathJax code. There are lazy load options and (my favorite) you can set a fixed height of div's around MathJax.

Half-Way Solution: fixed height div's

This initial solution goes something like this:

<div style="height: 120px;">
    \[ E = mc^2 \]
</div>

Nevertheless, this solution still causes some cumulative layout shift. Presumably because the first height of larger formulas renders quite unlike text.

But this is a quick and dirty solution and I can recommend it.

For purists, however, there is the ultimate solution.

The ultimate solution: vector graphics

The ultimate solution without any cumulative layout shift of course is the most involved one. No pain, no gain.

In this solution, we replace the actual TeX / MathJax code by an svg image that is pre-rendered using the formula.

The workflow is easy:

  1. Locate TeX math formulas on your website
  2. Convert the formulas from TeX to svg using, for example, Thomas Lochmatter's LaTeX to SVG tool
  3. Replace the formulas on your site with the svg images

Why vector graphics and not pixel ones? Simply zoom in on a png or jpg and you will see the difference. Wikipedia used to render (if I'm not mistaken) pixelized graphics and they looked horrible when zoomed in. Now they also use svg's for the above reasons.

Note: You don't want to cause cumulative layout shift by your newly established vector graphics! Therefore, set the height attribute of the images explicitly!

Here's an example of the aforementioned Maxwell equations, as svg vector graphics:

Maxwell's equations as vector graphic.
Maxwell's equations as vector graphics: a fixed height elimininates cumulative layout shift entirely.

And here's the associated code, that you can also look up in the html:

<figure>
    <img src="/imagesblog/22/maxwells-equations.svg" height="130">
    <figcaption>Maxwell's equations as vector graphics: a fixed height elimininates cumulative layout shift entirely.</figcaption>
</figure>

This method has also the advantage that you may use an "alt" attribute for the image. This attribute might give you a minimal advantage in terms of search engine optimization (SEO). However, most SEO advantage comes from no cumulative layout shift.

The test: 99% pagespeed on my math-heavy page

It was quite some time until I was able to update a number of pages on the mentioned site. The reason is simple: setting the height attribute or replacing formula's with svg graphics is quite the effort. Anyhow, I think I made some good progress. Here are the results of my MathJax adaptations!

First of all it is very important to load a lightweight installation. I use the tex to chtml configuration, loaded from the jsdelivr cdn. The implementation is simple:

<script type="text/javascript" id="MathJax-script" async
  src="https://cdn.jsdelivr.net/npm/[email protected]/es5/tex-chtml.js">
</script>

Note that I do not use lazy loading or whatever. Lazy loading gives you a faster initial page load. However, once you scroll down, non-optimized math expressions will cause cumulative layout shift. So lazy load was not an option.

The file of the tex to chtml module is only a little larger than 100kB. That is after automatic gzip compression by the cdn. This is quite small given the power of MathJax!

Note that the tex to svg module is about twice the size, which gives you a bit of punishment for mobile pagespeed.

For the site that we will see the test for I simply implemented the "half-way" method with fixed height. This is absolutely OK for my purposes.

Ok, let's see the results from https://pagespeed.web.dev/

MathJax site with very good pagespeed performance after tweaks
Optimized site for MathJax rendering: No cumulative layout shift, extremely fast response. Note: The site is running a popular CMS, i.e. no static site, which could even further improve the site's speed Note: The mobile speed score was 94 / 95.

This is not bad! To be fair I am a bit proud here. It took me about a month to come back to the basic configuration after trying out a myriad of different ideas.

Conclusions

Google has introduced a number of measures to analyze the Core Vitals of a website. One of these measures is the cumulative layout shift.

MathJax can cause considerable cumulative layout shifts, especially for longer formulas, i.e. equation arrays.

The only way around MathJax-caused cumulative layout shift is to either - hard-code the height of the respective formula or to - use an svg image with given height and width

It is imparative not to use MathJax' lazy load module.

Furthermore an appropriate configuration for example the tex to chtml one reduces loading times, especially on mobile devices. The file is best loaded from a fast cdn.