What's your stack?


The inevitable question every blogger must post about is "What is your stack?". My main piece is SvelteKit, which is fucking amazing. I was already a fan of Svelte, and SvelteKit just takes it to a whole new level.


I use Svelte because I want something performant (I tend to write a lot of code that touches stuff like WebGL), and easy to use. I know people have some issues with Svelte's template language, but I think it is pretty darn good. JSX looks incredibly ugly to me, with all it's ternary if expressions and maps over arrays. I admit I haven't used JSX very much at all, but I don't understand how people don't have trouble "jumping" between HTML elements and JS expressions when they're deeply nested inside each other. With Svelte, the control flow always sits "outside" the elements, and the "values" always go inside the elements which makes it so much easier to understand what the fuck is going on, which is a good quality to have with code. I might also just be biased against ternary expressions because I'm also a Rust Developer. Lol.


I also find I've got a good mental model for Svelte. I don't get the "I don't like Svelte because it's magic" argument. Any high level programming language is magic. There's good magic and bad magic. Svelte is definitely in the good magic. The code I write is the code that runs. I put dollar signs infront of things that need to be recalculated when their dependencies change. The dependencies are calculated by analyzing the source code (Thank god for not needing to manually specify them). When a varible changes, Svelte marks it dirty and the recalculate dance begins. Easy peasy lemon squeezy.


And now SvelteKit enters the scene. Meta-frameworks are the current hotness, and for good reason. In exchange for tying your soul to JS, you get to have an incredibly flexible but performant experience. It's hard to describe without a point of comparison, so maybe I should talk about the SSG that I'm most familiar with...


Rant About Hugo


Hugo is one of the worst pieces of dev software I've had the misfortune of using. It's so bad I'm going to spend multiple paragraphs explaning why it's worse than going with a meta-framework (SvelteKit, Next, Nuxt, etc), and why it's bad in isolation too. I've had to use it for multiple years in production, so I've had plenty of time to hate it.


Let's start with speed shall we? After all we are dealing with "The world’s fastest framework for building websites" [citation needed] . They boldly claim that "At <1 ms per page, the average site builds in less than a second." One of the bigger sites I work on has 485 pages. I regularly have to wait upwards of 15 seconds for it to build (cold start). The worst I've seen was 41 seconds, this was on a modern powerful desktop machine with SSDs. Fourty One Seconds for a site with just under 500 pages means that on average it was taking almost 100ms per page. Hugo proudly prints the total time it took to build in milliseconds, which makes it absolutely comedic when it shows an number in the tens of thousands. At least the save + refresh cycle is much faster, since that's what your going to be doing most of the time.


Hugo doesn't seem to be that stable either. It regularly crashes once a day for me with a nil pointer exception. This usually happens when a file change is made while running it with the dev server. You know, the one thing a dev server should be good at, and you will be doing a lot when building a website.


Alright, so it's not fast and it's not stable. I've used plenty of software like that before because the developer experience or built artifact is so good it's worth dealing with those issues. So how does Hugo hold up? Let's start with the developer experience. There's three main directories you'll be dealing with, 'content' for markdown files, 'static' for assets (images, JS, CSS, etc), and 'layouts' for HTML templates (Think something like Handlebars).


Let's start with 'static'. Not much to complain about here, apart from hugo doesn't hash filenames for assets, which means that you now need to do a browser hard refresh when you push changes to prod. Fine, whatever.


You'll be creating markdown files for making pages, with frontmatter up top and then body content beneath it. Ok, I'm not a huge fan of markdown, but sounds like I can work with that. Hugo also has a 'data' folder, where you can put things like JSON and then use that information in your pages. Neat! Except, you can't generate pages with this. So if I have a nice big JSON file which contains the data for 100 things, I need to make 100 markdown files first, each with different frontmatter that I can use to index my organized piece of JSON. Sigh, that's tedious as hell.


The 'layouts' folder is the last bit you'll be dealing with, unless you're using an existing hugo theme and not modifying it in which case you won't be touching it at all. Up until this point, I would be on the fence about recommending hugo. If you've got a silly little blog where you're going to just use an existing theme with absolutely no modifications (and you have no problems with the issues I've mentioned so far) then hugo might be for you. However if you're trying to build something a little more custom with nested routes then this is where hugo absolutely falls apart. You could say "But aren't you then using hugo for a job it wasn't designed for? Of course it's not going to work well". But hugo advertises itself as a generic SSG. Their showcase sites are a mix of blogs, documentation, and company marketing sites. As such, I'm judging it as a generic SSG. So then, what's wrong with layouts?


