install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/valtio" ~/.claude/skills/comeonoliver-skillshub-valtio && rm -rf "$T"
manifest:
skills/TerminalSkills/skills/valtio/SKILL.mdsource content
Valtio
Overview
Valtio makes React state management feel like plain JavaScript — mutate objects directly and React re-renders automatically. No reducers, no actions, no selectors. Wrap an object in
proxy(), mutate it anywhere, and components that read the changed properties re-render. Based on JavaScript Proxy, it tracks which properties each component uses and only re-renders when those specific properties change.
When to Use
- Want the simplest possible state management
- Tired of Redux boilerplate or Zustand's
functionset() - Sharing state between components without prop drilling
- State that's accessed/modified outside React (event handlers, WebSocket callbacks)
- Team prefers mutable patterns over immutable
Instructions
Setup
npm install valtio
Basic Store
// store/app.ts — Define state as a plain object import { proxy, useSnapshot } from "valtio"; export const appState = proxy({ user: null as { name: string; email: string } | null, theme: "light" as "light" | "dark", notifications: [] as Array<{ id: string; text: string; read: boolean }>, sidebar: { open: true, width: 280 }, }); // Mutate directly — React components auto-update export function login(user: { name: string; email: string }) { appState.user = user; } export function toggleTheme() { appState.theme = appState.theme === "light" ? "dark" : "light"; } export function addNotification(text: string) { appState.notifications.push({ id: crypto.randomUUID(), text, read: false }); } export function markAllRead() { appState.notifications.forEach((n) => { n.read = true; }); } export function toggleSidebar() { appState.sidebar.open = !appState.sidebar.open; }
Use in Components
// components/Header.tsx — Read state with useSnapshot import { useSnapshot } from "valtio"; import { appState, toggleTheme, toggleSidebar } from "@/store/app"; export function Header() { // useSnapshot creates a read-only snapshot // Component ONLY re-renders when `user` or `theme` changes // Changes to `notifications` or `sidebar` don't trigger re-render here const snap = useSnapshot(appState); return ( <header className="flex items-center justify-between p-4"> <button onClick={toggleSidebar}>☰</button> <span>{snap.user?.name ?? "Guest"}</span> <button onClick={toggleTheme}> {snap.theme === "light" ? "🌙" : "☀️"} </button> </header> ); }
// components/NotificationBell.tsx — Derived/computed values import { useSnapshot } from "valtio"; import { appState, markAllRead } from "@/store/app"; export function NotificationBell() { const snap = useSnapshot(appState); const unread = snap.notifications.filter((n) => !n.read).length; return ( <button onClick={markAllRead} className="relative"> 🔔 {unread > 0 && ( <span className="absolute -top-1 -right-1 bg-red-500 text-white text-xs rounded-full w-5 h-5 flex items-center justify-center"> {unread} </span> )} </button> ); }
Computed Values with derive
// store/derived.ts — Computed values that auto-update import { derive } from "valtio/utils"; import { appState } from "./app"; export const derived = derive({ unreadCount: (get) => get(appState).notifications.filter((n) => !n.read).length, isDarkMode: (get) => get(appState).theme === "dark", isLoggedIn: (get) => get(appState).user !== null, });
Subscribe Outside React
// Listen to state changes outside components import { subscribe } from "valtio"; import { appState } from "./store/app"; // Log every state change subscribe(appState, () => { console.log("State changed:", JSON.stringify(appState)); }); // Subscribe to specific property subscribe(appState.sidebar, () => { localStorage.setItem("sidebar-open", String(appState.sidebar.open)); }); // Use in WebSocket handler socket.on("notification", (data) => { appState.notifications.push(data); // Components auto-update });
Examples
Example 1: Build a shopping cart
User prompt: "Build a shopping cart with add/remove/update quantity using simple state management."
The agent will create a Valtio proxy for cart state, actions that directly mutate the array, and components that reactively display cart contents and total.
Example 2: Theme and layout preferences
User prompt: "Store user preferences (theme, language, sidebar state) that persist across page loads."
The agent will create a Valtio store with localStorage sync via subscribe, and components that read preferences reactively.
Guidelines
for state,proxy()
for reading — always use snapshot in componentsuseSnapshot()- Mutate directly —
works; nostate.count++
orsetState
neededset() - Automatic render optimization — only re-renders when accessed properties change
for side effects — persist to localStorage, log, syncsubscribe()
for computed values — auto-recalculates when dependencies changederive()- Works outside React — mutate from event handlers, WebSocket, timers
- Snapshot is read-only — don't mutate
, mutate the originalsnapproxy - Arrays work naturally —
,push
,splice
all trigger re-rendersfilter - Nested objects tracked —
triggers re-renderstate.user.name = "new" - Devtools —
for Redux DevTools integrationimport { devtools } from "valtio/utils"