惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog RSS Feed
Google DeepMind News
Google DeepMind News
罗磊的独立博客
Martin Fowler
Martin Fowler
博客园_首页
Stack Overflow Blog
Stack Overflow Blog
Last Week in AI
Last Week in AI
The GitHub Blog
The GitHub Blog
B
Blog
C
Check Point Blog
WordPress大学
WordPress大学
G
Google Developers Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
量子位
月光博客
月光博客
U
Unit 42
Engineering at Meta
Engineering at Meta
有赞技术团队
有赞技术团队
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
大猫的无限游戏
大猫的无限游戏
博客园 - 聂微东
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Y
Y Combinator Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Vercel News
Vercel News
Application and Cybersecurity Blog
Application and Cybersecurity Blog
博客园 - 【当耐特】
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Jina AI
Jina AI
S
Secure Thoughts
aimingoo的专栏
aimingoo的专栏
D
Darknet – Hacking Tools, Hacker News & Cyber Security
I
Intezer
Latest news
Latest news
V
Vulnerabilities – Threatpost
D
Docker
Attack and Defense Labs
Attack and Defense Labs
Help Net Security
Help Net Security
S
Security @ Cisco Blogs
Forbes - Security
Forbes - Security
MongoDB | Blog
MongoDB | Blog
云风的 BLOG
云风的 BLOG
L
LINUX DO - 热门话题
P
Palo Alto Networks Blog
Cloudbric
Cloudbric
Spread Privacy
Spread Privacy

Josh Collinsworth

LLMs and performative productivity Man Cereal My review of the Nüborn Baby at 3 months 2025 Year in Review AI optimism is a class privilege Alchemy Titles matter The blissful zen of a good side project Goodbye, Griff. You were a good boy. Rare words in common phrases, and how to avoid getting them wrong Things I enjoyed in 2024 The childlike and the childish A response to "Defending Open Source: Protecting the Future of WordPress" If WordPress is to survive, Matt Mullenweg must be removed For whatever it's worth: my advice on job hunting in tech A decade of code Follow-up: the Glove80 after six months The quiet, pervasive devaluation of frontend I worry our Copilot is leaving some passengers behind Things I enjoyed in 2023 First impressions of the MoErgo Glove80 ergonomic keyboard A message from the Captain of the S.S. Layoff Things you forgot (or never knew) because of React Alfred vs. Raycast: my constant debate Adding page transitions in SvelteKit Ten tips for better CSS transitions and animations Understanding easing and cubic-bezier curves in CSS Impressions of the ZSA Moonlander at one month Why you should never use px to set font-size in CSS Forty-two Breaking changes in SvelteKit, August 2022 The self-fulfilling prophecy of React Announcing Hondo Building accessible toggle buttons (with examples for Svelte, Vue, and React) Debugging iOS Safari (when all you have is a Mac) Creating dynamic bar charts with CSS grid Let's learn SvelteKit by building a static Markdown blog from scratch Adding blog comments to your static site with utterances Introducing Svelte, and Comparing Svelte with React and Vue Goodbye, WordPress Announcing Quina (My First App)! How to Create Custom Editor Blocks with Block Lab A New Headless Site with Gridsome This isn't the Time, But it's the Perfect Time; Goodbye, Instagram How to Connect Local with CodeKit How to Check Uniqueness in an Array of Objects in JavaScript Adding Gutenberg Full- and Wide-Width Image Support to Your WordPress Theme Let's Learn CSS Variables! New Site, New Theme for 2018 Five Ways to Become a Better Designer (That Aren't Design) My Essential Tools for WordPress Development The Five Things I Wish Somebody Had Told Me as a Design Student WordPress Child Theme Explanation and Walkthrough Why Designers Shouldn't "Fix" Other Designers' Logos 8 Mistakes to Avoid in Your Student Design Portfolio Profit is Not a Value Understanding the Difference Between Image and Vector File Types Pantone, Color, and What I Wish I Had Known Sooner as a Designer Social Media, Compulsion, and the 12 Things I Learned on My Break from Facebook Classic rock, Mario Kart, and why we can't agree on Tailwind
Converting from Gridsome to SvelteKit
2021-10-04 · via Josh Collinsworth

