Signals, effects, memos, context, and async resources in the Krate runtime.
#Reactivity: Signals, Effects & Memos
Krate's reactivity system is inspired by SolidJS: fine-grained, signal-based, with no virtual DOM and no diffing.
All primitives are exported from @krate/runtime.
#Signals
import { createSignal } from '@krate/runtime';
const [count, setCount] = createSignal(0);count()reads the current value.setCount(next)sets the value and notifies subscribers.setCount(prev => ...)performs a functional update.
Signals are read inside components and effects; the compiler and runtime track which effects depend on which signals automatically.
#Effects
import { createEffect } from '@krate/runtime';
const dispose = createEffect(() => {
console.log(`count is ${count()}`);
});
dispose(); // remove the effect
An effect re-runs whenever any signal it reads changes. The tracker works via a context stack, so nested reads inside createEffect are captured correctly.
#Memos
import { createMemo } from '@krate/runtime';
const doubled = createMemo(() => count() * 2);
doubled(); // memoized until count changes
A memo caches its computed value and only recomputes when its dependencies change. Only the final consumer re-renders — the memo itself is cached.
#Cleanup & mount
import { onCleanup, onMount } from '@krate/runtime';
createEffect(() => {
const timer = setInterval(() => {}, 1000);
onCleanup(() => clearInterval(timer)); // runs before re-run and on dispose
});
onMount(() => {
// runs once after the initial synchronous render completes
});#Context
import { createContext } from '@krate/runtime';
const ThemeCtx = createContext('light');
// ThemeCtx.Provider — wraps children with a value
// ThemeCtx.useContext() — reads the nearest provider value
// ThemeCtx.defaultValue — fallback when no provider is present
#Resources
import { createResource } from '@krate/runtime';
const [user, { mutate, refetch }] = createResource(
() => userId(), // reactive source
async (id) => fetch(`/api/user/${id}`).then(r => r.json()), // fetcher
);
user() // current data (or undefined)
user.loading // boolean
user.error // error or undefined
user.state // 'unresolved' | 'loading' | 'ready' | 'error' | 'refreshing'
mutate(prev => ({ ...prev, name: 'new' })) // optimistic update
refetch() // trigger a re-fetch
Resources re-fetch automatically when their source signal changes and abort in-flight requests when the source changes mid-flight.
#Conditional rendering (showIf)
For conditionally rendering an element, Krate supports a showIf prop — sugar for the {expr && <el/>} guard:
const [count] = createSignal(0);
// These are equivalent:
{count() > 0 && <p>Positive</p>}
<p showIf={count() > 0}>Positive</p>- Works on intrinsic elements (
<div>) and components (<List>). visibleIfis accepted as an alias.- A bare attribute (
<p showIf>) is always true. - The attribute is compiler-erased: it never appears in the emitted HTML and is never passed to a component's props.
- A statically-known test folds at build time (the losing branch is not emitted); a reactive test becomes part of the hydration bundle and toggles the element's visibility as the signal changes.
#CSS Signals
Interactive state can be expressed with zero client JavaScript using CSS Signals (createCSSChoice, createCSSToggle, createCSSFlags, createCSSGroup, createCSSRange, createCSSStack) � compiled to hidden inputs plus :has() CSS. They are described in full in their own guide.
- CSS Signals guide � concepts, both authoring styles, composition, roles, browser support, and limits.
- CSS Signals API reference.
- Cookbook and errors.
#Compile-time validation
The reactive dependency graph is validated at build time and surfaced as ⚠ warnings. The compiler detects:
- Unused signals — declared but never read or written.
- Write-only effects — effects that call setters but read no signals.
- No-read effects — effects that read and write nothing (run exactly once).
- Circular dependencies — including self-referential feedback loops like
setCount(count() + 1).
These warnings help you catch bugs before they reach the browser.
Last updated: 2026-10-16
Contributors: Noinkin