Migrating a personal homepage to Svelte
Mar 4, 2024 #development #frontend #svelte
Thinking about the Svelte Web Framework? I’m going to describe a 3 fears I had before trying it out and where I landed on Svelte when compared with NextJS for personal homepages.
So, when I'm working on a blog or hobbyist side-project, I usually value minimizing boilerplate code. I’ve really enjoyed React’s way of thinking about components as composable units of code via unidirectional data flow. Server-side rendering pushed me into adopting NextJS (for a bunch of reasons, but one was just having an elegant way to implement meta-tags). NextJS worked pretty well for me, but I was listening to the Syntax podcast share positive things about Svelte. I decided I wanted to try it for this site, a frontend-only static blog.
Three Fears
Based on my previous experiences and the code for this blog, there were three themes I was nervous about Svelte after a quick read about it:
Flexibility
My first fear when reading the Svelte getting started guide was its lack of support for multiple components in one file. I was worried this rule was foreshadowing future inflexibility. As I’ve experimented with it, I’ve hardly been challenged by this limitation. My NextJS components nearly all 1:1 mapped to a page in Svelte. I realized that the only times when I had designed multiple components within a single TSX file were the times when I was destructuring complicated data structures into views. In these cases, I found the components more readable after migrating them to multiple components. Here are examples where I needed to think the hardest:
-
Comments & Sidebar Menus. In React, I defined comments and sidebar menus as data. For example, for Sidebars:
[{ name: 'Blog', link: '/posts', list: getBlogPosts(), // Tree with more data }, { name: 'About Jake', link: '/about' }]
When I went to render them, I recursively walked the tree structure and decomposed the sidebar into React components. (Note: The sidebar could have probably just been rewritten as basic
.svelte
files, but this was more difficult for my Mastodon Comments system. Solving this was straightforward using dynamic components, such as svelte:self and svelte:component components. I recursively walk the tree using injected-in properties. -
Blog posts. Each blog post on this website is two things: (1) metadata, such as the
title, date it was posted, etc, and (2) content. The content was previously JSX, and I was
anxious that migrating to Svelte would require each blog post to be spread across multiple
files. There are probably more ways to solve this, but this ended up being trivial for me by
defining
module-level exported constants
that can be imported when the Svelte Component is imported. Here's an example of how that
looks:
// Metadata defined alongside the actual posts, written as .svelte files <script context="module"> export const metadata = { static_path: '2024-02-04-svelte', title: 'Migrating my Blog to Svelte: Fears and Outcomes', tags: ['development', 'frontend', 'svelte'], // Dates are zero indexed in javascript date: new Date(2024, 2, 2), }; </script> <!-- content goes here -->
I then import that into a global post list with two lines: (I imagine someday I'll generate this file automatically.)
import { content as p2024m02d22content } from './2024-02-22-svelte.svelte'; import p2024m02d22 from './2024-02-22-svelte.svelte'; export const posts = [ { content: p2024m02d22content, component: p2024m02d22 as ComponentType<p2024m02d22> }, // ... more posts
Styles
Another fear I had was that it'd be difficult to translate my existing styles to Svelte, and expressing them using Svelte's styling system would require too many compromises. In React, I write many of my styles as using CSS-in-JS, describing them with variables in the same file as the JSX for the components that use them. The styles themselves were defined using Emotion. In the old code, I could easily make the styles more dynamic by just passing values into the style code.
Having dynamic styles was easy to do in Svelte. The main difference was that I had to pass in a var(--parameter)
into the CSS. Not only was it easy, but my code was more readable because it forced me to
separate the business logic from the rendering. The only caveat about Svelte styles is that
there's more boilerplate. I have lines referencing the color variables in the
<script>
, html, and <style>
sections of my
.svelte
file. I couldn't help but wish there was an easy way to reference the script
variables from the CSS file.
Translating my styles to Svelte was easy too. My actual styling code was so close between NextJS and Svelte that I was able to create a very simple ChatGPT prompt to help me do the rote work of translating double-quotes and commas to semicolons. The interaction looked like this (though, with less-trivial code-examples):
const buttonStyle = { color: "#FFF", backgroundColor: "#333", ":hover": { backgroundColor: "#000" }, }
.button { color: #FFF; background-color: #333; } .button:hover { background-color: #000; }
Though it occasionally hallucinated, ChatGPT did a better job than a static parser would because it was able to identify opportunities to change things for Svelte that I hadn't considered.
Templating – My final fear was that the .svelte
templates would
not be as expressive as I needed. I'd often destructure data in-line in JSX in NextJS / React.
In Svelte, I found it was simplier to map more complicated objects from within the
<script/>
section of the .svelte
file, which could be easily referenced by the html.
I was happy that Svelte #if
statements are sufficiently smart enough to behave as you'd
expect, as a trivial example:
{#if value !== undefined} {value} <!-- assumed not undefined here. --> {:else} {value} <!-- assumed undefined here. --> {/if}
Also, I found triaged issues in the Svelte open-source project[1] [2], that gave me the impression that the the Svelte team cares about templating being sufficiently espressive, even if not every problem is solvable by templates.
Outcomes
Ultimately, I had a very positive experience with Svelte. It’s my current favorite web framework for personal projects. I'm optimistic towards using it in future projects. Most of my usage so far has been for frontend-only projects, yet Svelte excels at defining fullstack web applications and I haven't touched on any of those capabilities in this post. Hopefully some of what I’ve written help you know if any of your fears are addressed by this web framework.
So, I’d love to hear from you! What do you care about most in a web framework? Please leave a comment on the Mastodon thread below!
Appendix
A note: I work at Google, on Firebase, I work on mobile developer tools, I don't work on any of the hosting solutions for web developers, but I wanted to disclose this. Both NextJS and Svelte work really well on both Vercel and GCP, though I get a slightly more cloud agnostic vibe from Svelte's adapters, yet my perspective is probably colored by where I work.
What about other Frameworks? I also took a quick exploration of Remix before
diving into Svelte.
Remix was acquired by Shopify
and looks really good. I think it’d be really sensible in a professional setting. I used
Emotion
to create inline-styles in most of my personal projects in React and this pattern seemed to not
be well supported in Remix
[1]
[2]. Though I'm not using CSS-in-JS in Svelte, the solution to
inline your css
within each .svelte
file felt similar enough in practice to be worth trying.