React vs. Svelte: Comparing the Basics

February 23, 2021
Written by
Reviewed by
Diane Phan
Twilion

svelte vs react.png

When it comes to JavaScript frameworks, Svelte is the new kid on the block. I’ve been hearing a lot of buzz about it, so I decided to give it a try and see how it feels compared to React.

This article is going to talk a little bit about the differences between Svelte and React, and then show you how to build a basic app with both frameworks. The goal is to demonstrate a few core concepts that cover:

  • Structuring components
  • Initializing state
  • Passing props
  • Lifting state
  • Event listeners
  • Dynamic styling

There’s a lot more to discuss, including conditional rendering, lifecycle methods, and other cool concepts, but for this article I’ll focus on the basics and give my gut check feeling along the way.

Prerequisites

To get started with this tutorial, you should have the following tools and technologies ready to go:

Svelte vs. React

Both Svelte and React.js are component-based JavaScript frameworks for web application development. Their main difference is that Svelte doesn’t use a virtual DOM. It will compile your code down to vanilla JavaScript at build time, whereas React interprets your code at run time.

According to the Svelte documentation

Svelte is a radical new approach to building user interfaces. Whereas traditional frameworks like React and Vue do the bulk of their work in the browser, Svelte shifts that work into a compile step that happens when you build your app.
Instead of using techniques like virtual DOM diffing, Svelte writes code that surgically updates the DOM when the state of your app changes.

Cool! But the behind the scenes details weren’t as important to me during this investigation. I wanted to look at this from a developer experience perspective. What is it like to write Svelte versus React? Does it feel nice? Is it fun? Is it intuitive?

Let’s find out.

Scaffold your app

In this tutorial comparison, you’re going to be building a small app with the following requirements:

  • Three components: App, Heading, and Button
  • Every time the button is clicked, the heading will update with the current click count
  • Every time the button is clicked, the color of the button will change

Create a new directory on your computer called svelte-react using the following commands:

mkdir svelte-react
cd svelte-react

Your next step is to scaffold boilerplate Svelte and React apps and run them locally. Svelte’s setup process has one more step than React’s does. Your Svelte app will also run on PORT=5000 as opposed to React’s PORT=3000.

Svelte

In your terminal or command prompt window, run the following commands:

npx degit sveltejs/template svelte-test
cd svelte-test
npm install
npm run dev

React

Open a second terminal, and navigate back to your new svelte-react parent directory. From there, run the following commands:

npx create-react-app react-test
cd svelte-react
npm start

You’ll notice that the Svelte commands ran a lot faster - that’s because with Svelte, you’re not really running a utility, you’re cloning a template.

Build the App component

After running the commands above, you’ll see that both your Svelte and React starter apps have a lot of files and code already. Feel free to explore these files.

Component files for both Svelte and React projects should be placed inside the /src folder. If you look inside the provided /src folders, you’ll see that Svelte files end in the .svelte extension, whereas React component files end in .js.

Each starter app came with an App component, located at svelte-react/svelte-test/src/App.svelte and svelte-react/react-test/src/App.js. Open each of these files in your preferred code editor and delete the code inside both of them. You’ll start fresh with new code.

Component structure

Svelte

Unlike React components, Svelte components allow you to write code in a way that feels a lot more like writing the HTML, CSS, and JavaScript of yore.

All the JavaScript for your component goes inside <script></script> tags at the top of the file.

Below the <script></script> tags, you can write the HTML for your component. Yep - it’s just there, like in a regular HTML document. Whoa.

Then, below your HTML, you can add styles inside <style></style> tags. Fun fact, the styles in each component are scoped only to that component. That means, for example, you can style <p> tags differently in every single component, and the styles won’t override each other on import.

To get started with App.svelte, delete everything inside the file, and add some empty <script> tags:

<script>
</script>

The bulk of the code for your component is going to go inside these <script> tags.

React

In your React app, open your App.js file, delete the contents, and add the following:

function App() {

}

export default App;

This code creates a basic functional component called App() and then exports it. Note another key difference between Svelte and React here - in Svelte, you don’t export your components.

Imports

As described earlier, this app will have three components App, Heading, and Button. In both the Svelte and React apps, the Heading and Button components are imported into App so they can be used as child components inside the App component. You’ll write these components later on, but for now, know that you’ll reference them as you build out the App component.

Svelte

In Svelte, you add your imports inside the <script> tags. Edit your App.svelte file to reflect the highlighted lines:

<script>
  import Button from './Button.svelte';
  import Heading from './Heading.svelte';
</script>

React

Your imports in React go at the very top of the file, before your function (or class) component. At the very top of App.js, before your App() function, add the following code:

