React Context in Svelte? Let's Hack State Management Like a Pro!

If you’re coming from React and love Context API + useReducer, Svelte 5’s new Runes make similar patterns possible - but simpler. Let’s explore both basic and advanced implementations.

Here’s how common React patterns translate to Svelte 5’s new reactivity system using runes:


1. Basic Context Implementation

React Approach

// ThemeContext.jsx
import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext();

export function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => setTheme(prev =>
    prev === 'light' ? 'dark' : 'light'
  );

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  return useContext(ThemeContext);
}

Svelte 5 Approach

<!-- ThemeContext.svelte -->
<script>
  import { setContext } from 'svelte';

  const ThemeSymbol = Symbol();
  let theme = $state('light');

  setContext(ThemeSymbol, {
    theme,
    toggleTheme: () => theme = theme === 'light' ? 'dark' : 'light'
  });
</script>

<slot />

2. Reducer Pattern Implementation

React (useReducer)

// TodoReducer.jsx
import { useReducer, createContext } from 'react';

const TodoContext = createContext();

const todoReducer = (state, action) => {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, action.payload]
      };
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? {...todo, completed: !todo.completed}
            : todo
        )
      };
    default:
      return state;
  }
};

export function TodoProvider({ children }) {
  const [state, dispatch] = useReducer(todoReducer, {
    todos: [],
    filter: 'all'
  });

  return (
    <TodoContext.Provider value={{ ...state, dispatch }}>
      {children}
    </TodoContext.Provider>
  );
}

Svelte 5 (Runes Pattern)

<!-- TodoStore.svelte -->
<script>
  import { setContext } from 'svelte';

  // Reactive state
  const todos = $state([]);
  const filter = $state('all');

  // Reducer-like actions
  const dispatch = (action) => {
    switch (action.type) {
      case 'ADD_TODO':
        todos = [...todos, {
          id: crypto.randomUUID(),
          text: action.payload,
          completed: false
        }];
        break;
      case 'TOGGLE_TODO':
        todos = todos.map(todo =>
          todo.id === action.payload
            ? {...todo, completed: !todo.completed}
            : todo
        );
        break;
    }
  };

  // Expose context
  setContext('TodoContext', { todos, filter, dispatch });
</script>

<slot />

Key Differences Table

FeatureReactSvelte 5
Boilerplate~30 lines per context~15 lines per context
ReactivityManual re-rendersAutomatic via $state
Provider ComponentsRequiredOptional
Type SafetyPropTypes/TypeScriptBuilt-in TypeScript
State UpdatesImmutable requiredDirect mutation allowed
Bundle Size (approx)+40KB (runtime)0KB runtime

Pro Tip: TypeScript Implementation

React + TypeScript:

interface Todo {
  id: string;
  text: string;
  completed: boolean;
}

type TodoAction =
  | { type: 'ADD_TODO'; payload: string }
  | { type: 'TOGGLE_TODO'; payload: string };

Svelte 5 + TypeScript:

<script lang="ts">
  import { setContext } from 'svelte';

  type Todo = { /* same as above */ };
  type TodoAction = { /* same as above */ };

  // Annotate state directly
  const todos = $state<Todo[]>([]);
  const dispatch = $state<(action: TodoAction) => void>();
</script>

Alhamdulillah, Comparison Complete! 🎉

Why Choose Svelte 5?

  1. 60% less code for same functionality
  2. Direct DOM updates (no virtual DOM diffing)
  3. Gradual adoption (mix with other state solutions)

When to Use React Context:

  1. Existing React codebases
  2. Large teams familiar with Flux pattern
  3. Need error boundaries (Svelte equivalent coming in 5.1)

FAQ: Q: Can I use Redux with Svelte? A: Yes, but try native stores first - you might not need it!

Q: How to handle async actions? A: Same as React - use async/await in your dispatch functions


Saw a mistake? Edit on GitHub | Built with ❤️ in Indonesia

#svelte#state-management#frontend