Image Management in Ghost - A performance report

This post is 4 years old. (Or older!) Code samples may not work, screenshots may be missing and links could be broken. Although some of the content may be relevant please take it with a pinch of salt.

Since its first release, Ghost - an open source publishing platform - has come a long way. It has immediately gained popularity because it's easy of use and simplicity. Since then, lot of new features have been added to it, and the platform became a lot more robust: it's very easy to manage it via it's CLI, it's easy to create customisations using its amazing API, and there's a myriad of new features that are added in each release and that are being actively worked on.

Being one of the earlier adopters of Ghost I have immediately fallen in love with it and have used it to launch a few blogs of my own - including this very blog that you're reading at the moment.

There's one feature of this otherwise great platform missing in my honest opinion. Image management. Don't get me wrong, Ghost can handle images but it's rather basic, and it's not feasible if we want to keep a blog that performs well (i.e., loads fast). And if you don't take my word for it, you can see a lotofdiscussion on this topic on the Ghost Forum.

Image management in Ghost

So what does Ghost do at the moment regarding image management? Using the new Koenig editor (before it was only Markdown) we can add images as well as image galleries to blog posts (plus a great number of other things, please refer to the link before) and the upload of the image assets are also handled by Ghost. The images get uploaded to the server and served directly from the filesystem. The code behind the displaying implementation looks like this:

<figure class="kg-card kg-image-card">
<img src="/content/images/2018/10/IMG_1234.jpg" class="kg-image" />
<figcaption>Some caption</figcaption>
</figure>

