Vue vs. Svelte

April 8th, 2022

coke-pepsi

Javascript frameworks like React, Vue, and Svelte make building robust web applications easier. They provide a declarative layer that acts as sort of a middle-man between the user and DOM. The user communicates the output they want and the framework takes care of the busy work involved with manipulating the page.

In the past I've worked primarily with React. But I don't think there is a "best" framework. Each framework shines in different situations. And in an effort to add more tools to my web dev belt, I thought it may be interesting to write a blog post where I take React out of the picture and compare Vue and Svelte.

So first up is Vue. What made it unique at the time it was created? Let's go back to 2013.

These days we kind of take JavaScript frameworks for granted. It's pretty easy to stand up a sleek, performant web application. But when Evan You began building Vue, most web developers relied on tools like jQuery to build their apps. This was a very hands on approach - the more complex an application became, the more painful it was to keep everything working in unison.

To help solve this problem, Evan leveraged the concept of declarative and reactive rendering. The idea was that when a user interacts with an application their actions should change state, not the DOM itself. And then there's logic in place that updates the DOM efficiently based on what state changed. I should mention that in 2013 this idea was already being used by other frameworks. However, Evan was striving to offer better performance and simpler syntax. Not only did he want to improve on other frameworks' functionality, he wanted to make sure new developers could pick up Vue and be productive as fast as possible.

When a developer builds a Vue application they assemble the UI by combining small, manageable pieces called "components". This not only improves the aesthetic of a page, it allows for a separation of concerns with application state. For example, the button the user clicks to sign out shouldn't have any knowledge of how many items are in their shopping cart. Each small piece of an interface should be concerned with as little state as possible. When state changes in a Vue application, a diffing algorithm compares the new state to the old and determines which components need to be updated. It will then re-render only those components. This makes the app performant while also preventing changes to irrelevant components.

The main thing that helps ensure Vue is easy to learn is the way the components are written. Each component uses a template syntax that's accessible to any developer with a rudimentary knowledge of HTML, CSS, and JavaScript.

<script>
  export default {
    data() {
      return { count: 0 };
    },
  };
</script>

<template>
  <button @click="count++">increment</button>
  <p>{{ count }}</p>
</template>

<style>
  p {
    color: blue;
  }
</style>

Additionally, it's possible to create a Vue application without a build step - you can pull the framework into an HTML document via a script tag and you're off to the races.

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Hello World</title>
  <script src="https://unpkg.com/vue/dist/vue.min.js"></script>
  <script>
    document.addEventListener('DOMContentLoaded', function () {
      new Vue({
        el: '#myDiv',
        data: {
          message: "Greetings!"
        }
      })
    })
  </script>
</head>
<body>
  <div id="myDiv">
    <p>{{ message }}</p>
  </div>
</body>
</html>

A framework like Angular sometimes gets described as having too many tools included out of the box. Which isn't desirable to developers who want to make a simple application. On the contrary, a framework like React sometimes gets described as not having enough included. This makes it challenging to figure out which tools to reach for. Vue does a great job of keeping things minimal, while at the same time including just enough out of the box to get your app up and running quickly. Also, it's clear which tools to reach for (and how to use them) when you want to scale up your project's complexity.

In 2016, a developer named Rich Harris started working on a new framework called Svelte. His aim was to push Evan's ideas even further. He wanted to make a tool that was more performant than Vue and even easier to use.

Rich wanted to take a slightly different approach. He wanted Svelte to handle as much of the work as possible at build-time instead of run-time. The idea was that the magic would happen prior to deploying your application, when your code is compiled to static files the browser can interpret. With a framework like Vue, the magic happens while your application is actually running in the browser. This is only possible because Vue includes the extra code needed in the files generated. So Rich's approach would allow for smaller files, hence the application would load faster.

Although Vue's declarative/reactive model works like a charm, it leaves more to be desired from a performance standpoint. The 2022 user expects speedy applications. Svelte is more surgical in how it updates the page when state changes. Instead of diffing the entire application with each change, Svelte (or rather just JavaScript) updates the HTML elements associated with that changed state. With Svelte there are no components in play by the time your app is running in the browser, everything's already been converted to plain JS.

