Svelte for React Developers

1. Component State

In react, we have the useState hook, while in Svelte any value with let is reactive.

React

App.js
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

App.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.

App.js

function ColoredBox({ color }) {
  return (
     <p>You picked the color: {color}</p>
  );
}

Svelte

App.svelte
<script>
  export let color;
</script>

You picked the color: {color}

3. Children

React

App.js
function Navbar(props) {
  return (
     <nav>
        {props.children}
     </nav>
  );
}

Svelte

App.svelte
<slot />

4. Initialization Logic

React

App.js
function Navbar() {
    useEffect(() => {
        const fun = async () => {
            // handle promise
        }
        fun();
    }, []);
}

Svelte

App.svelte
<script>
  onMount(async () => {
    // handle promise
  });
</script>

5. Side-effects & Computed State

React

App.js
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

App.svelte
<script>
  let count;

  $: doubled = count * 2 
  $: document.title = `count is ${count}`
</script>

6. Forms

React

App.js
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

App.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

App.js
{doubled > 1000 ? (
    <p>big</p>
    ) : doubled > 500 ? (
    <p>medium</p>
    ) : (
    <p>small</p>
)}

Svelte

App.svelte
{#if doubled > 1000}
  <p>big</p>
{:else if doubled > 500}
  <p>medium</p>
{:else}
  <p>small</p>
{/if}

8. Keyed Loops

React

App.js
{items.map((item) => (
    <div key={item.id}>{item.name}</div>
))}

Svelte

App.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

file_type_js atom.js
import { atom } from 'jotai'

export const countAtom = atom(0)
App.js
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

file_type_js store.js
import { writable } from "svelte/store";

export const countStore = writable(0);
App.svelte
<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

App.js
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

App.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

page.js
export default async function Users() {

  const item = await prisma.items.findOne();

  return (
    <p>{item.title}</p>
  );
}

SvelteKit

file_type_js +page.ts
export const load = (async () => {

      const item = await prisma.items.findOne();

      return {
        item
      };

}) satisfies PageLoad;
+page.svelte
<script lang="ts">
    import type { PageData } from "./$types";
    export let data: PageData;
</script>

<p>{data.item.title}</p>

Questions? Let's chat

Open Discord