The `figure` element is used in conjunction with the `img` element to display
the image, and by default, the upload folder is the root of the Ghost blog
followed by `/content/images/` and a year and month folder. As mentioned before
using such image management is easy, and very straightforward and at least using
this new editor some basic operations on images like uploading is handled. ###
Concerns regarding the current solution However, there are a few concerns
regarding this approach, and there's a lot more that could be done. There are a
lot of companies that have started to use Ghost as their publishing platform and
some heavily use media such as images - engineering blogs, including Mozilla's
VR Blog, Tinder and DuckGoGo all use Ghost. (You can take a look at some of
[Ghost's Customers](https://ghost.org/customers/).) Let's walk through an
example and see an interesting issue with Ghost's current image management
solution. The basic premise here is that we'd like to avoid having to edit an
image before uploading it to Ghost and using it in a blog post - some people may
not have the necessary skills (Photoshop) anyway to do exhaustive image
manipulation. # The test setup To run a few tests, I had setup Ghost locally and
added two articles with the same content. Each article has a cover image, two
large photos displayed in the manner discussed earlier plus an image gallery was
also added to the article. ## A typical use case Let's consider a travel blogger
or even a travel magazine wishing to use Ghost. Such a blog is by default going
to be very visual, sharing a lot of images were snapped during the travel and
therefore creating a visual experience that would, in turn, get more people
visiting the site. A very simple idea which should be created using Ghost very
easily.
![](https://res.cloudinary.com/tamas/image/upload/c_scale,f_auto,q_auto,w_600/v1539334649/fullstack-developer-academy/ghost-full-page-post.jpg)
Now, of course, this is only a sample blog article, and there's not much text,
but there are nine images displayed on the page. We have to assume that our
blogger in question does what the majority of people would do: take photos on a
mobile device and use the Ghost editor to add them to the article. This has
quite a few bad repercussions: - when displayed, the image will be scaled down
using CSS only - the picture is not going to consider the browser and always be
presented in a given format (e.g. JPG) Even before running any tests we can use
the Chrome DevTools to check that the images are scaled down either via CSS or
by the browser itself:
![](https://res.cloudinary.com/tamas/image/upload/c_scale,f_auto,q_auto,w_600/v1539334701/fullstack-developer-academy/browser-scaled-image.png)
What happens is that we load an image that's 2000 x 2667 pixels into a space
that can hold only 840 x 1120 pixels. This also means that we are downloading
the entire image which is about 615 KB in size. That again is not the best thing
to do. In fact, the size of the image served is the same size on the filesystem
after the upload:
![](https://res.cloudinary.com/tamas/image/upload/c_scale,f_auto,q_auto,w_600/v1539334750/fullstack-developer-academy/filesystem-file-size.png)
> Note that the Koenig editor in Ghost can display the image in its original
size or resize it as we saw it in the example. The raw image is 2.5 MB in size.
We are using a scaled version, but that's still not optimal. > Please also note
that if, on the same day the same image is uploaded Ghost will keep a copy of
both pictures taking up disk space in the same folder. (Ghost appends the
filename with -[x] where [x] is an integer). In fact, the same image could be
uploaded every day and be placed in the appropriate folder creating massive
redundancy. Let's run the site through a [Website Speed
Test](https://webspeedtest.cloudinary.com) tool that will take a look at all the
media on the site and offer some recommendations.
![](https://res.cloudinary.com/tamas/image/upload/c_scale,f_auto,q_auto,w_600/v1539334790/fullstack-developer-academy/webspeedtest-c-grade.png)
We can see from the above that we got a "Mediocre" C score, which is not great.
11 images got analysed on our page, and just these 11 images contribute to 6MB
of the site. That's quite a lot considering we "only" have 11 images. Let's take
a look at an optimised version of the site as well as discuss how the was
optimisation done. Each image has been uploaded in its raw format to Cloudinary,
and it's being served from there via URLs that also make the following changes:
* reduce the quality of the image without impacting the visuals to the human eye
* serve the right type of file per each browser (e.g. WebP for Chrome) * apply
the right width to the images These optimisations performed are available from
Cloudinary out of the box, and we only need to manipulate the image URL. So this
is what we had before: ```html
<img
src="/content/images/2018/10/PPFUMdBlS3ahDoquOdamkQ.jpg"
class="kg-image"
/>

And we merely turned this into:

<img
src="https://res.cloudinary.com/tamas-demo/image/upload/f_auto,q_auto,w_840,h_1120/ghost/PPFUMdBlS3ahDoquOdamkQ.jpg"
class="kg-image"
/>

Where:

  • f_auto delivers the right image format to the browser (WebP for Chrome, JPEG2000 for Edge etc.)
  • q_auto reduced the quality of the image without a visual impact
  • w_840 and h_1120 resize the image to be 840 x 1120 pixels

All these options above help us reduce the size of the image.

And the result?

The same number of images that now only contribute to 1MB of the weight of the blog.

Please note that the optimised page was created entirely manually.

Clearly there's a need to handle images online - especially in blogs which could display a lot of visual content. Having a third party solution integrated would help achieve this goal. Cloudinary has a plugin for WordPress to manage image galleries - such a plugin would make a great addition for Ghost as well.

DPR, Responsive Breakpoints

We could go fancier and change the DPR of the image as well as add responsive breakpoints. To do this, we can use the dpr_auto flag, and Cloudinary has a Responsive Breakpoint Generator to further aid us in adding such breakpoints.

To read more about DPR and Responsive Breakpoints, please read Responsive Images with 'srcset' and 'sizes'

Watermarks, because, we can

While creating this article, I knew I wanted to concentrate on performance comparison between the Ghost "native" image solution vs Cloudinary, but it occurred to me that a lot of bloggers like to place a copyright message or a logo as a watermark to images. This couldn't be easier by using Cloudinary:

<img
src="https://res.cloudinary.com/tamas-demo/image/upload/f_auto,q_auto,w_840,h_1120/w_80,g_south_east,x_5,y_5,l_logo/ghost/PPFUMdBlS3ahDoquOdamkQ.jpg"
/>

Where:

  • After /upload/ we find the previously mentioned options
  • After these options we get another set of content between // - think of this as a layer
  • l_logo is a layer referencing an asset called 'logo' (already uploaded in a Cloudinary account
  • w_80 is the width of the logo
  • g_south_east is the location of the layer
  • x_5, y_5 indicates the location of the logo: 5 pixels away from the south east corner of the main image

And the result looks like this:

Here's the image that we have seen previously and notice that now in the bottom right corner the Cloudinary logo appears (any logo can be used as an overlay watermark) - all this achieved again just by manipulating the URL.

Feature request

There's an open feature request for Ghost to have an improved Media Library added to the project - take a look at it, upvote it and add your thoughts.

Plugins

There are a bunch of plugins that integrate Ghost with Cloudinary already, and you can find them in the official Ghost Documentation.

Conclusion

Ghost, as a blogging engine has been my favourite for the past many years and it's packed with great features however as noted in this article there's a lack for image management and there's a lot more that could be done regarding image delivery and manipulation. Such addition(s) - should those be created by the Core team or as an open-source contribution - would not only be convenient but it could help immensely with site performance and using a tool such as Cloudinary would come with extra benefits on top of image management - such as adding watermarks to images.