In this LogRocket interview, Rich mentions he feels the concept of a virtual DOM (referring to React's approach to declarative/reactive rendering) is less about performance and "more about providing this really nice, declarative programming model". He goes on to say how there are other ways to provide that same declarative programming model without the overhead that comes with it. Essentially he wanted to create a tool that provides the same functionality but without the extra baggage.

Rich says: "Svelte analyzes your code during your build step and converts it into (essentially) vanilla JavaScript. Or as close as possible to what you would write if you were writing your code by hand." So, as far as the browser's concerned, you're not even using a framework. It performs lightning quick.

Svelte's component syntax is pretty similar to Vue, but it's even simpler:

<script>
  let count = 0;
</script>

<button on:click={() => count++}>
  increment
</button>
<p>{count}</p>

<style>
  p {
    color: blue;
  }
</style>

With all that said - the Vue team has started adopting Svelte's ideas in recent years. Compared to the original version, the most recent version of Vue does more at compile time in an effort to provide smaller files and faster performance. There's less of a difference between the two frameworks than there was five years ago. But the developer experience Svelte provides is still second to none.

🤘

Depending on what you're looking to do, both Vue and Svelte are great options. I should mention I built out basic todo list applications as a way to get a better feel for these two technologies. If you want to check out the Vue one, the repo's here, and app is deployed here. For the Svelte one, the repo's here, and app is deployed here.

To wrap up, here's a syntax comparison for a few common scenarios:

Conditionals

Vue
<div v-if="conditionA">some content</div>
<div v-else-if="conditionB">some other content</div>
<div v-else>even more content</div>
Svelte
{#if conditionA}
  <div>some content</div>
{:else if conditionB}
  <div>some other content</div>
{:else}
  <div>even more content</div>
{/if}

Looping

Vue
<ul>
  <li v-bind:key="value" v-for="value in arr">
    {{ value }}
  </li>
</ul>
Svelte
<ul>
  {#each arr as value}
    <li>{value}</li>
  {/each}
</ul>

Reactive Values

Vue
<script>
  export default {
    data() {
      return { count: 0 };
    },
  };
</script>

<template>
  <button @click="count++">increment</button>
  <div>{{ count }}</div>
</template>
Svelte
<script>
  let count = 0;
</script>

<button on:click={() => count++}>
  increment
</button>
<p>{count}</p>

Binding an Input Value

Vue
<script>
  export default {
    data() {
      return {
        inputValue: "",
      };
    },
  };
</script>

<template>
  <input v-model="inputValue" />
</template>
Svelte
<script>
  let inputValue = 0;
</script>

<input bind:value={inputValue}>

Computed Values

Vue
<script>
  export default {
    data() {
      return { count: 0 };
    },
    computed: {
      double() {
        return this.count * 2;
      },
    },
  };
</script>

<template>
  <button @click="count++">increment</button>
  <p>{{ count }}</p>
  <p>{{ double }}</p>
</template>
Svelte
<script>
  let count = 0;
  $: double = count * 2;
</script>

<button on:click={() => count++}>
  increment
</button>
<p>{count}</p>
<p>{double}</p>

Props

Vue
<script>
  export default {
    props: ["count"],
  };
</script>

<template>
  <p>{{ count }}</p>
</template>
Svelte
<script>
  export let count;
</script>

<p>{count}</p>

Named Slots

Vue
<BaseLayout>
  <template #header>
    <div>header content</div>
  </template>
  <template #default>
    <div>main content</div>
  </template>
  <template #footer>
    <div>footer content</div>
  </template>
</BaseLayout>

<!-- BaseLayout component -->
<template>
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</template>
Svelte
<BaseLayout>
  <header slot="header">header content</header>
  <main>main content</main>
  <footer slot="footer">footer content</footer>
</BaseLayout>

<!-- BaseLayout component -->
<slot name="header"></slot>
<slot></slot>
<slot name="footer"></slot>