Svelte and SvelteKit course ยท Module 3: Reactivity and Runes
$state - Reactive State
In this lesson5
$state is the most powerful rune - it creates reactive state that automatically updates the UI on changes.
Basic Usage
<script>
// Number
let gold = $state(0);
// String
let vikingName = $state('Ragnar');
// Boolean
let isReady = $state(false);
// Array
let warriors = $state(['Bjorn', 'Ivar']);
// Object
let ship = $state({
name: 'Drakkar',
crew: 50,
cargo: []
});
</script>Updating State
<script>
let count = $state(0);
let items = $state(['a', 'b']);
let user = $state({ name: 'Viking', level: 1 });
function examples() {
// Numbers - like regular assignment
count += 1;
count = count * 2;
// Arrays - mutations work!
items.push('c'); // Works in Svelte 5!
items[0] = 'x'; // Works!
items = [...items, 'd']; // Also works
// Objects - mutations work!
user.level += 1; // Works!
user.name = 'Jarl'; // Works!
}
</script>Deep Reactivity
$state creates a deeply reactive object:
<script>
let kingdom = $state({
name: 'Nordheim',
villages: [
{ name: 'Svenheim', population: 500 },
{ name: 'Bjornvik', population: 300 }
]
});
function growVillage() {
// Nested mutation - works automatically!
kingdom.villages[0].population += 100;
}
</script>
<p>{kingdom.villages[0].name}: {kingdom.villages[0].population}</p>
<button onclick={growVillage}>Grow village</button>$state with Classes
<script>
class Warrior {
name = $state('');
health = $state(100);
weapon = $state('Sword');
constructor(name) {
this.name = name;
}
takeDamage(amount) {
this.health -= amount;
}
}
let hero = new Warrior('Ragnar');
</script>
<p>{hero.name}: {hero.health} HP</p>
<button onclick={() => hero.takeDamage(10)}>
Deal damage
</button>$state.raw - Without Deep Reactivity
Sometimes you do not need deep reactivity:
<script>
// Only the reference is reactive, not the contents
let battleLog = $state.raw([
{ year: 793, place: 'Lindisfarne' },
{ year: 845, place: 'Paris' }
]);
// battleLog.push(...) will NOT refresh the view - assign a new array
function addBattle() {
battleLog = [...battleLog, { year: 866, place: 'York' }];
}
</script>
<p>Battles in the chronicle: {battleLog.length}</p>
<button onclick={addBattle}>Add a battle</button>Use $state.raw for:
- Large datasets
- Read-only data
- Performance optimization
Code for this lesson: App.svelte
<script>
// $state - deep reactivity
let warrior = $state({
name: 'Ragnar',
stats: {
health: 100,
strength: 95,
energy: 80
},
inventory: ['Axe', 'Shield']
});
function takeDamage() {
// Nested mutations work!
warrior.stats.health -= 10;
}
function heal() {
warrior.stats.health = Math.min(100, warrior.stats.health + 20);
}
function addItem(item) {
warrior.inventory.push(item);
}
</script>
<main class="warrior-panel">
<h1>{warrior.name}</h1>
<section class="stats">
<h2>Stats</h2>
<div class="stat">
<span class="stat-label">Health</span>
<div class="bar">
<div class="fill health" style="width: {warrior.stats.health}%"></div>
</div>
<span>{warrior.stats.health}/100</span>
</div>
<div class="stat">
<span class="stat-label">Strength</span>
<div class="bar">
<div class="fill strength" style="width: {warrior.stats.strength}%"></div>
</div>
<span>{warrior.stats.strength}/100</span>
</div>
<div class="actions">
<button onclick={takeDamage} class="damage">Take damage</button>
<button onclick={heal} class="heal">Heal</button>
</div>
</section>
<section class="inventory">
<h2>Inventory ({warrior.inventory.length})</h2>
<ul>
{#each warrior.inventory as item}
<li>{item}</li>
{/each}
</ul>
<button onclick={() => addItem('Sword')}>+ Add Sword</button>
</section>
</main>
<style>
.warrior-panel {
font-family: 'Georgia', serif;
max-width: 500px;
margin: 0 auto;
padding: 2rem;
background: #1a0f0a;
color: #e8d5b7;
min-height: 100vh;
}
h1 {
color: #d4af37;
text-align: center;
}
h2 {
color: #d4af37;
font-size: 1rem;
margin-bottom: 1rem;
}
section {
background: rgba(212, 175, 55, 0.1);
border: 1px solid #d4af37;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
}
.stat {
margin-bottom: 0.75rem;
}
.stat .stat-label {
display: block;
margin-bottom: 0.25rem;
}
.bar {
height: 20px;
background: rgba(0,0,0,0.5);
border-radius: 4px;
overflow: hidden;
}
.fill {
height: 100%;
transition: width 0.3s;
}
.fill.health { background: linear-gradient(90deg, #8b0000, #ff4444); }
.fill.strength { background: linear-gradient(90deg, #d4af37, #ffd700); }
.actions {
display: flex;
gap: 0.5rem;
margin-top: 1rem;
}
button {
flex: 1;
padding: 0.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
color: white;
}
.damage { background: #8b0000; }
.heal { background: #2d5016; }
ul {
list-style: none;
padding: 0;
}
li {
padding: 0.5rem;
background: rgba(0,0,0,0.3);
margin: 0.25rem 0;
border-radius: 4px;
}
</style>Docs entries used in this lesson
Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. How do you create reactive state in Svelte 5?
2. What is true about $state in Svelte 5?
Hands-on tasks in the game
- Code editor
Complete the code by creating a reactive array of warriors