How I optimized the Pagespeed of a theme from 59 to 100

Storyblok editing capabilities

Why speed matters?

With more than 50 % of mobile users browsing the web Google is putting a lot of effort to make the experience of the mobile web better.

Pagespeed is a ranking factor and you will have happier users if your site is fast also on mobile phones with a slow internet connection.

In this tutorial I will explain how I optimized an existing one-pager from a over-bloated, slow website to a instant loading page.

Stack

For the tutorial I used a open source theme which uses bootstrap as CSS framework and a bunch of jQuery plugins.

I deployed the final version on Heroku mysterious-tor-92558.herokuapp.com and you can checkout the code before and after the optimizations at Github:

What needs to be done?

  1. Identify the CSS used to render the above the fold content and inline it in the head section
  2. Add gzip compression middleware
  3. Minify CSS and Javascript
  4. Minify HTML
  5. Optimize images with lossless compressing
  6. Lazyload images
  7. Optimize image sizes for every screen resolution

1. Identify the CSS used to render the above the fold content and inline it in the head section

This is probably the biggest task in the list. The above the fold content is the content that the user sees without any interaction on your site. It’s the first impression and therefore it needs to be visible to the user immediately.

An external stylesheet in the head section of the document is blocking the rendering and just will show a blank page until the file is loaded. Therefore Google recommends to inline the styles that are necessary to render the above the fold content.

Above the fold content

First I created two gulp tasks. The task styles:above writes all styles defined in the above.scss file to the public folder and then puts it in a handlebars template which I included in the head section {{> 'above_fold_css'}}.

gulp.task('styles:above', function () {
  return gulp.src('source/scss/above.scss')
    .pipe(sassGlob())
    .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
    .pipe(gulp.dest('./public/css/'))
    .pipe(browserSync.stream())
    .pipe(rename('above_fold_css.hbs'))
    .pipe(gulp.dest('./views/components/'))
})

gulp.task('styles:below', function () {
  return gulp.src('source/scss/below.scss')
    .pipe(sassGlob())
    .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
    .pipe(gulp.dest('./public/css/'))
    .pipe(browserSync.stream())
})

To identify the styles necessary I removed all css and inspected the html code of the teaser section. Step by step I added back the classes required for the teaser and compared the result with the version before. At the end I had only 268 lines of CSS for the above the fold content.

The below the fold CSS and the font I put after the closing html tag.

<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,300,600' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="/public/css/below.css">

2. Add gzip compression middleware

I added a compression middleware in index.js since gzip is not enabled by default in an Express app. For performance reasons I recommend to let gzip compression be handled by Nginx or a CDN like Cloudflare or Cloudfront.

// index.js
const compression = require('compression');
app.use(compression());

3. Minify CSS and Javascript

That one is easy once you have setup Gulp or Webpack.

// gulpfile.js
// Add compressed to the sass compiler
.pipe(sass({outputStyle: 'compressed'}))

// Add the uglify plugin to the Webpack build
plugins: [new webpack.optimize.UglifyJsPlugin()]

Don’t forget to set the right expire header for your public files in index.js.

const oneYear = 1 * 365 * 24 * 60 * 60 * 1000;
app.use('/public', express.static('public', { maxAge: oneYear }));

4. Minify HTML

Depending on the amount of HTML used on a page minifying it can give you a huge benefit to save some network roundtrips.

// index.js
const minifyHTML = require('express-minify-html');
app.use(minifyHTML({
  override: true,
  exception_url: false,
  htmlMinifier: {
    removeComments: false,
    collapseWhitespace: true,
    collapseBooleanAttributes: false,
    removeAttributeQuotes: false,
    removeEmptyAttributes: false,
    minifyJS: false
  }
}));

5. Optimize images with lossless compressing

As I use Storyblok as a CMS for all the images I can just use the free image service which automatically compresses on all the images. All I have to do is to write a Handlebars helper which gives me back the image from img2.storyblok.com (The original version is saved at a.storyblok.com).

// This is how the handlebars helper looks like:
// Usage: {{img source }} or {{img source size='600x0' }}, 
img: function(content, block) {
    var size = block.hash.size;
    if (!content) {
      return  ''
    }
    var img = content.replace('//a.storyblok.com', '');
    img = '//img2.storyblok.com' + (size ? '/' + size : '') + img;
    return img;
},

If you are not using Storyblok you can use a tool like thumbor or sharp for optimizing images.

6. Lazyload images

I’ll use lazysizes, a lightweight lazy loading script and add it to my scripts.js file.

npm install lazysizes --save-dev
// scripts.js
import lazysizes from 'lazysizes';

Replace the src attribute with data-src and add the class lazyload.

<img data-src="{{img image size='300x0' }}" class="lazyload" />

8. Optimize image sizes for every screen resolution

For responsive images I used a source set and defined different resized images for different screen resolutions. The awesome lazyload script automatically detects the right size and inserts it to the sizes attribute.

<img
  data-sizes="auto"
  data-src="{{img source size='300x0' }}"
  data-srcset="{{img source size='300x0' }} 300w,
  {{img source size='600x0' }} 600w,
  {{img source size='900x0' }} 900w" class="lazyload img-responsive" />

Conclusion

For small sites the effort to optimize to a green Google Pagespeed value is small compared to the benefit for your users. For bigger websites it’s more complicated to identify the above the fold content and it maybe also needs some redesigning. But Google will thank you with a better Pagerank and you will have a better conversion rate.

Hint: If you want to start with a new website which is already optimized you can try out getspeed100.com which is the first marketplace for highspeed themes.


More to read...

About the author

Alexander Feiglstorfer

Passionate developer and always in search of the most effective way to resolve a problem. Thinking in components helped him to be five times more productive. After working for big agencies and developing a scalable e-commerce platform he founded Storyblok.