Yesterday I tried Svelte to see what all the fuss is about. Version 3.0.0 came out recently and it smells like Svelte is ready for the big time.

You can watch the livestream here 👇

Created by @rich_harris, Svelte’s unique idea is that there’s no runtime. The framework compiles itself away to become vanilla JavaScript and create the smallest bundles you ever did see.

Plus a few features to make development absolutely painless:

  • plain JavaScript in every file
  • plain CSS automatically scoped to components
  • plain HTML … with some additions
  • no state management

You can use plain HTML. You need the extensions to interact with Svelte and your code. Such is life.

Building with Svelte feels like magic. Lemme show you 👇

Building a Marvel movie timeline with Svelte

We built a Marvel Movie timeline; 22 movies sorted by their timeline inside the Marvel Cinematic Universe. The AMC website helped out with a list coz I had no idea there were so many Marvel movies 😅

Click 👆 to try it in action. Total JavaScript size just 17.2KB, or 7.2KB when compressed for transfer.

That still sounds like a lot for this app, but it’s less than React where you some 40KB goes just to React itself. The CSS compiled into a 1KB file with random classes for scoping. There’s nothing else.

App loads fast and works fast 👌

You can see the full code on GitHub. Importing the repo into CodeSandbox didn’t quite work and I’m not sure why.

Best way to start a new Svelte project is with the CLI

$ npx degit sveltejs/template my-svelte-project
$ cd my-svelte-project
$ yarn install
$ yarn dev

That creates a new Hello World project in my-svelte-project and runs the dev server. Go to localhost:5000 and start hacking. Every change triggers a page reload so you can see what you’re doing.

I didn’t have to install anything new to make the above commands work 🤷‍♂️

A Svelte component

Svelte components live in .svelte files, split into 3 sections:

  1. JavaScript inside <script> tags
  2. CSS inside <style> tags
  3. HTML at the bottom

Our App.svelte component looks like this.

// src/App.svelte
<script>
  // import other components
  import MarvelMovies from "./MarvelMovies.js";
  import Movie from "./Movie.svelte";
  import Header from "./Header.svelte";
  import Footer from "./Footer.svelte";

  // this is automatically reactive
  let index = 0;

  // assigning new values in functions triggers re-renders
  function nextMovie() {
    index = (index + 1) % MarvelMovies.length;
  }

  function prevMovie() {
    index = index - 1;
    if (index < 0) {
      index = MarvelMovies.length - 1;
    }
  }
</script>

<style>
  // all of this gets scoped to your component
  .grid-container {
    display: grid;
    grid-template-columns: 0.1fr 2.9fr 0.1fr;
    grid-template-rows: 0.1fr 2.8fr 0.1fr;
    grid-template-areas: "h1 h2 h3" "b1 b2 b3" "f1 f2 f3";
  }

  button {
    cursor: pointer;
  }
</style>

// free floating HTML is kinda neat
<div class="grid-container">
  <Header />

  <button on:click={prevMovie} style="grid-area:b1">👈</button>

  <Movie {...MarvelMovies[index]} />

  <button on:click={nextMovie} style="grid-area:b3">👉</button>

  <Footer />
</div>

You might wanna install the Svelte plugin for VSCode to have proper syntax highlighting, Prettier support, and all the rest. The plugin did cause some weird issues while I was streaming, but who knows what that was.

A few interesting things to note about App.svelte:

  • You can import .svelte components just like normal JavaScript
  • Every let variable becomes reactive by default
  • Assigning new values in functions triggers re-renders. No need to think about any of that
  • Your CSS is scoped to your component so there’s no bleed-over
  • Your HTML free-floats at the bottom
  • Just like in JSX, Svelte components become HTML-like elements
  • Dynamic values go in curly braces, {}, same as JSX

My favorite way to use custom components and add dynamic values to HTML. Vue’s approach never quite made sense to me 😇

A Svelte component with props

Where I think Svelte really shines is building components for re-use. Like the <Movie /> component we used above to render the Movie view.

// src/Movie.svelte

<script>
  export let title;
  export let description;
  export let image;
  export let yearOut;
</script>

