DEV Community

Rob Levin
Rob Levin

Posted on

I finally switched from Sapper to SvelteKit

Original post (ironically the blog I'm talking about itself) is here

This post is a sort of “stream-of-conciousness set of notes” that jot down lessons I learned as I refactor this previously Sapper-based blog to use SvelteKit instead.

My main motivation for updating the blog is that I'd like to incorporate agnostic-svelte examples and dog-food that package. The main AgnosticUI site leverages Vitepress which allows you to use Vue components within your content. So, any rendered examples you see are using Vue 3 under the hood. As I'm big on verification through tried-and-true real usage, I wanted to start creating Svelte templates here on my blog as well.

Svelte

Along the way there were a couple of basic Svelte lessons I had to learn or relearn as well. Probably it's stuff I should have grok'd when I built the Sapper-based site but didn't.

Auto-subscriptions

I noticed I had a memory leak bug in my old blog code — I wasn't using onDestroy(unsubscribe). I went to the docs to ensure I understood how I should do that and discovered auto-subscriptions:

Svelte has a trick up its sleeve — you can reference a store value by prefixing the store name with $...You're not limited to using $count inside the markup, either — you can use it anywhere in the <script> as well

As such, I refactored code like:

  import { isDarkModeEnabled } from '$lib/common/store.js';
  let isDarkMode;
  onMount(() => {
    const unsubscribe = isDarkModeEnabled.subscribe((value) => {
      isDarkMode = value;
    });
  });
  onDestroy(unsubscribe);
Enter fullscreen mode Exit fullscreen mode

To simply use $isDarkModeEnabled removing for the lifecycle code cruft:

  {#if $isDarkModeEnabled}...conditional code here...{/if}
Enter fullscreen mode Exit fullscreen mode

SvelteKit

These are some SvelteKit specific notes…

Static Site

For my use case, I needed to be able to completely build out a static site and was please to learn that @sveltejs/adapter-static can give you that for free. No fuss, just followed the README instructions and was off and running.

Page Slugs with no .html

The way I originally set up the Sapper version of this site had a couple of features in the produced URLs that I still like:

  • The blog post comes right off the top-domain so foo.com/my-slug
  • Omits .html file type

In fact, if you look in the URL address bar you should see:

https://developtodesign.com/sveltekit-refactor-notes

Well, once I refactored to use SvelteKit and built the static site with:

npm run build # npm run preview to verify
Enter fullscreen mode Exit fullscreen mode

I noticed that my site would work ok if I came in from the home page and clicked around. However, if I hard reloaded an article's URL e.g. this very page: https://developtodesign.com/sveltekit-refactor-notes I would get a 404 not found. On closer examination, I saw that the static pages being produced actually had .html file extensions. I did not find instructions for this in the static adapter readme (perhaps it's considered common knowledge), but I added the following to my .htaccess file which got things working as expected:

RewriteEngine On
RewriteBase /

# Check if request is for an actual file, serve the file
RewriteRule ^([^\.]+)$ $1.html [NC,L]

# Catch request to HTML file, remove extension
RewriteCond %{THE_REQUEST} ^[A-Z]{3,9}\ /([^\ ]+)\.html
RewriteRule ^/?(.*)\.html$ /$1 [L,R=301]
Enter fullscreen mode Exit fullscreen mode

SvelteKit & mdsvex

I switched to mdsvex to pull in my markdown-based static posts. This seems to be the popular choice for SvelteKit static blogs.

Honestly, fussing around with getting mdsvex and svelte-kit Endpoints to work together took some experimentation. My blog articles are NOT at /posts/my-article-slug as is typical, but right off the top-level e.g. /my-article-slug. Achieving this was a challenge but I got it going.

I found I could only get the mdsvex to work correctly if I “artifically” created a src/routes/posts directory and placed my index.json.js in there, but place my content top-level in src/routes and use [slug].json.js to load the articles individually. It's nuanced and if you're looking for details I'd point you to my blog's GitHub repo and you can reverse engineer ;-)

Code Highlighting

mdsvex provides Prism.js support out-of-the-box and so all I had to do was to drop in a css theme and I had code highlighting. Super easy!

Svelte Components in Markdown

One of the more interesting features of mdsvex is full support for Svelte syntax—perfect for my motives to start showing some example templates using agnostic-svelte! I'm quite happy that the AgnosticUI Svelte setup docs worked just fine.

Feeds

From when the site was in Sapper, I had dynamic sitemap, rss, and atom feeds. I basically hand-refactored any subtle path changes, etc., and got those working. But, they all leveraged a helper to actually pull in the markdown. It used to be super long and I was quite please that it's been reduced to just:

export const getMarkdownContent = async () => {
  const posts = await Object.entries(
    import.meta.globEager('/src/routes/*.md')
  ).map(([, post]) => post.metadata);
  posts.sort((a, b) => (a.date < b.date ? 1 : -1))

  return posts
}
Enter fullscreen mode Exit fullscreen mode

The huge lines-of-code saver was this import.meta.globEager thing that Vite calls Glob Import. It's very nice to have your builder remove the need for extra dependencies.

The dynamic feed scripts are a bit involved for this article, but feel free to reverse them off the blog's GitHub repo.

Conclusion

So far I've mainly enjoyed working with SvelteKit. I didn't find a lot of examples of having the posts show up top-level as I've done here, but did find the following resources helpful in one way or another:

Top comments (0)