import Heading from './Heading.js';
import Button from './Button.js';
import { useState } from 'react';

In the case of React, you’re also importing the useState hook, because App is a stateful component. There is no analog for this hook in Svelte - you don’t have to import anything.

Initialize state

App is a stateful component. It will have two state values: color and count.

color is the color of the button. This value is passed as props to the Button component, and it changes every time the Button component is clicked. It will be initialized with a value of #000000, which is the hex code for the color black.

count represents the number of times the Button has been clicked. It will be initialized with a value of 0.

Svelte

In Svelte, state is created by assigning variables. Beneath your imports, inside the <script> tags, add the following state declarations:

let count = 0;
let color = '#000000';

Svelte also offers something called reactive declarations, which lets you recompute a state value. You don’t have to use these, but it’s very helpful if you’re using a state value that’s derived from other state values that are subject to change.

Something to note is that DOM updates in Svelte are triggered by assignment of state variables. If your state includes arrays or objects, updating them using methods like .push() won’t trigger a DOM update. Svelte details how to manage this in its Updating arrays and objectsdocumentation.

React

You’ve already imported the useState hook, so now it’s time to put it to action.

Inside your App() function, in App.js, add the following state declarations:

const [count, setCount] = useState(0);
const [color, setColor] = useState('#000000');

This code creates a state variable called count with an initial value of 0, and a method to update that value called setCount(). Likewise, it creates a state variable called color with an initial value of #000000, and a method to update that value called setColor(). At this point, Svelte’s state initialization feels a lot easier to read and use.

Render the component and pass props

In both projects, your goal is to create a user interface made up of a <main> element that wraps your two nested components: Heading and Button.

The App component is passing props to both of these child components. The Heading component receives the count state value, and the Button component receives the color state value, in addition to a handler function called handleClick() that you’ll write shortly.

Svelte

Svelte uses its own templating language to create your user interface and React uses JSX to accomplish the same thing. Svelte’s templating language feels very akin to writing HTML templates in a server-side environment, like with Flask, PHP, or Liquid. This article won’t cover the details, but what’s nice is that you can get started by writing HTML directly in-file, beneath your <script> tags.

Copy and paste the highlighted code in your App.svelte file, beneath your <script> tags:

<script>
  ...
</script>

<main>
  <Heading count={count} />
  <Button color={color} handleClick={handleClick} />
</main>

React

Back in App.js, copy and paste the following code inside your App() function, below the state declarations.

return (
  <main>
    <Heading count={count} />     
    <Button color={color} handleClick={handleClick} />
  </main>
)

This code returns the JSX for your user interface from your App() function.

This is where Svelte and React look most similar - passing props happens in exactly the same way. In this example, Svelte’s templating looks exactly the same as React’s JSX.

For React developers curious about Svelte, you won’t find anything surprising or new in regard to passing props. Receiving props, on the other hand, is slightly different in Svelte, but I’ll touch on that later on this post.

Lift state

For this app to work, everytime the Button is clicked, the App component’s count state value must be increased. That means you’ll need a mechanism for lifting data from the child component back up to the parent.

You already started this process in the previous step by passing the handleClick() function to the Button component as props.

This function, which you’ll write now, belongs to the App component. When you pass it as props to the child Button component, the Button component has access to it, and can call it every time the button is clicked. This is how the App component can become aware of what’s happening in its child component.

This function is responsible for updating the App component’s count and color state values.

Svelte

In App.svelte, copy and paste the following code inside your <script> tags, after the state declarations:

const colors = ['#00ff00', '#ff0000', '#0000ff'];

let handleClick = () => {
  count++;
  color = colors[Math.floor(Math.random() * 3)];
}

React

In App.js, copy and paste the following code inside your App() function, directly above the return statement:

const colors = ['#00ff00', '#ff0000', '#0000ff'];

let handleClick = () => {
  setCount(count+1);
  setColor(colors[Math.floor(Math.random() * 3)]);
}

In React, you’re required to use the setCount() and setColor() methods that you declared earlier to update the state values, whereas you can update them directly in Svelte.

That concludes your work in the App component. Now it’s time to move on to the Heading component.

Build the Heading component

The Heading component shows the app’s heading text and the click counter. It’s not a stateful component, and it receives one piece of data as props: count, representing the number of times the button has been clicked.

Inside the Svelte project’s src folder, create a new file called Heading.svelte.

Inside the React project’s src folder, create a new file called Heading.js.

Receive props

Svelte

Inside your new Heading.svelte project, copy and paste the following code:

<script>
  export let count;
</script>

<h1>Hello, I am a Svelte App!</h1>
<h2>The following button has been clicked {count} times.</h2>