<style>
  .main {
    grid-area: b2;

    display: grid;
    grid-template-columns: 1fr;
    grid-template-rows: 0.1fr 400px 1.1fr;
    grid-template-areas: "title" "thumbnail" "description";
  }

  h2 {
    text-align: center;
    grid-area: title;
  }

  img {
    max-height: 100%;
    max-width: 640px;
    display: block;
  }

  .thumbnail {
    grid-area: thumbnail;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  p {
    grid-area: description;
    padding: 0 1em;
  }
</style>

<div class="main">
  <h2>{title}</h2>

  <div class="thumbnail">
    <img src={image} alt={`${title} movie thumbnail`} />
  </div>

  <p>{description}</p>

</div>

Yep it’s mostly CSS.

You declare props by exporting let variables. Use them just like you would props in React 👉 <Movie title="Iron Man" description="..." />.

Because our Movie props come from a variable, we used a spread and that worked too. <Movie {...MarvelMovies[index]} />.

Notice that my CSS is very sloppy.

I’m redefining h2, img, and p tags and yet it’s totally fine. Svelte scopes it all to the <Movie> component.

Whatever CSS you write here affects this file/component only and that’s neat.

Plus there was no boilerplate at all to get props working, reactive, and passed into the template. To be fair, the same React component looks like this:

// <Movie> as React
const Style = styled.div`
  grid-area: b2;

  display: grid;
  grid-template-columns: 1fr;
  grid-template-rows: 0.1fr 400px 1.1fr;
  grid-template-areas: "title" "thumbnail" "description";
  
  h2 {
    text-align: center;
    grid-area: title;
  }

  img {
    max-height: 100%;
    max-width: 640px;
    display: block;
  }

  .thumbnail {
    grid-area: thumbnail;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  p {
    grid-area: description;
    padding: 0 1em;
  }
`

const Movie = ({ title, description, image, yearOut }) => (
    <Style>
      <h2>{title}</h2>
    
      <div class="thumbnail">
        <img src={image} alt={`${title} movie thumbnail`} />
      </div>
    
      <p>{description}</p>
    
    </Style>
)

Which isn’t so bad either 🤔

Svelte’s reactivity

Where Svelte shines is its reactivity. Look at all the state management for our app

  // this is automatically reactive
  let index = 0;

  // assigning new values in functions triggers re-renders
  function nextMovie() {
    index = (index + 1) % MarvelMovies.length;
  }

  function prevMovie() {
    index = index - 1;
    if (index < 0) {
      index = MarvelMovies.length - 1;
    }
  }

Define a variable, index, change it inside methods. When its value changes, the component re-renders.

😍

As far as I understand, Svelte is smart enough to re-render only the HTML nodes that rely on the index variable. Sort of like what you can achieve with MobX and React if you’re super careful.

This does seem easier than React’s setState. Less to think about.

However, reactivity can come to bite you in the butt with more complex apps. Sometimes it leads to an effect like screen tearing where parts of the UI update before everything is ready.

Imagine if instead of changing index, each button click changed title, image, and description separately. You might get each part of the UI updating on its own without waiting for all 3 to finish changing.

For that, Svelte comes with a built-in dispatcher. I haven’t used it, but looks like it gives you functionality similar to Redux.

Svelte’s event handling

Another neat Svelte feature is the event handling.

Svelte uses what I believe are called directives. thing:something syntax added to the HTML. Remember when I said there’s no such thing as plain HTML in modern JS? 😛

We used the on: directive to make buttons work

<button on:click={prevMovie} style="grid-area:b1">👈</button>

Call the prevMovie function when the button is clicked.

That’s it. No binding. No passing params. No nothing. Svelte figures it all out on its own.

Svelte’s template logic

This is where I think Svelte fumbles. Svelte’s template logic is easy to use, but reminiscent of old Mustache or Handlebars syntax.

{#if user.loggedIn}
    <button on:click={toggle}>
        Log out
    </button>
{:else}
    <button on:click={toggle}>
        Log in
    </button>
{/if}

Not sure how I feel about that 🤔

Easy to read, yes, easier to write than React’s nested ternary expressions, definitely, but means you have to learn another syntax beyond JavaScript, CSS, and HTML. Which can be good or bad.

Depends what you’re doing I guess. For simple stuff this is great.

The verdict?

Svelte is great. Quick to get started, easy to use, good ergonomics, not too complex. Like other frameworks, I’d have to build some bigger projects to see how it feels in the real world.

That’s where I’ve found in the past that React shines. With bigger more complex projects. Whereas Vue, in my experience, is quick to get started but breaks down with complexity.

Svelte? I don’t know yet.

But it feels like Svelte takes the best of React and the best of Vue to make something awesome.

I recommend you dig through the examples. They’re great and show off Svelte’s best features.

Like the built-in transition support. Love it 👌

Learned something new? Want to improve your skills?

Join over 10,000 engineers just like you already improving their skills!

Here's how it works 👇

Leave your email and I'll send you an Interactive Modern JavaScript Cheatsheet 📖right away. After that you'll get thoughtfully written emails every week about React, JavaScript, and your career. Lessons learned over my 20 years in the industry working with companies ranging from tiny startups to Fortune5 behemoths.

PS: You should also follow me on twitter 👉 here.
It's where I go to shoot the shit about programming.