Provides reactive UI helpers for rendering lists and conditional components in a performant way.
Install
mkdir -p .claude/skills/control-flow-yw662 && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/16055" && unzip -o skill.zip -d .claude/skills/control-flow-yw662 && rm skill.zipInstalls to .claude/skills/control-flow-yw662
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Reactive ranges for lists and conditionals — `For` (lists), `Show` (visibility), `When` (binary branches), `Switch` / `Match` (multi-way). Load this when the task is about rendering a list of items, conditionally showing/hiding elements, or pattern-matching on a value to render different branches.Key capabilities
- →Render lists from signals
- →Toggle element visibility based on a signal
- →Implement two-branch conditionals
- →Implement multi-way matching with cached DOM
- →Nest control flow helpers
How it works
It provides reactive range helpers (For, Show, When, Switch, Match) that cache their DOM elements, updating them based on signal changes without rebuilding.
Inputs & outputs
When to use control-flow
- →Rendering lists dynamically
- →Toggling visibility of components
- →Conditional UI rendering
About this skill
Control Flow
rikka-dom ships four reactive range helpers: For for lists, Show for visibility, When for binary branches, Switch / Match for multi-way. All cache their DOM so toggling does not rebuild elements.
Imports
import { For, Show, When, Switch, Match } from "@takanashi/rikka-dom";
For(source, render, keyFn?): ReactiveRange
Render a list from a Signal.State<T[]> or Signal.Computed<T[]>. Returns a range you can mount anywhere Child is accepted.
import { signal } from "@takanashi/rikka-signal";
import { For, li } from "@takanashi/rikka-dom";
const items = signal(["a", "b", "c"]);
const list = For(items, (item) => li({}, item));
Keyed rendering
Pass a keyFn to cache DOM by key, so updates only add/remove the changed items:
const users = signal([{ id: 1, name: "Alice" }, { id: 2, name: "Bob" }]);
For(
users,
(user) => li({}, user.name),
(user) => user.id, // keyFn
);
Without a keyFn, caching is by reference (Object.is). Unused cached entries are cleaned up on each re-evaluation.
Show(condition, render): ReactiveRange
Toggle visibility based on a signal. The element is created once and shown/hidden on subsequent toggles.
import { signal } from "@takanashi/rikka-signal";
import { Show, span } from "@takanashi/rikka-dom";
const visible = signal(true);
Show(visible, () => span({}, "visible"));
// visible.set(false) → element removed from DOM
// visible.set(true) → same element re-inserted
When(condition, trueRender, falseRender): ReactiveRange
Two-branch conditional. Both branches are cached.
import { signal } from "@takanashi/rikka-signal";
import { When, span } from "@takanashi/rikka-dom";
const loggedIn = signal(false);
When(
loggedIn,
() => span({}, "Welcome!"),
() => span({}, "Please log in"),
);
Switch(value, ...cases) and Match(matcher, render)
Multi-way matching. Each case DOM is cached. The first matching case is shown.
import { signal } from "@takanashi/rikka-signal";
import { Switch, Match, textarea, div } from "@takanashi/rikka-dom";
const mode = signal("edit");
Switch(
mode,
Match("edit", () => textarea()),
Match("preview", () => div({}, "Preview")),
Match(
(v) => v.startsWith("admin"), // predicate form
() => div({}, "Admin"),
),
);
Match's matcher is either:
- A value — compared with
Object.is - A predicate function
(value: T) => boolean
The last case acts as a fallback (no matcher, or always-true predicate).
Nesting control flow
For / Show / When / Switch return a ReactiveRange, which is a valid Child. Compose freely:
For(todos, (todo) =>
Show(todo.done, () => s({}, todo.text), () => s({}, todo.text, " ✓"))
);
Pitfalls
1. Plain array instead of signal
// ❌ Static — list never updates
For(["a", "b", "c"] as any, (x) => li({}, x));
// ✅ Signal — list updates
For(signal(["a", "b", "c"]), (x) => li({}, x));
(rikka's type system catches this — source must be a Signal.State<T[]> or Signal.Computed<T[]>.)
2. Forgetting keyFn on a list with stable IDs
Without keyFn, caching is by reference. If your data is replaced wholesale (e.g. fetched fresh from an API), every item will be re-rendered. Use keyFn whenever your items have a stable identity:
// Without keyFn: full re-render on each refresh
For(users, (u) => li({}, u.name));
// With keyFn: only changed items re-render
For(users, (u) => li({}, u.name), (u) => u.id);
3. Forgetting () after () => in Match
Switch(mode,
Match("edit", () => textarea()), // ✅ render function called
Match("preview", textarea), // ❌ passes the function reference, not its result
);
Always invoke the render function with ().
4. Using Show for two branches
// ❌ Awkward — two `Show` blocks
Show(cond, () => A());
Show(computed(() => !cond.get()), () => B());
// ✅ Use `When`
When(cond, () => A(), () => B());
See also
When not to use it
- →When using a plain array instead of a signal for lists
- →When using `Show` for two branches instead of `When`
- →When forgetting `keyFn` on a list with stable IDs
Limitations
- →Forgetting `keyFn` on a list with stable IDs will cause full re-render on each refresh
- →Match render functions must be invoked with `()`
- →Using `Show` for two branches is awkward compared to `When`
How it compares
This skill offers specialized components for reactive UI rendering that optimize performance by caching DOM elements, unlike manual conditional rendering that might rebuild elements.
Compared to similar skills
control-flow side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| control-flow (this skill) | 0 | 2mo | No flags | Intermediate |
| zustand | 113 | 2mo | No flags | Intermediate |
| threejs-skills | 61 | 4mo | No flags | Intermediate |
| accessibility-compliance | 45 | 2mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
zustand
lobehub
Zustand state management guide. Use when working with store code (src/store/**), implementing actions, managing state, or creating slices. Triggers on Zustand store development, state management questions, or action implementation.
threejs-skills
sickn33
Three.js skills for creating 3D elements and interactive experiences
accessibility-compliance
wshobson
Implement WCAG 2.2 compliant interfaces with mobile accessibility, inclusive design patterns, and assistive technology support. Use when auditing accessibility, implementing ARIA patterns, building for screen readers, or ensuring inclusive user experiences.
react-modernization
wshobson
Upgrade React applications to latest versions, migrate from class components to hooks, and adopt concurrent features. Use when modernizing React codebases, migrating to React Hooks, or upgrading to latest React versions.
3d-graphics
samhvw8
3D web graphics with Three.js (WebGL/WebGPU). Capabilities: scenes, cameras, geometries, materials, lights, animations, model loading (GLTF/FBX), PBR materials, shadows, post-processing (bloom, SSAO, SSR), custom shaders, instancing, LOD, physics, VR/XR. Actions: create, build, animate, render 3D scenes/models. Keywords: Three.js, WebGL, WebGPU, 3D graphics, scene, camera, geometry, material, light, animation, GLTF, FBX, OrbitControls, PBR, shadow mapping, post-processing, bloom, SSAO, shader, instancing, LOD, WebXR, VR, AR, product configurator, data visualization, architectural walkthrough, interactive 3D, canvas. Use when: creating 3D visualizations, building WebGL/WebGPU apps, loading 3D models, adding animations, implementing VR/XR, creating interactive graphics, building product configurators.
d3-visualization
lyndonkl
Use when creating custom, interactive data visualizations with D3.js—building bar/line/scatter charts from scratch, creating network diagrams or geographic maps, binding changing data to visual elements, adding zoom/pan/brush interactions, animating chart transitions, or when chart libraries (Highcharts, Chart.js) don't support your specific visualization design and you need low-level control over data-driven DOM manipulation, scales, shapes, and layouts.