Universal Reactivity: Runes Everywhere

+15 Mana ✨

The killer feature of Runes is universal reactivity — Runes work anywhere, not just in .svelte files.

The .svelte.js Extension

To use Runes in JavaScript files, use the .svelte.js (or .svelte.ts) extension:

js
// counter.svelte.js
let count = $state(0);

export function increment() {
  count += 1;
}

export function getCount() {
  return count;
}

Now import and use it anywhere:

svelte
<!-- App.svelte -->
<script>
  import { increment, getCount } from './counter.svelte.js';
</script>

<button onclick={increment}>
  Count: {getCount()}
</button>

Why This Matters

1. Reusable State Logic

js
// useCounter.svelte.js
export function createCounter(initial = 0) {
  let count = $state(initial);
  
  return {
    get value() { return count; },
    increment: () => count++,
    decrement: () => count--,
    reset: () => count = initial
  };
}

2. Shared Global State

js
// auth.svelte.js
export const user = $state(null);
export const isLoggedIn = $derived(user !== null);

export async function login(email, password) {
  const response = await fetch('/api/login', { ... });
  user = await response.json();
}

export function logout() {
  user = null;
}

3. Complex Business Logic

js
// cart.svelte.js
let items = $state([]);

export const cart = {
  get items() { return items; },
  get total() { return items.reduce((sum, item) => sum + item.price * item.qty, 0); },
  get count() { return items.reduce((sum, item) => sum + item.qty, 0); },
  
  add(product) {
    const existing = items.find(i => i.id === product.id);
    if (existing) {
      existing.qty += 1;
    } else {
      items.push({ ...product, qty: 1 });
    }
  },
  
  remove(productId) {
    items = items.filter(i => i.id !== productId);
  }
};

The Power of Getters

Notice the get keyword above. When you return reactive state from a function, use getters to maintain reactivity:

js
// ❌ Wrong: loses reactivity
function createCounter() {
  let count = $state(0);
  return {
    count,           // Captured value, not reactive!
    increment: () => count++
  };
}

// ✅ Correct: maintains reactivity
function createCounter() {
  let count = $state(0);
  return {
    get count() { return count; },  // Getter preserves reactivity
    increment: () => count++
  };
}
✓ Completed