Through the many technologies I’ve used over the years, I’ve come to realise that there are a few that I keep coming back to. Whether it be for personal projects, my company or just for fun, these are the technologies that I’ve found to be the most useful and enjoyable to work with.
I’ve decided to call this stack the SUKIT stack (yes, really), which stands for:
Unless you've been living under a rock for the past few years, you've probably heard of at least one of these technologies. They're all relatively new, and they're all great for building modern web applications. I'll go into more detail about each of them below.Svelte
Svelte is a JavaScript framework. Or, due to its compiler-centric nature, it could technically be called a language for composing interactive user interfaces. Svelte allows you to build web applications using a declarative syntax. It solves many of the same problems React or Vue do, but with a few key differences:
Svelte is actually reactive
This means that you don’t need to use useState
or useEffect
to update your components. Svelte will automatically update your components when their dependencies change. In many cases, simply updating a let
variable is enough to trigger an update.
Another great feature built into the language is stores, which are a way to share state between components. Stores are reactive, so updating a store will automatically update any components that depend on it. They can be accessed like this:
// stores.js
import { writable } from 'svelte/store'
export const count = writable(0)
And used like this:
<script>
import { count } from './stores.js'
</script>
<button on:click={() => ($count += 1)}>Increment</button>
<p>The count is {$count}</p>
Styling is built into the framework
This means you don’t need to worry about nonsense like Styled Components, Emotion, or CSS Modules.
Svelte is a actually a compiler, not a framework.
This means that it doesn’t have a virtual DOM, and it doesn’t have a runtime. In many cases, this makes it much faster than React or Vue. Partly due to compile-time optimisiations, and in part because it does not ship a large runtime. Less code means less to parse, and less to parse means faster load times.
Ultimately, Svelte could be considered a language instead of a web framework. It’s compiler-centric nature and own syntax make it a unique and powerful tool for building web applications.
Some examples of Svelte syntax
Conditionals
In Svelte, you can use the #if
block to conditionally render elements. You can also use :else
and :else if
to render different elements depending on the condition. This is in my opinion much cleaner than using ternary operators or &&
to conditionally render elements, as you would in React. Vue’s v-if
and v-else
syntax is also nice, but it looks too similar to HTML for my taste.
<script>
let user = { loggedIn: true }
</script>
{#if user.loggedIn}
<button>Log out</button>
{:else}
<button>Log in</button>
{/if}
Loops
Loops in Svelte are a breeze. You can use the #each
block to loop over an array, or any other iterable.
<script>
let todos = [
{ id: 1, text: 'Buy milk' },
{ id: 2, text: 'Buy cheese' },
{ id: 3, text: 'Buy bread' }
]
</script>
<ul>
{#each todos as todo}
<li>{todo.text}</li>
{/each}
</ul>
Two-way data binding
This takes me back to the good old days of AngularJS, back in 2014. Two-way data binding is a feature that allows you to bind a variable to an input element. This means that when the input changes, the variable will automatically update. This is great for highly interactive interfaces. You can use the bind
directive to bind a variable to an input element.
<script>
let name = 'world'
</script>
<input bind:value={name} />
You can also use bind:this
to bind a variable to an HTML element.
<script>
let element
</script>
<div bind:this={element} />
That’s the gist of it. Svelte is a joy to work with, and I highly recommend you check it out. If you want to learn more, check out the official tutorial or go mess around in the REPL.
SvelteKit
SvelteKit is Svelte’s first-party metaframework. It lets you use Svelte to build anything from static websites with 0KB of JavaScript, to packages and libraries ready to be published on NPM, to full-blown web applications with server-side rendering. I’ve used it extensively since its first public beta back in April of 2021. It celebrates using the platform, with a focus on native browser features like the Fetch API, Service Workers, and (soon) WebSockets.
Some of my favorite features
Directory-based routing
SvelteKit handles routing through the filesystem. This means that files on your system correspond to routes on your server. For example, a +page.svelte
file at src/routes
will be served at /
, and a +page.svelte
file at src/routes/blog
will be served at /blog
. SvelteKit decides which files are meant for the framework based on the +
prefix. A few examples of this are +page.svelte
, for writing pages as Svelte components, +layout.svelte
, for writing layouts, and +server.ts
, for writing server endpoints.
When this design decision was first made for the framework, I was skeptical. Coming from the initial file-based router, where routes were defined by their name (routes/about.svelte
would point to /about
on the server), I felt it was less ‘clean’ and more cumbersome. But after having used it in several large-scale projects now, I can comfortably say this is a great addition to the framework. Being able to colocate files, components and server endpoints works great in practice.
Data loading
Another important feature is data loading. SvelteKit solves this through the use of load
functions inside of +page(.server).ts
files. They make data fetching almost laughably easy to use:
// +page.ts
export const load = async ({ fetch }) {
const res = await fetch('/api/todos')
const todos = await res.json()
return {
todos
}
}
<!-- +page.svelte -->
<script>
export let data
</script>
{#each data.todos as todo}
<p>{item}</p>
{/each}
Server endpoints
For situations where you want to return custom responses, you can create an HTTP endpoint using a +server.ts
file. This is great for creating APIs, or for creating custom redirects.
// +server.ts
import { json, error } from '@sveltejs/kit'
export const GET = async () => {
return json({ message: 'Hello world!' })
}
export const POST = async ({ request }) => {
const body = await request.json()
if (!body.name) throw error(400, 'Missing name')
return json({ message: `Hello ${body.name}!` })
}
Automatic typings
SvelteKit automatically generates TypeScript typings for your load functions, endpoints and more. This means you rarely have to type your functions manually. This is great for productivity, and it’s also great for learning the framework. You can just hover over a function to see what it returns, and what parameters it takes.
Here’s a quick video of what that looks like, taken from this post on the official Svelte blog:
Supabase
Next up is Supabase. Supabase markets itself as an open-source alternative to Firebase, but in my opinion it is so much more. It’s a database, authentication service, and file storage service all rolled into one. It’s built on top of PostgreSQL, and it’s completely free to use. It’s also open-source, so you can host it yourself if you want to. It’s a great tool for building MVPs, and it’s also great for building production applications.
Some of my favorite features
Authentication Supabase has a built-in authentication service. It supports email/password authentication, phone log in, magic links and OAuth providers like Google, Facebook, GitHub, and a lot more.
Realtime
Supabase has a realtime feature that allows you to subscribe to changes in your database. This means that you can get updates in real time, without having to poll the database. This is great for building chat applications, or any other application that requires real time updates. It’s fast. Really fast. updates. It’s fast.
File storage
Supabase has a built-in file storage service. If I recall correctly, it’s built on top of AWS S3. It’s a great way to store user uploads, and the API is quite simple too:
// Upload a file
const { data, error } = await supabase.storage.from('avatars').upload('avatar.png', file)
// Get a file
const { data, error } = await supabase.storage.from('avatars').download('avatar.png')
Well, that’s it for Supabase. This blog is not their official documentation, so if you want to learn more, check out their website or their docs.
TailwindCSS
Last but not least, we have TailwindCSS. Tailwind is a utility-first CSS framework. Blabla aside, it’s a CSS framework that allows you to style your components using utility classes. It makes me productive as hell, and I love it. I’ve written CSS for years, and I’ll say I’m very good at it. Having used everything from SCSS to CSS Modules, I still prefer Tailwind over writing CSS by hand. It is simply faster. And as someone who is also a designer working on design systems, it is a life-saver for consistency. I can’t recommend it enough. Heck, I even have a testimonial on their homepage…
I’m not going to go into detail here, but I’ll give you a quick example of what it looks like:
<button
class="rounded-lg border-t border-gray-700 bg-gray-900 px-4 py-1.5 font-medium text-white shadow-sm transition hover:bg-gray-700">
Button
</button>
gives us this button:
If you know what CSS is, you’ll know what Tailwind does. Either way, I urge you to give it a try, even if you’ve developed bias against it. It’s not for everyone, but I find it to be a joy to use. If you want to learn more, check out their website or their docs.
Conclusion
That about wraps up my rambling about my favorite stack. To use it, we can use these handy little commands:
# (Optional) Set up a new SvelteKit project
pnpm create svelte@latest
# Install Tailwind
pnpx svelte-add@latest tailwindcss
# Install Supabase
pnpx apply supabase-community/svelte-supabase
That’s it for this piece. I hope you enjoyed it. If you did, please consider sharing it with your friends. If you didn’t, please consider sharing it with your enemies. If you have any questions, feel free to reach out; I’m always happy to help. Until next time.