Take a look at the highlighted line inside the <script> tags. This line indicates to Svelte that the component will be receiving a prop called count.

This enables you to use count directly inside your Heading component’s HTML template, as you can see on line 6 above.

Writing this felt a little weird, but having the props declared immediately at the top of the file does look nice, and I like being able to use the prop directly.

React

Switch to your new Heading.js file. Copy and paste the following code into this file:

function Heading({ count }) {
  return (
    <div>
      <h1>Hello, I am a React App!</h1>
      <h2>The following button has been clicked {count} times.</h2>
    </div>
  )
}

export default Heading;

This code creates a new functional component called Heading, that has one parameter: { count }, which is the count value destructured from the props object passed to the component.

Build the Button component

The Button component renders the <button> element on the UI. This component receives two props: color, which it will use to style the <button> element, and handleClick(), the function that will be called whenever the <button> element is clicked.

Inside the Svelte project’s src folder, create a new file called Button.svelte.

Inside the React project’s src folder, create a new file called Button.js.

Listen for events

Listening for interactive events like clicks and other mouse events is a slightly different process in Svelte and React. This section will demonstrate both.

Svelte

Copy and paste the following code into Button.svelte:

<script>
  export let handleClick;
  export let color;
</script>

<button style="--color: {color}" on:click={handleClick}>
  Click me!
</button>

In this code, both props are declared at the top of the file in the <script> tags.

Then, it creates the <button> element. You’ll notice there’s some new looking syntax on line 6. Ignore the style part for now, I’ll explain that in the next section. But checkout the syntax for the click event listener: it’s different from what you might be used to.

Svelte uses an on: directive to add event listeners to DOM elements. Svelte also has really neat event modifiers that allow you to, for example, only trigger a click event on the first click. You can also dispatch your own component events.

React

Add the following code inside Button.js:

function Button({ color, handleClick }) {
  return (
    <button style={styles}  onClick={handleClick}>
      Click me!
    </button>
  )
}

export default Button;

If your server is still running and you see an error at this point, don’t worry, you’ll be adding the styles object shortly and the error will be fixed.

The code above creates a new functional component called Button() that receives one argument, props, destructured into color and handleClick. The handleClick() function is available on the handleClick props, and you can use a standard inline onClick event on the <button> element JSX.

Dynamic styling

In this app, the Button component is receiving a color as props. This color value will become the background color of the <button> element.

Svelte

Dynamic styling is certainly possible in Svelte, but it’s less straightforward than I expected.

Unfortunately, you can’t use prop values directly inside your <style> tags in Svelte. You can, however, use the component’s HTML as a way to communicate between your JavaScript and your CSS.

Beneath your Button component’s HTML, at the end of the file, copy and paste the following styles into Button.js:

<style>
  button {
    color: white;
    background-color: var(--color);
  }
</style>

The background-color style property isn’t referencing the color props directly, it’s referencing a style variable called color that you created on the <button> element in the HTML in the previous section.

This is a little cumbersome, but the trade-off is getting to write real CSS that’s automatically locally scoped. (Don’t worry, you can have global CSS too).

React

In React, you can add styles to your component multiple ways, and specifically, inline styles are used most commonly for dynamic styling.

To use inline styles in your JSX, you can create an object with your styles, and then assign this object to the style attribute on your element, the latter of which you already did in the previous section.

Inside your Button() function, before your return statement, copy and paste the following code to create your styles object:

const styles = {
  backgroundColor: color,
  color: '#ffffff'
}

Test the apps

Save and close all your files. If your project servers aren’t still running, restart them now (npm run dev for Svelte, npm start for React). Then, visit localhost:5000 in one browser tab, and localhost:3000 in a second browser tab.

Once you have each of the apps running, click the button and see the heading update its count as well as the button color change.

Svelte

Gif showing svelte app

React

gif showing react app

Svelte and React seem to have a few differences in their native styling for margins, padding, and buttons, but all in all, you built two identical apps in two different frameworks. What did you think? How did it go?

Conclusion

This was a really fun exploration of Svelte. So far, the capabilities of Svelte and React seem similar. Svelte’s templating language is very intriguing, particularly the on: directive, and I won’t lie, I miss writing templated HTML. I’m definitely going to build more with Svelte, and I look forward to diving into more advanced concepts like lifecycle methods and data binding, which right now is a pain in React.

Let me know how you liked working with Svelte on Twitter!

Ashley is a JavaScript Editor for the Twilio blog. To work with her and bring your technical stories to Twilio, find her at @ahl389 on Twitter. If you can’t find her there, she’s probably on a patio somewhere having a cup of coffee (or glass of wine, depending on the time).