Published: October 4, 2021
Last updated: October 14, 2021

I’ll assume for the sake of this post that you’re at least a little familiar with what Svelte is. If not, I’ve written an introduction to Svelte that you might enjoy reading first before diving in here.

Otherwise: let’s dive into what SvelteKit is, how it works, why I made the choice to switch (other than the obvious fact that I just can’t resist rebuilding my site about once a year), how it’s paid off, and whether I’d make the same decisions again.

What is SvelteKit?

If you’re familiar with Next or Nuxt, it would be fair to think of SvelteKit as the Svelte equivalent.

If not: all three are “meta-frameworks,” sometimes also called app frameworks. You could think of meta-frameworks as a large set of add-ons for frontend UI frameworks like React, Vue, and Svelte (in the cases of Next, Nuxt, and SvelteKit, respectively).

If a frontend framework is a toolbox, a meta-framework is a complete workshop.

Why? Frontend UI frameworks are ideal for…well, frontend UIs. They’re built to handle pieces of an interactive user interface. By nature, being JavaScript-based, they’re limited to the capabilities of the browser page they’re loaded on. (Because of this, sites built just with a frontend framework are sometimes called “single-page applications,” or SPAs.)

A meta-framework is an enhanced toolset for building full-fledged sites and apps with a specific frontend framework (hence the “meta” part of the name; a framework for a framework).

Most meta-frameworks come with all your build tools and routing pre-configured. They also generally include data stores; layouts; image optimization; better SEO and full-page control; data fetching; and/or plugins—usually just about everything except a database to help you build anything you might want.

Going static

Next, Nuxt, and SvelteKit are all capable of building your finished project as a server-side rendered app, as a static site, or as some combination of both.

SvelteKit is even more adaptable than that, thanks to its aptly named adapters, which process your code differently for whatever type of output and hosting you’re targeting.

Currently, SvelteKit offers adapters to run your project as a Node app; as static, pre-generated HTML files; or as serverless functions (there are adapters specifically targeted for Netlify, Vercel, and Cloudflare Workers). There are several community-created adapters available as well, or you can even write your own.

This site uses SvelteKit’s static adapter, which means the pages and components are pre-rendered as plain ol’ HTML files. They can still benefit from “hydration”—JavaScript running once the page has loaded—but they don’t have to.

Thanks to the static adapter (and some strategic <noscript> tags), most of this site works just fine even with JavaScript disabled entirely.

Worth noting, however: by default, after the first page load, SvelteKit’s router hydrates and takes over page loading, to make transitions as smooth and fast as possible. You can even designate routes to preload in the background, so that by the time the user clicks, the load is nearly instantaneous.

You don’t have to go entirely one way or the other; even if you’re deploying your project as a Node app or with serverless functions, you can still mark specific pages to be prerendered as static HTML—handy for things like an “about” page that don’t need dynamic content.

SvelteKit layouts

SvelteKit follows the convention of layout files, which are: files that “wrap” the content of any given page in additional markup, such as a header and footer.

Your base layout file renders every route in your site or app, so you’ll most commonly use it as a base template that includes everything that would appear on every page of the site. The layout includes a <slot /> for the page’s main content to go inside, whatever that page or content might be, and SvelteKit renders the content in that slot.

Here’s what this site’s main layout file’s markup looks like (slightly simplified for readability):

<div
	id="app"
	class:reduce-motion={$prefersReducedMotion}
	class:prefers-dark={$prefersDarkMode}
	class:sidebar={pageHasSidebar}
