For years, React has dominated the industry as the go-to framework of choice. However, there's a new player in town: Svelte. It's gaining adoption from major players and has the potential to be the next big thing. So, are you ready to make the switch?
Back in 2020, I tried Svelte with Sapper and Routify, the first Svelte frameworks at the time. But it still had some catching up to do compared to the React world of Gatsby and Next.js. However, fast forward to 2022 and enter SvelteKit – a completely different story! SvelteKit has borrowed the best ideas from Next.js and other frameworks like Remix. But is it worth redefining your stack?
Is SvelteKit as good as Next.js?
SvelteKit is to Svelte what Next.js is to React. It will help you organize your app in folders, routes, and modules, and has all the necessary components to launch your app in production. If you ever worked with Next.js, you should have no issue to adopt SvelteKit.
Excited about React Server Components?
Welcome SvelteKit Form actions: you probably already know about it as those ideas exist on Remix or Next.js Server-Side Components. With SvelteKit' "form actions", the idea is to use JavaScript as an enhancement, rather than it being the main entry point of your application. Imagine an app that still works with JS disabled. Just like if you were building a PHP application, you have <form>
elements on the client that are calling endpoints on the server. It’s not a “client + server API” relation anymore.
"I'm just fine with my Next.js + React-Query stack"
I used to be really happy with that stack and it was a massive improvement over what we used to do for data fetching with Redux. Now with SvelteKit you can follow the same pattern where you export a load
function. You can define whether the function should be called on the client side, on the server side, or both. It can be a bit complicated at the beginning with the filenaming conventions of SvelteKit, but it is flexible and it offers you the same level of control as Next.js.
If you have advanced data fetching requirements, the "TanStack" has released a Svelte-Query equivalent to React-Query. But with SvelteKit built-in invalidations and refetching options, you might not even need it.
Server-Side or Client-Side rendering?
You can do them all with SvelteKit as it provides fine grained control on how each page should be rendered.
It is using Vite under the hood (a faster Webpack equivalent) and you can hook into it for more advanced scenarios.
Environment variables management
Building a production website always calls for environment variables. SvelteKit handles them slightly differently. You'll need to explicitly import the variables using modules provided by SvelteKit, like so:
import { PUBLIC_BASE_URL } from '$env/static/public';
This makes things more explicit compared to Next.js. Sure, it might be a bit more complicated, but at least you'll always know in which context your env variables are available.
It took me a bit of time to understand that SvelteKit has two modes for environment variables: dynamic and static. Static variables are only available during build time, whereas dynamic variables are working at runtime. Both modes can have private (server-side only) and public variables.
Is Svelte as good as React?
With React being the leading framework for so many years, one of the main benefit is its ecosystem. But is Svelte as good and could it be following the same path?
Props and Callbacks
When it comes to React, we spend a considerable amount of time declaring component props or passing callback functions: className
, onClick
, isOpen
, ... and the list goes on.
In Svelte, it is very similar, but there are some differences that you have to know!
Forget Callback Props, Say Hello to Event Dispatching
In Svelte, you usually don’t do this:
<MyComponent onOpen={handleOpen} />
Instead, you would dispatch an event from inside your component:
<div on:click={() => dispatch("open")} />
And then:
<MyComponent on:open={handleOpen} />
I can already hear some of you React pros raising concerns about using events! After years of experience using JS frameworks (I remember Backbone.js), I learned that using events was a bad practice and they often lead to unmaintainable code. But in Svelte, these events are not your regular JavaScript events; they are custom Svelte events! They don't bubble all over the place, causing spaghetti code nightmares. They are scoped to where you use them, making them very easy to manage.
However, there is one pattern that you might be used to in React that does not work in Svelte, and there is no easy alternative:
export const MyButton = ({ onSubmit }) => {
const [isLoading, setLoading] = React.useState();
const handleClick = async () => {
setLoading(true);
onSubmit().then(() => setLoading(false));
};
return <button onClick={handleClick}>{isLoading ? 'loading...' : 'Click me!'}</button>;
};
In this example, we rely on the callback prop onSubmit
to determine when the submission is complete, so we can toggle our loading state on and off. This is a really handy pattern, so you don’t have to pass down an extra prop isSubmitted
to the MyButton
component. Well, in Svelte, you cannot use events as promises, so you will have to do another way!
Reactivity, Hooks and Side Effects
As React developers, we've all gone through the process of mastering reactivity. When is a hook called? On mount? On the server-side? Will it trigger a re-render? It's a skill we've honed over the years. Now, brace yourself, because with Svelte...you'll probably be asking those very same questions! 🙃
Let's take an example of a React component that will scroll down the page when the user receives a new message, like in a chat app:
const Chat = ({ messages }) => {
useEffect(() => {
window.scrollTo({
top: document.documentElement.scrollHeight
});
}, [messages])
return <div>{messages}</div>
};
Here is the Svelte equivalent:
<script>
import { onMount, afterUpdate } from 'svelte';
let messages;
$: if (messages) {
window.scrollTo({
top: document.documentElement.scrollHeight
})
}
</script>
<div>{messages}</div>
So as you can see in this example the Svelte equivalent to React.useEffect here is $:
followed by the "dependencies array" of the React hook.
Reactivity in Svelte simplifies things significantly. It reintroduces two essential lifecycle functions: onMount and onDestroy. These functions disappeared during the Hooks era (remember componentDidMount and componentWillUnmount in React?).
Overall reactivity with Svelte is great and you will understand it pretty fast as a React developer.
However, there are some quirks you'll inevitably encounter along the way!
Let's look at an example of an alert modal with a default title:
AlertModal.svelte
<script>
export let title = 'Something went wrong!';
</script>
+page.svelte
<script>
let newMessageTitle = 'You received a new message!';
onMount() {
setTimeout(() => {
newMessageTitle = undefined;
}, 2000)
}
</script>
<AlertModal title={newMessageTitle} />
What do you think should be the title of the alert after 2 seconds?
-
undefined
? -
Something went wrong!
? -
You received a new message!
?
I let you discover the answer on Svelte REPL.
State Management
In React, there are many tools for state management, like Redux, Mobx, React Context API, Zustand, and more.
In Svelte, there is also a Context API, but even if it has the same name, it is different from the React one (Svelte Context API is not reactive by default).
For state management in Svelte, you would use Stores. Stores are pretty much a kind of React.useState, but they are global to your application. You can import them and read them from anywhere. Unlike the useState hook, you can read it outside of a React component as well.
I haven’t gone too far in scaling state management in Svelte, but for small/medium side applications, Stores are working fine without a 3rd party solution!
Classname Management
In React, you are probably used to clsx or classnames. With Svelte, you most likely won't need those, as you can do the following:
<div class="some-class {isOpen ? 'text-red' : 'text-black'}" />
Just keep in mind that doing the following could produce class='some-class undefined'
, so use a ternary operator instead!
<div class="some-class {isOpen && 'text-red'}" />
Animations
In React, it has always been a pain to do animations when components appear or disappear from the DOM. You probably remember things like React Transition Group, which was a real struggle. Fortunately now we have Framer Motion (definitely the ultimate solution for animations in React) and this problem is pretty much gone.
For Svelte, you will be delighted to know that it includes some great animation tools right into its core!
No more struggle to make a Modal component fade in and out when appearing:
<script>
import { cubicOut } from "svelte/easing";
import { fade } from "svelte/transition";
</script>
<div transition:fade={{ easing: cubicOut, duration: 300 }}>
<slot />
</div>
For more insights on animations with Svelte, check out my other article: Instagram-like page transitions with Svelte
Overall Svelte is not revolutionizing the game but it greatly improves productivity by having the answers to most applications problems.
It brings back the feeling of developing traditional server-side rendered apps (like a PHP app, but with without the drawbacks).
It's quickly gaining popularity among companies, and you'll see it more and more in the market. Especially companies that were still using Angular until now and decided to skip the React wagon entirely. Major players are realizing its potential and using it to build powerful apps. This shows that Svelte is reliable and becoming a top choice for frontend development.
Bonus: improve your VSCode experience on SvelteKit project
You might have this setting already set even while using React but this is especially useful for SvelteKit: by default VSCode only shows the filename in the opened tabs. However in SvelteKit you will often have files with the same name. This can become really difficult to follow the context of the file once you start switching between tabs.
One trick to solve this is to set the following setting in your VSCode preferences:
"workbench.editor.labelFormat": "short"
This will not only show the filename for the tab but also its parent folder, giving you the context you needed!
Can you think of other things you need to be aware of as a React developer starting with Svelte? Let me know in the comments!
Other articles on this topic: https://css-tricks.com/svelte-for-the-experienced-react-dev/
Credits: cover image edited from "Portal to another dimension" by Deni Dedic on Midjourney.
Top comments (1)
I am super on board with Svelte. I honestly think Svelte has already put all other frameworks in their graves, they just haven't noticed. Well, I may exaggerate, but Svelte is so nice it sure feels like this.