RE:DOM is a library for manipulating the DOM, and Svelte is a framework with routing and transitions. In this post, we’ll review their differences and establish which would be a better choice for different kinds of applications.
RE:DOM and Svelte both let us create components to render content.
In Svelte, we create components in individual files. For example, we can write:
//App.svelte <script> import Button from "./Button.svelte"; </script> <style> main { font-family: sans-serif; text-align: center; } </style> <main> <h1>Hello</h1> <Button /> </main>
//Button.svelte <script> let count = 0; function handleClick() { count += 1; } </script> <style> button { background: #ff3e00; color: white; border: none; padding: 8px 12px; border-radius: 2px; } </style> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button>
We created the Button
component in Button.svelte
, which we include in the App.svelte
component file.
The script
tag has the logic, and style
has the styles for each component. The remainder of the file has the HTML content we want to render.
We embed JavaScript expressions in braces, and it has its own syntax for attaching event handlers. The on:click
attribute lets us attach a click handler.
Functions that are in the script
tag can be referenced in the HTML template, as we did with the handleClick
function.
Any variables declared in the script
tag can referenced and rendered in the template, as we have in the button
element.
Data binding is done automatically, so no need to do that ourselves.
With RE:DOM, we install the redom
package by running:
npm i redom
Then we can create a simple component by writing:
//index.js import { el, mount } from "redom"; const hello = el("h1", "Hello world"); mount(document.body, hello);
This assumes we’ll use a module bundler like Parcel to create our project. We create our component with the el
function and pass in the tag and the HTML we want to show. Then, we call mount
to mount the hello
component to the body.
We can create a text component with the text
function:
import { text, mount } from "redom"; const hello = text("hello"); mount(document.body, hello); hello.textContent = "hi!";
We pass in the initial textContent
into the text
function, and when we reassign its value, it’ll automatically be updated.
If we want to add child elements, we write:
import { el, setChildren } from "redom"; const a = el("h1", "foo"); const b = el("h2", "bar"); const c = el("h3", "baz"); setChildren(document.body, [a, b, c]);
Then we add the elements in the same order as in the array.
We can also create RE:DOM components with classes. This lets us set lifecycle methods to run code in various stages of the component lifecycle. For example, we can write:
import { el, mount } from "redom"; class Hello { constructor() { this.el = el("h1", "Hello world"); } onmount() { console.log("mounted Hello"); } onremount() { console.log("remounted Hello"); } onunmount() { console.log("unmounted Hello"); } } class App { constructor() { this.el = el("app", (this.hello = new Hello())); } onmount() { console.log("mounted App"); } onremount() { console.log("remounted App"); } onunmount() { console.log("unmounted App"); } } const app = new App(); mount(document.body, app);
Above, we have a Hello
component that’s nested inside the App
component. To do the nesting, we create a new instance of it and pass it as the second argument into el
.
In general, Svelte has many more options for creating components since it’s meant to be an app development framework. RE:DOM is much more limited since it should be used as a DOM manipulation library.
In Svelte, we can update components in various ways.
We update the variables in the script
tag, and the new value will automatically be rendered. For example:
//Button.svelte <script> let count = 0; function handleClick() { count += 1; } </script> <style> button { background: #ff3e00; color: white; border: none; padding: 8px 12px; border-radius: 2px; } </style> <button on:click={handleClick}> Clicked {count} {count === 1 ? 'time' : 'times'} </button>
If we click the button, then the handleClick
function is run, which increases count
by one. count
‘s latest value will then be rendered in the template. We just display it, and we also use it to render something else, as we can see from the expression.
We can also add style bindings with Svelte. For example, we can write:
<script> let size = 42; let text = "edit me"; </script> <input type=range bind:value={size}> <div> <span style="font-size: {size}px">{text}</span> </div>
We have a range slider, which binds to the size
variable. So, when we change the slider value, the size
value will change with it.
And in the span
element, we use the size
value to change the font-size
style. Therefore, the slider will change the size of the edit me text.
It also comes with a special syntax for setting classes.
For example, we can write:
<script> let active = true; </script> <style> .active { background-color: red; } </style> <div> <div class="{active ? 'active' : ''}" on:click="{() => active = !active}" > toggle </div> </div>
We toggle the active
class on and off when we click the button by toggling the active
variable between true
and false
.
We can also write something like this to do the same thing:
<script> let active = true; </script> <style> .active { background-color: red; } </style> <div> <div class:active='{active}' on:click="{() => active = !active}" > toggle </div> </div>
We have an expression that determines whether the active
class is applied with the class:active
attribute.
With RE:DOM, updating components is more limited. We can set attributes of an element or set the style. For example, we can write something like this to create an h1
element and set its color to red:
import { el, setAttr, mount } from "redom"; const hello = el("h1", "Hello world!"); setAttr(hello, { style: { color: "red" }, className: "hello" }); mount(document.body, hello);
The setAttr
function takes two arguments: the first is the element we created, and the second is an object, with the keys being the attribute names and the values being the attribute values.
RE:DOM also comes with a setStyle
method to let us set the styles of an element. For instance, we can use it by writing:
import { el, setStyle, mount } from "redom"; const hello = el("h1", "Hello world!"); setStyle(hello, { color: "green" }); mount(document.body, hello);
This sets the h1
element to green.
These are the main ways to update components.
Svelte is a full app development framework, while RE:DOM is a library for DOM manipulation. They both make manipulating the DOM easy.
Svelte lets us write code expressively and eliminate boilerplate. To quickly create a Svelte project, we have to clone the Svelte template repo, which is not terribly convenient.
On the other hand, with RE:DOM, we just install the redom
package with npm i redom
.
We can slso use the script from https://redom.js.org/redom.min.js to add it to our app. This means we don’t need build tools to add RE:DOM to our app project.
Both tools support the latest JavaScript syntax. Since RE:DOM is a library, it doesn’t have a CLI program to let us create apps. On the other hand, there’s no CLI program to create a Svelte project from scratch and build it, which isn’t very convenient.
According to many tests, RE:DOM is faster than Svelte. From AJ Meyghani’s benchmarks, we can see that Svelte is slower than RE:DOM in many respects. This plays out similarly in more recent benchmarks as well.
However, the differences amount to only a few milliseconds, which may not be very noticeable in most cases. It’s worth reiterating that RE:DOM is a lightweight library, while Svelte is a full app development framework, so this makes sense.
RE:DOM comes with a router for mapping URLs to components to render, while Svelte doesn’t come with its own router.
To add routing to our Svelte app, we need to install the svelte-routing
package by running:
npm i svelte-routing
Then, we write:
//routes/About.svelte <p>about</p>
//routes/Blog.svelte <p>blog</p>
//routes/BlogPost.svelte <script> export let id; </script> <p>blog post {id}</p>
//routes/Home.svelte <p>home</p>
//App.svelte <script> import { Router, Link, Route } from "svelte-routing"; import Home from "./routes/Home.svelte"; import About from "./routes/About.svelte"; import Blog from "./routes/Blog.svelte"; import BlogPost from "./routes/BlogPost.svelte"; export let url = ""; </script> <Router url="{url}"> <nav> <Link to="/">Home</Link> <Link to="about">About</Link> <Link to="blog">Blog</Link> <Link to="blog/1">Blog Post</Link> </nav> <div> <Route path="blog/:id" let:params> <BlogPost id="{params.id}" /> </Route> <Route path="blog" component="{Blog}" /> <Route path="about" component="{About}" /> <Route path="/"><Home /></Route> </div> </Router>
We add the Router
component to add the router, and Route
adds the routes.
We pass in URL parameters with the let:params
attribute and the id
prop to accept the id
URL parameter.
:id
is the URL parameter placeholder. If there are any parameters, we can get them as props, as we did in BlogPost.svelte
. If we have the export
keyword in front of the variable declaration, then it’s a prop.
To use RE:DOM’s router, we can write:
//index.js import { el, router, mount } from "redom"; class Home { constructor() { this.el = el("h1"); } update(data) { this.el.textContent = `Hello ${data}`; } } class About { constructor() { this.el = el("about"); } update(data) { this.el.textContent = `About ${data}`; } } class Contact { constructor() { this.el = el("contact"); } update(data) { this.el.textContent = `Contact ${data}`; } } const app = router(".app", { home: Home, about: About, contact: Contact }); mount(document.body, app); const data = "world"; app.update("home", data); app.update("about", data); app.update("contact", data);
We create the routes by using the router
function.
The keys in the object are the URLs for the routes. app.update
goes to the route we want. We pass data
into the routes, which we get from the update
method’s parameter.
Generally speaking, RE:DOM’s router is definitely less flexible than Svelte’s.
RE:DOM is a lightweight DOM manipulation library. Even though we can create components with it, it’s not very expressive for the purpose of creating complex components. As such, we should stick with just manipulating the DOM with it.
Svelte, on the other hand, is a full app framework as long as we add the svelte-routing
package to it for routing.
There’s no doubt that frontends are getting more complex. As you add new JavaScript libraries and other dependencies to your app, you’ll need more visibility to ensure your users don’t run into unknown issues.
LogRocket is a frontend application monitoring solution that lets you replay JavaScript errors as if they happened in your own browser so you can react to bugs more effectively.
LogRocket works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred. LogRocket also monitors your app’s performance, reporting metrics like client CPU load, client memory usage, and more.
Build confidently — start monitoring for free.
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]