Hugo has many magic names. It has 'baseof.html' as your root layout. Ok, odd choice but fine. Then you've got 'index.html' for your root page. Sure, that makes sense. Except you can also do 'home.html' too. Two names for the same thing is bad. Then we have 'list.html' and 'single.html'. You use 'list' for group pages (think '/posts') and 'single' for individual pages (think '/posts/01'). This division is bizarre. But ok, is that all? No, because there are 'baseof-*.html' for each of the above, which (I think?) reset the current layout. After then there are '*.html.html' versions of each of these for some stupid reason. That is a lot of file name permutations.


As a result of this chaos, hugo has an insane lookup order. This is one of the things that has tripped me up the most. Along with hugo's terrible error messages, you often have no idea why your site isn't working. Either hugo will tell you about a missing template (but not give you enough information to quickly locate the problem) but march on regardless, or just error out (and give you even less information about where the problem is).


Inside these templates your templating is done with functions from the go programming language. It uses '.' as the context for the current template, which is tiny and easy to miss. Forgetting to pass it to another layout or partial often does not throw a compile error. Functions don't have parenthesis around their arguments, they just have a set number of arguments seperated by whitespace. This makes nested function calls insanely hard to read. There are so many other nitpicks here, I really don't like it. It's also just not that powerful. If there isn't a builtin function to acheive what you want to do, you're usually screwed, or you have to restructure large chunks of your project.


Perhaps most problematic, hugo also suffers from the "two language problem". As soon as you want any interactivity on your page, you're going to need to reach for JS. Let's say that you want your blog posts to be sortable. Now all that hard work you did in your layout file has to be re-implemented in JS. Or alternatively you just ship blank HTML and get JS to do everything. At that point, there's no advantage to using an SSG, just make an SPA and suffer the worse SEO and accessibility.


And finally, hugo is made with the go programming language. Even though I haven't used go, it seems to be a very flawed language. It's a small thing, but the reveal that hugo is written in go seems to play a large part of why I don't like using hugo (go's formatting for templates, frequent crashes, terrible error messages, dumb magic filenames, lots of boilerplate). From everything I've read about go, hugo seems to embody a lot of go's general feel.


Let's sum up with my list of issues with hugo:

  1. It's not actually that fast.
  2. It crashes frequently.
  3. No asset filename hashing.
  4. No way to generate pages from JSON or other data.
  5. Lots and lots of magic filenames.
  6. Insane template lookup order.
  7. Terrible error messages.
  8. Formatting can be hard to read and write.
  9. Limited formatting power.
  10. Two language problem.

SvelteKit to the rescue


Let's go over each of the above points and talk about how SvelteKit solves them.


  1. It's not actually that fast.
    SvelteKit is fast (faster than hugo at least). It uses vite under the hood and as a result of the unbundled development I've never seen a dev server startup take more than a second. Additionally, hot module reloading means no more page refreshes while working on the site. If you make only CSS changes, it even keeps all component state!
  2. It crashes frequently.
    SvelteKit seems extremely stable. If you have a misconfiguration, sure it'll throw an error. But it doesn't randomly die with nil pointer exceptions like hugo does.
  3. No asset filename hashing.
    Vite hashes assets for you, but provides an opt-out if you need it (e.g. ROBOTS.txt)
  4. No way to generate pages from JSON or other data.
    SvelteKit allows dynamic routes, and it crawls as at build time. Which means you don't have to make a file for every page in a directory.
  5. Lots and lots of magic filenames.
    SvelteKit has far fewer magic filenames, and they all start with '+' so you can imediately identify them.
  6. Insane template lookup order.
    Because SvelteKit has fewer magic filenames, template lookup is much more predictable. It's also really easy to break things into components, so you need fewer layout files in the first place.
  7. Terrible error messages.
    Vite's error messages are ok, as are Node's and TypeScript's.
  8. Formatting can be hard to read and write.
    Svelte has beautiful formatting, and if you need to do something complex you can just make your own function and call it. Add comments if it gets really complex.
  9. Limited formatting power.
    SvelteKit gives you get the ability to run whatever JS you want, whenever you want.
  10. Two language problem.
    SvelteKit's pages are hydrated automatically meaning you get interactive sites without sacrificing SEO and accessibility. For example, did you know that you've been on this site for 0 milliseconds?
    But if you look at the source code sent from the server ('Ctrl + U' on Windows) it's fully fleshed out HTML unlike an SPA.

If that were all, SvelteKit would already be a solid choice. But it has even more things to offer like SSR, client side routing, a prefetching, and automatic code splitting. It's incredibly flexible, with some of the best performance web frameworks have to offer, and perhaps the best developer experience of them all.


TailwindCSS and Vercel


The other two pieces of my stack are TailwindCSS and Vercel. While Svelte has CSS that's scoped to components and Vite will bundle in CSS for you, Tailwind's prefixes make it quick and easy to design stuff for different screen sizes. Additionally, Tailwind has got a nice set of options when it comes to paddings, colours, and font sizes, which means you're not fiddling with individual values for ages. Vercel seems to be the hot new thing, and it has great support for SvelteKit, so it seems like a natural fit.