>
	<Loader loading={isLoading} />

	<Header />

	<div class="layout">
		<main id="#main" tabindex="-1">
			<PageHeading />

			<PageTransition>
				<slot />
			</PageTransition>
		</main>

		{#if pageHasSidebar}
			<Sidebar />
		{/if}
	</div>

	<Footer />
</div>

That’s pretty much it. This way, every page includes the header and footer, and the sidebar where appropriate. Some states and preferences are passed in from the global store (those prefixed with a $) so that conditional classes can be applied as needed. And the <slot /> where the page content goes is wrapped in a custom <PageTransition> component that (predictably) just adds the fancy transitions between pages.

It is possible to have nested layouts, or layouts that apply on a per-route basis. You can also reset the layout, if you have a deeply nested route that needs its own setup.

And speaking of routing…

Routing in SvelteKit

By default, a new SvelteKit project has a src/routes directory. Anything inside src/routes compiles to a page at that relative root.

For example:

File:Becomes route:
src/routes/index.svelte/ (homepage)
src/routes/about.svelte/about
src/routes/blog/index.svelte/blog
src/routes/blog/some-post.md/blog/some-post

And so on. (Markdown files do require a small bit of extra config, but yes, you can have Markdown files as pages, or just import Markdown to inject into pages or components.)

SvelteKit can also have dynamic routes. For example, /blog/[post].svelte would be a single component that would render any /blog/* slug. You can read more about SvelteKit Pages here.

The really magical part, though, is that you can have server-side routes, too!

For example: lots of places in a typical blog need access to the list of your posts. You might want to put your most recent posts on your hompage, have some posts in the sidebar, and of course, they should all be listed the /blog page itself. You might even want category or tag pages.

That’s a lot of fetching posts to redo over and over. So it’s a perfect use case for a server-side route!

If you want your app to have a /posts endpoint that returns JSON, you just create src/routes/posts.json.js. This will become a /posts.json route in the finished application.

From there, you just define a get() JavaScript function that retrieves the desired data and returns it (along with a status code). This is made easier by the fact that SvelteKit has top-level await and fetch available by default.

Here’s a somewhat simplified example of how you might create an endpoint to return all your Markdown posts as JSON:

// posts.json.js

// The `get` function responds to GET requests; you can have post(), etc. as well
export const get = async () => {
	const posts = await Promise.all(
		Object.entries(import.meta.glob('/blog/posts/*.md')).map(async ([path, page]) => {
			const { metadata } = await page();
			const slug = path.split('/').pop().split('.').shift();
			return { ...metadata, slug };
		})
	);

	return {
		status: 200,
		body: {
			posts //Automatically converted to JSON ✨
		}
	};
};

Once you’ve retrieved the post data as JSON, you can display it in a Svelte page or component; here’s a short example:

{#each posts as post}
	<article>
		<img src="/images/{post.coverImage}" alt="" />

		<h3>
			<a href={post.slug}>
				{post.title}
			</a>
		</h3>

		<p class="excerpt">
			{post.excerpt}

			<a href={post.slug}> Read more… </a>
		</p>
	</article>
{/each}

I won’t get too much into it here, but SvelteKit also offers a way to pre-load data server-side from routes like this, or from external sources.

Worth noting: when using the static adapter, there of course isn’t any server to query at run time. So in that case, any server-side queries or fetch calls will run at build time, and whatever the result at that point, it will be output as plain static files. Any JSON routes you might have will still be query-able on the live site, but they’ll be static.

The ability to read Markdown files isn't included by default in SvelteKit, but it does have the fairly easy-to-install MDSvex for that (the Svelte version of MDX, if you're familiar).

Static SvelteKit vs. Gridsome

Before we dive into comparisons, it’s worth mentioning that SvelteKit and Gridsome aren’t exactly the same type of thing. SvelteKit is an meta-framework capable of generating many different kinds of sites and apps, where Gridsome is just a fairly straightforward static site generator.

Still, if we’re scoping the discussion to just SvelteKit’s static adapter, I think it’s a fair, if not exact, comparison.

Comparing performance

To compare Gridsome with SvelteKit, we need to do a bit of configuration. Gridsome pre-loads all of its routes in the background, so that they’re already loaded up and ready to go instantly the moment the user clicks. So unless we’re doing the same thing with SvelteKit, we’re comparing apples to oranges.

Luckily, SvelteKit does offer prefetch and prefetchRoutes functions (the former to prefetch only specified routes, and the latter to just prefetch all routes). So I threw prefetching on all the routes on the site, just to see how it would stack up against the Gridsome version.

Even when preloading all the site’s content, the SvelteKit build is dramatically smaller.

FrameworkFull sizeCompressed
Gridsome3.09 MB1.74 MB
SvelteKit, preload all routes1.7 MB536 kB
SvelteKit, top-level routes only322 kB184 kB

As you can see from the table above, the SvelteKit version shaves about 45% off the Gridsome build, and over two thirds when compressed. The SvelteKit site at full size is about the size the Gridsome site was compressed!

Granted, 1.7 MB is not exactly tiny, but bear in mind that’s with the weight of every route on the site preloading.

The preloading strategy I eventually settled on, however, is the last row in the table: here, I preload only top-level routes unless we’re on a blog page, and then I preload all blog routes. This was my biggest savings, but there’s a tiny caveat that I also decided to eliminate Google Analytics in this approach as well, so the reduction is slightly exaggerated. Still, though: the overall load is tiny (and honestly, mostly made up of fonts. There’s a little over 100 kB of fonts to load on any given page).

Scrapping Google Analytics was an easy decision once I realized it blocked the main thread and prevented me from ever getting an optimal performance score—even, ironically, from Google Lighthouse. I don’t really need analytics on this site, but I can use Cloudflare or even pay for Netlify Analytics for that.

Perfect 100s across the board from Lighthouse test

By the way: although it isn’t implemented in any form in most browsers yet, I put a prefers-reduced-data media query on the site that will prevent preloading when detected.


Builds and dev start times with SvelteKit are also much faster: the production build of my Gridsome site ran about seven minutes, compared to about 90 seconds for the SvelteKit version (about five times faster). The dev startup times have similar gains. But again, this is misleading, for two reasons:

  1. Gridsome was doing a lot of image work at build time that SvelteKit isn’t by default; and

  2. Gridsome uses Webpack under the hood, where SvelteKit utilizes the much faster and more modern Vite (pronounced “veet”).

One particularly nice thing about Gridsome was its built-in <g-image> component. Just by using it in place of the standard HTML <img> tag, Gridsome would compress your images, generate a resized, responsive source set, use lazy loading, and create blurred placeholder images.

By contrast, out of the box, SvelteKit does…nothing to help with images.

My website uses few enough images (which are already generally compressed) that I decided browser-native lazy loading (combined with aspect-ratio) was acceptable for now. Hopefully, SvelteKit will have a first-party image compression option in the near future. (And even if not, it’s possible to rig that up directly through Vite, though that’s its own rabbit hole. Or, I could even use a service like Cloudinary if image size became an issue.) But for now, the few Svelte-focused solutions I tried didn’t work particularly well for my use case, and the tradeoff didn’t seem to be worthwhile.

Why move away from Gridsome?

As you can see from just perusing the posts list on this blog, it wasn’t all that long ago that I moved to Gridsome in the first place. I went to a headless Gridsome frontend just over a year ago, and converted the site to fully static barely seven months ago. I’ve already talked here about how nice Gridsome’s image handling is, and how fast it makes the site feel.

This naturally prompts the question: why move in the first place? At this point it almost seems like the only reason this site exists is so that I can rebuild it, then write a post about it.

I was a very early adopter of Gridsome, and at the time (in 2019), it still seemed to be regularly updated and headed towards a 1.0 release. But it’s been almost exactly two years since the last minor version update of Gridsome (0.7), and at this point, it doesn’t seem like it’s an actively maintained project any longer. It’s been all but silent since then.

Gridsome didn’t ever really feel complete to me, and that was fine when updates were still rolling out. I knew what I was in for going with a pre-1.0 technology, and it was really good at what it did well—namely, generating a speedy static site with Vue and GraphQL—but the more you wanted to tweak things or leave the happy path, the more you ran into its rough edges.

More than once, I spent a day or two fighting with NPM, unable to even run Gridsome on my machine. (That’s more to do with the packages Gridsome relies on than Gridsome itself, but still; the frustration is the same.) The last two times I’ve set up new machines, I’ve had to spend significant time trying to get Gridsome running on them. I had to explicitly set Netlify to an older version of Node to even get it to deploy.

But moreover: SvelteKit sparks joy in a way that Gridsome doesn’t anymore. It was exciting to be a part of something new and actively progressing with Gridsome, but now it just feels like being part of something forgotten and stagnant. SvelteKit replaces that feeling; the community is vibrant and the project has an electric momentum to it.

You might ask why, then, I didn’t move over to Nuxt, given that it’s a larger and energetic community. Plus, it’s still in Vue, which would seem less disruptive.

When I was writing this site in Gridsome, my list of Vue projects was fairly small, which made the appeal of having a Vue outlet more appealing. Now, though, it doesn’t feel like I need a site in Vue anymore—especially since my day job isn’t Vue-focused anymore.

Maybe the fact that I’ve been working professionally with Vue for the last two years (and released Quina late last year) is part of it, too. I still love Vue dearly, and will almost certainly pick it back up to write a project in Vue 3 one day in the near future. But silly as it sounds, for right now: that itch is scratched, and I want a different thing to play with.

This site, like any side project, is at least partially for me to enjoy. This is the one little corner of the internet that's 100% mine, where I can do anything I want for whatever reason. And that thing, right now, is SvelteKit.

Finally, TypeScript has first-class support in SvelteKit. I’m relatively new to TypeScript and have somewhat mixed feelings on it at this scope (I think it mainly shines on larger projects with multiple contributors), but I’ve been working on involving it more in my workflows to get better at it. At this point, close to 100% of this site’s JS is typed, so I’ve given it a good shot at least.

What other options were considered?

To some degree, I considered both Astro and Eleventy for this overhaul, and you could make reasonable arguments that either one would’ve been better suited for the task. If my primary goal had been to build the fastest statically generated site possible with absolutely minimal JavaScript client-side, I no doubt would’ve gravitated towards one of these tools.

In the end, however, Eleventy is still too unopinionated for my personal tastes, and Astro is still a bit too new. (Yes, SvelteKit is new, too, but it seems to have much more of a foundation beneath it.)

And again: this is my personal site, and so the tool I like the most is an important factor. So while SvelteKit might arguably be a little overkill, personally, I think it’s the most fun.

How does the code compare?

You might wonder: how does the old Vue code compare to the newer Svelte code? Is it shorter, better, and/or more readable?

Turns out: that’s actually a really tricky question to answer.

I originally thought I’d show side-by-side comparisons to demonstrate Vue vs. Svelte, but there’s been enough change at this point that most of the differences are either trivial, or so far apart they don’t even make sense to compare anymore.

The site’s changed quite a bit, even if it doesn’t necessarily look like it.

The one non-trivial component that’s mostly the same between the two versions is the font tester (seen on the /uses page). But it’s actually about the same size, both in terms of line count and disk size. The Svelte version is negligibly larger (only by a fraction of a kB), and almost certainly just because of the addition of TypeScript.

Most of the rest just isn’t comparable anymore. The colorful square grid in the header and footer has been completely refactored for better performance. TypeScript is everywhere; layouts have changed; new pieces have been added and old removed. MDSvex came with PrismJS built in, which let me delete both the a full component and an external library. Storybook and my tests are both removed for now. Lots was refactored. I relied more on global CSS previously, and have moved more towards component-based CSS this time around.

"Is it better?" is a really hard question to answer, even without getting into the highly subjective topic of what "better" even means. But I like it better (even the parts that are more verbose), and I think that's the most important part. I even enjoyed the relatively rote process of moving Vue components over to Svelte.

By the way: I kept an archival copy of the old site live for myself to look back on, just in case you’d like to compare the two for yourself.

And while we’re on the topic: here’s the link to my site’s new SvelteKit repo, if you’d like to take a firsthand look behind the scenes. A lot still needs to be refactored and cleaned up (I keep a list), but you’re welcome to poke around, or even clone the repo as a starter for your own blog if you like.

The redesign

I didn’t set out to make any design changes when moving this site over to SvelteKit, but after a while, I got tired of staring at the old design and started the dangerous journey of playing with new fonts.

In the end, the old body font (Averta Std) got promoted to the heading font, and I added a nice serif (Alkes) for the body copy. More of a small refresh than an overhaul, but the pairing and tweaked styles give the site a fresh new look that I very much like.

What to know about SvelteKit

As of this writing, SvelteKit is still in pre-1.0 status. It seems very stable to me—I don’t think anything is going to be changing—and Svelte itself is definitely solid. But there are still some portions of the Kit that aren’t fleshed out yet.

I found the static rendering to be extremely good, but as mentioned, SvelteKit can do a lot more than that. Depending on what you’re building and what features you’re most interested in, it may be worth spending some time to make sure SvelteKit is in good shape to handle your task, and works as expected with your deploy target.

Also worth knowing: Svelte itself doesn’t support IE11 by default. There are some workarounds, but hopefully that’s not anything that matters to you in the first place—especially since not only has IE11 support been dropped by a slew of major companies at this point, but it’s due to be killed by Microsoft itself in a matter of months at the time of writing.

Don’t listen to the haters

Arguments against Svelte(Kit) tend to focus on how it theoretically scales (emphasis on theoretically), and the relative size of its community and ecosystem compared to other frameworks.

I won’t go into either of those here, but I do address them both in detail in my introduction to Svelte post.

Don’t get confused by Sapper

One other thing to know at this point in SvelteKit’s existence is that it’s actually the second stab at a Svelte meta-framework; Sapper was the first.

Sapper never seemed as big as SvelteKit does now, but it’s been deprecated in favor of SvelteKit, and there’s still some confusion that arises when searching online for code solutions in the space.

SvelteKit doesn’t always work exactly the same as Svelte or Sapper by default (largely because Svelte and Sapper both have a Rollup config—Rollup being the bundler that powers Svelte—where SvelteKit has its own config file). So a lot of the examples and answers you come across related to setting up configuration are likely to either need some syntax adjustment, or just not work exactly as expected. (This doesn’t apply to Svelte itself so much as to SvelteKit and its unique configurations.)

Wrapup: would I use SvelteKit again?

For just about any project, yes, I would use SvelteKit again in a heartbeat.

Even though it’s still technically pre-1.0, SvelteKit feels very solid—much more so than other pre-1.0 frameworks I’ve tried—and it’s a delight to work with. The adapters allow you to tailor your input to any output you like, and the scope of things you can build with it is impressively vast. Plus, it’s likely to be smaller and faster than whatever else you might have chosen, and with even better developer experience.

As mentioned, it’s still early days for SvelteKit, so there are still some areas where its established solutions may not be as robust as with other frameworks. I can understand hesitance to bet the farm on something a little newer, but I don’t think I’d have any real hesitation at this point. An established solution with a rich ecosystem like Nuxt might have more to offer in this moment, but I’m confident both that I could do anything I needed to do in SvelteKit, and that it won’t be long before SvelteKit fills the gaps. It’s hard to imagine SvelteKit not becoming the #1 go-to in all cases in the near future—especially knowing it only came out of closed beta a matter of months ago.

The Svelte rocketship is a wonderful place to be. I encourage you to step aboard.