<what>
title: "Step 5 — Sessions & State"
active_step: 5
</what>
<h1 class="text-3xl font-bold mb-2" style="color: #111827; letter-spacing: -0.02em;">Step 5 — Sessions & State</h1>
<p class="text-gray-500 text-sm mb-8">Server-side state that persists, mutated from HTML attributes.</p>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">How it works</h2>
<p class="text-gray-600 mb-4" style="line-height: 1.7;">
Sessions are stored server-side in SQLite. Every browser gets a unique session. The <code>w-set</code> attribute on any HTML element mutates session data when clicked or changed. No JavaScript. No API routes.
</p>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><button w-set="session.counter += 1">+1</button>
<span>#session.counter#</span></code></pre>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Interactive counter</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
This counter is stored in your session. Reload the page — the value persists.
</p>
<div class="card mb-4">
<div class="card-body">
<div class="flex items-center gap-4 mb-2">
<div class="text-4xl font-bold" style="color: #6366f1; min-width: 3rem; text-align: center;">#session.tut_counter|default:"0"#</div>
<div class="flex gap-2">
<button class="btn btn-primary" w-set="session.tut_counter += 1">+1</button>
<button class="btn btn-outline" w-set="session.tut_counter -= 1">-1</button>
<button class="btn btn-outline" w-set="session.tut_counter = 0">Reset</button>
</div>
</div>
<p class="text-sm text-gray-500">Reload the page. The number will still be there.</p>
</div>
</div>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><div>#session.tut_counter|default:"0"#</div>
<button w-set="session.tut_counter += 1">+1</button>
<button w-set="session.tut_counter -= 1">-1</button>
<button w-set="session.tut_counter = 0">Reset</button></code></pre>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Toggle demo</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
Booleans toggle with the <code>toggle</code> operator.
</p>
<div class="card mb-4">
<div class="card-body">
<div class="flex items-center gap-4">
<button class="btn btn-outline" w-set="session.tut_toggle = toggle">
<if session.tut_toggle>ON</if>
<unless session.tut_toggle>OFF</unless>
</button>
<span class="text-sm text-gray-500">
Status: <strong>
<if session.tut_toggle>active</if>
<unless session.tut_toggle>inactive</unless>
</strong>
</span>
</div>
</div>
</div>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code><button w-set="session.tut_toggle = toggle">
<if session.tut_toggle>ON</if>
<unless session.tut_toggle>OFF</unless>
</button></code></pre>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Mutation patterns</h2>
<div class="card">
<div class="card-body">
<table style="width: 100%; border-collapse: collapse; font-size: 0.875rem;">
<thead>
<tr style="border-bottom: 1px solid #e5e7eb;">
<th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Pattern</th>
<th style="text-align: left; padding: 0.5rem 0.75rem; color: #6b7280; font-weight: 600;">Effect</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.5rem 0.75rem;"><code>session.n += 1</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Increment by 1</td>
</tr>
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.5rem 0.75rem;"><code>session.n -= 5</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Decrement by 5</td>
</tr>
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.5rem 0.75rem;"><code>session.x = "hello"</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Set a string value</td>
</tr>
<tr style="border-bottom: 1px solid #f3f4f6;">
<td style="padding: 0.5rem 0.75rem;"><code>session.flag = toggle</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Toggle boolean</td>
</tr>
<tr>
<td style="padding: 0.5rem 0.75rem;"><code>session.x = $value</code></td>
<td style="padding: 0.5rem 0.75rem; color: #6b7280;">Set from input field value</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<section class="mb-8">
<h2 class="text-lg font-semibold mb-3" style="color: #111827;">Application-wide state</h2>
<p class="text-gray-600 mb-3" style="line-height: 1.7;">
Use <code>app.*</code> instead of <code>session.*</code> for state shared across all users. Use <code>wired.*</code> for real-time state pushed to all connected browsers via WebSocket.
</p>
<pre class="bg-gray-50 p-4 rounded text-sm font-mono" style="border: 1px solid #e5e7eb; overflow-x: auto;"><code>session.* <!-- Per-user, persists across reloads -->
app.* <!-- Shared across all users -->
wired.* <!-- Shared + real-time WebSocket push --></code></pre>
</section>
<div class="card" style="border-color: #bbf7d0; background: #f0fdf4;">
<div class="card-body">
<div class="text-sm font-semibold mb-2" style="color: #14532d;">What you learned</div>
<ul class="text-sm space-y-1" style="color: #166534; padding-left: 1.25rem; list-style: disc;">
<li><code>w-set</code> on any element mutates server state when clicked or changed</li>
<li><code>session.*</code> is per-user and persists across page reloads</li>
<li>Mutations: <code>+= 1</code>, <code>-= N</code>, <code>= "value"</code>, <code>= $value</code>, <code>= toggle</code></li>
<li>Multiple mutations: <code>w-set="session.a += 1; session.b = 'ok'"</code></li>
<li><code>app.*</code> is shared across all users; <code>wired.*</code> pushes real-time via WebSocket</li>
</ul>
</div>
</div>