Chris Han

svimg

May 17 2020 code

I released my first NPM package today - svimg - and figured I'd write a little about why.

Svelte and Sapper

I've been doing a lot of playing around with the Svelte Javascript framework recently. I've been using other frameworks such as Vue for a few years now, and I find Svelte's approach to web application development really interesting. Svelte takes the approach of trying to handle as much of the processing during compile time of the application to generate as minimal Javascript as possible in order to dynamically update your application, whereas other Javascript frameworks like Vue and React do this work at runtime, and the entire framework runs in the browser. This aligns with a similar reasoning around why I transitioned my personal web sites to statically generated sites instead of using platforms like Wordpress - it's better to do as much work up front rather than hitting a database on every web page, since many web pages are viewed more often than they are changed. If I can minimize the amount of running Javascript, that means the site downloads faster and runs faster.

This site is currently built with Gridsome which uses Vue.js, and despite the fact that I've really enjoyed using Gridsome, I'm currently looking at rewriting it into a Svelte website using the Sapper framework for Svelte. Sapper is a great start for a framework, but it's a much more generic web application development framework - more similar to Nuxt.js - and isn't specifically geared towards static site generation the way Gridsome is. This, combined with the fact that the Svelte community is so much newer and smaller than the Vue community, means that there's a lot more that I have to do myself. While that does mean it's a bit slower going, it's actually great when you're trying to learn since you have to figure out a lot of details yourself. One thing that I've really taken for granted is how Gridsome has built-in image processing. Sapper doesn't have that, so I had to start looking for outside solutions, which is how I ended up creating svimg.

Why svimg?

When I started researching for a solution for this, I landed at svelte-image. At first, it looked like this would meet my needs. However, I ran into a number of issues using it.

First, I needed a couple of fixes to even get the project to work (only one of which has been merged as of this writing). Fixes are ok - I could always keep a fixed fork.

However, I started to run into more significant problems and design decisions that caused me to rethink whether I really wanted to maintain a fork with very significant changes from upstream long term.

  • svelte-image's width parameter doesn't do anything, due to the way the image is hosted within a div at 100% width. I use fixed width images in places - for example, my picture on the home page is fixed at 150px. You could address that by putting a div with explicit width around the image, but the other problem is that the image preprocessing will still spend time and disk space generating images of sizes 400, 800, and 1200, even though you'll never need them.
  • svelte-image sets the image sizes attribute to a fairly generous value of (max-width: 1000px) 100vw, 1000px. The documentation says the sizes parameter will be overridden by image preprocessing, but that never actually happens in the code. As a result, if your image is not actually 100vw (100% of the viewport), this causes the browser to download a much larger resolution image than is necessary for the actual image size that's displaying, which is wasted bandwidth. This is admittedly a really hard problem to solve purely at a component level - it's really difficult to programmatically set the sizes parameter without knowing how the component is sized in the surrounding webpage.
  • The biggest reason I decided to move to my own package is that in the future, I want to do this image processing in other places besides Svelte preprocessing, which is where both svelte-image and svimg do it today. In particular, Sapper's general strategy is to use a Svelte template for each page client side, and have each route preload a JSON file with the minimal data necessary as you navigate. This prevents users from having to redownload the entire page, instead just downloading the minimal JSON and navigating as a Single Page Application. In my case, the JSON files would be generated from Markdown, but this means I need a way to run image processing on Markdown - for example, as a remark plugin - outside of the svelte preprocessor. svelte-image's code just isn't structured like that right now - I would have had to completely refactor and significantly restructure everything to separate the image processing from the preprocessing, which would have been a pain to try to maintain with upstream.

Other packages I looked at didn't have image processing, relied on filenames which couldn't be cachebusted, or had other issues. So I decided to create my own, with the code structure and functionality that I wanted and fit my needs. Hopefully it'll be useful for someone else, too.