Svelte for React Developers
1. Component State
In react, we have the useState
hook, while in Svelte any value with let
is reactive.
React
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
count is {count}
</button>
);
}
export default Counter;
Svelte
<script>
let count = 0;
</script>
<button on:click={() => count++}>
count is {count}
</button>
2. Props
React
Passing props looks nearly identical in both frameworks:
<ColorPicker color={"red"}>
<p>Some child elements</p>
</ColorPicker>
In React, props are created passed via function arguments. In Svelte, props are created with the export
keyword.
function ColoredBox({ color }) {
return (
<p>You picked the color: {color}</p>
);
}
Svelte
<script>
export let color;
</script>
You picked the color: {color}
3. Children
React
function Navbar(props) {
return (
<nav>
{props.children}
</nav>
);
}
Svelte
<slot />
4. Initialization Logic
React
function Navbar() {
useEffect(() => {
const fun = async () => {
// handle promise
}
fun();
}, []);
}
Svelte
<script>
onMount(async () => {
// handle promise
});
</script>
5. Side-effects & Computed State
React
import { useEffect, useMemo, useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
const doubled = useMemo(() => count * 2, [count]);
useEffect(() => {
document.title = `count is ${count}`;
}, [count]);
}
Svelte
<script>
let count;
$: doubled = count * 2
$: document.title = `count is ${count}`
</script>
6. Forms
React
function FormComponent() {
const [firstName, setFirstName] = useState("jeff");
const handleChange = (e) => {
setFirstName(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
};
return (
<>
<h1>{firstName}</h1>
<form onSubmit={handleSubmit}>
<input type="text" onChange={handleChange} />
</form>
</>
);
}
Svelte
<script>
let firstName = "jeff";
function handleSubmit(e) {}
</script>
<h1>{firstName}</h1>
<form on:submit|preventDefault={handleSubmit}>
<input bind:value={firstName} />
</form>
7. Conditional Logic
React
{doubled > 1000 ? (
<p>big</p>
) : doubled > 500 ? (
<p>medium</p>
) : (
<p>small</p>
)}
Svelte
{#if doubled > 1000}
<p>big</p>
{:else if doubled > 500}
<p>medium</p>
{:else}
<p>small</p>
{/if}
8. Keyed Loops
React
{items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
Svelte
{#each items as item (item.id)}
<p>{item.name}</p>
{/each}
9. Shared State
React does not have a built-in mechanism for shared state, but libraries like Jotai provide a nice primitive. Svelte provides a built-in store API that is very similar to Observables in RxJS.
React
import { atom } from 'jotai'
export const countAtom = atom(0)
import { useAtom } from "jotai";
import { countAtom } from "./atom";
function Counter() {
const [count, setCount] = useAtom(countAtom) // useAtomValue to optimize re-renders
return (
<>
<button onClick={() => setCount(c => c + 1)}>
count is {count}
</button>
</>
);
}
Svelte
import { writable } from "svelte/store";
export const countStore = writable(0);
<script>
import { countStore } from "./store";
</script>
<button on:click={() => countStore.update(c => c + 1)}>
count is {$countStore}
</button>
10. Unwrapping Promises
In the future, React will have a use
hook that can unwrap promises, but it’s currently experimental.
React
function ComponentWithAsyncData() {
const number = use( Promise.resolve(69) );
return (
<p>{number}</p>
);
}
function App() {
return (
<ErrorBoundary fallback={<ErrorPage />}>
<Suspense fallback={<LoadingSpinner />}>
<ComponentWithAsyncData />
</Suspense>
</ErrorBoundary>
)
}
Svelte
<script>
const promise = Promise.resolve(69);
</script>
{#await promise}
<LoadingSpinner />
{:then number}
<p>The number is {number}</p>
{:catch error}
<ErrorPage />
{/await}
11. SSR
In Next.js, SSR and data fetching can be accomplished with React Server Components, while SvelteKit uses a dedicated load
function.
Next.js
export default async function Users() {
const item = await prisma.items.findOne();
return (
<p>{item.title}</p>
);
}
SvelteKit
export const load = (async () => {
const item = await prisma.items.findOne();
return {
item
};
}) satisfies PageLoad;
<script lang="ts">
import type { PageData } from "./$types";
export let data: PageData;
</script>
<p>{data.item.title}</p>