Henrik Sommerfeld

Migrating from Gatsby to Sveltekit

Small GatsbyJS logo to the left and a bigger Svelte logo to the right

During my first parental leave I built a static web site with Gatsby for the family’s recipe collection. It has worked great and cost us nothing to host, but it has also been all the bad things critics say about the npm ecosystem. node_modules has been around 1 GB and it doesn’t build on anything newer than Node.js 16. Upgrading one dependency requires upgrading a bunch of others and some of them are not maintained anymore. In hindsight, Gatsby was the wrong horse to bet on.

Choosing Sveltekit

For my second parental leave I aimed at rebuilding basically the same site, but with another framework. I wanted to try out Svelte, so the decision landed on Sveltekit.

I don’t know if Svelte instead of React really made a big difference. The important difference is Sveltekit instead of Gatsby (or if I had landed on Astro, Hugo or some other alternative). I’ll touch on the Sveltekit characteristics I found relevant for my use-case. To understand my use-case, here are some main characteristics of the site:

  • Fully static
  • Recipes stored as Markdown files in the repository
  • Images stored in the repository
  • Simple CMS with authentication. Netlify CMS got replaced with Sveltia CMS
  • Generating multiple versions of images
  • In-browser search and tags to find recipes, in addition to listing by category

Dev Server vs Static Build

How can this possibly work during compile time?

A popular idea among Javascript frameworks seems to be that where and when code is executed should be “abstracted away”. That is, server vs client and compile-time vs run-time. In Sveltekit this has the consequence of assuming an active server when using npm run dev, even though I’m using the @sveltejs/adapter-static and setting export const prerender = true in layout.ts. This led to a few occasions where I was happily hacking on and found myself thinking “how can this possibly work during compile time?” The answer was that it didn’t work, an npm run build showed the error that what I was doing wasn’t available for static builds.

Images

Images are an important part of the site, used in every listing and presentation of a recipe. Since an image can be uploaded unprocessed from a phone through the CMS (that doesn’t itself have any image scaling functionality), I need to generate multiple versions of each image at build-time. Each Markdown file (recipe) references at least one image, e.g. /static/uploads/egg-royale.jpeg

I thought having to write a full GraphQL query to reference an image was cumbersome in Gatsby, but generating images in Sveltekit is somewhat of a mess. My first mistake was to get confused with image components. First the @sveltejs/enhanced-img described in the Sveltekit docs has 21 open and 18 closed issues at the time of writing this. I also tried another component, but that didn’t have any caching, so multiple builds in a row without any change would result in all images being processed again and again, for every build.

I then realised that the components are just using vite-imagetools and rendering some HTML. The latter part I can do myself, so I ended up using vite-imagetools directly. I find it a bit odd that my images are now modules and that I have to import all images using a glob import just to pick the one I’m interested in.

const images = import.meta.glob('/src/uploads/*{.webp,.jpg,.jpeg,.png,.heif,.heic}', {
  import: 'default',
  eager: true,
  query: '?w=800;1500&format=webp&as=picture'
})
const image = getImage(images, lqipImages, imagePath)

I also have to produce sensible errors when a referenced image isn’t found or can’t be converted the way I expect. See the definition of getImage

Final Words

In hindsight I should probably have picked Astro instead of Sveltekit with the possibility to keep the React components from Gatsby, but I guess I have to have something left in the backlog. The explicitness of client vs server in Astro also appeals more to me than the “magic” that handles where my code is run in Sveltekit.

The overall experience of using Sveltekit is nice though and I would absolutely use it again, especially in a scenario with an active server, like the stuff I build at work.

The build times are actually a bit slower when comparing Sveltekit to Gatsby, but now it builds on Node.js 20.x and installs without --legacy-peer-deps, which is more important. Maybe I should’ve gone with Hugo…

No matching posts found. You can use wildcards and search only in titles, e.g. title:iot
Loading search index, please wait...
Search index failed to download 😢