Install
$ agentstack add skill-impertio-studio-solidjs-claude-skill-package-solid-impl-state-patterns ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
solid-impl-state-patterns
Quick Reference
State Primitives Overview
| Primitive | Import | Use Case | Access Syntax | |-----------|--------|----------|---------------| | createSignal | solid-js | Single values, primitives, toggles | signal() (getter function) | | createStore | solid-js/store | Nested objects, arrays, complex state | store.prop (direct access) | | createContext | solid-js | Shared state across component tree | Provider + useContext | | createMemo | solid-js | Derived/computed values | memo() (getter function) | | createMutable | solid-js/store | MobX/Vue migration only | state.prop (direct mutation) |
Critical Warnings
NEVER use createEffect to synchronize derived state — ALWAYS use createMemo instead. Effects are for side effects (DOM, network, logging), not for computing values from other signals.
NEVER store signal values in variables outside tracking scopes — the variable captures a snapshot, not a reactive binding. ALWAYS call the getter function where you need the current value.
NEVER destructure props or stores — destructuring reads values once and breaks reactive tracking. ALWAYS access properties through the proxy object.
NEVER use Redux, useReducer, or Zustand patterns — SolidJS has built-in primitives (signals, stores, context) that are more efficient and idiomatic.
NEVER use global mutable variables for shared state — ALWAYS use createContext + createStore for type-safe, reactive, scoped state sharing.
State Selection Decision Tree
Need state? → What kind of data?
│
├─ Single primitive value (string, number, boolean)
│ → createSignal
│
├─ Object with nested properties / array of items
│ → createStore
│
├─ Derived from other state (computed value)
│ → createMemo (NEVER createEffect + createSignal)
│
├─ Shared across multiple components
│ │
│ ├─ Parent → children (1-2 levels deep)
│ │ → Pass as props (direct)
│ │
│ └─ Across distant components / app-wide
│ → createContext + createStore + Provider
│
├─ Async data from server/API
│ → createResource (SolidJS 1.x) or createAsync (with @solidjs/router)
│
└─ External reactive source (RxJS, custom)
→ from() utility
When to Use Signals vs Stores
| Scenario | Use Signal | Use Store | |----------|-----------|-----------| | Counter, toggle, input value | YES | No | | User profile object | No | YES | | List of items (todos, products) | No | YES | | Theme (single string) | YES | No | | Form with multiple fields | No | YES | | Nested config object | No | YES | | Loading/error boolean | YES | No |
Global State Pattern: Context + Store
The canonical SolidJS pattern for shared state combines createContext, createStore, and a Provider component with a custom hook.
import { createContext, useContext, ParentProps } from "solid-js";
import { createStore, SetStoreFunction } from "solid-js/store";
// 1. Define state shape
interface AppState {
user: { name: string; email: string } | null;
theme: "light" | "dark";
notifications: { id: string; message: string }[];
}
// 2. Define context type (state + actions)
type AppContextValue = [
state: AppState,
actions: {
setUser: (user: AppState["user"]) => void;
toggleTheme: () => void;
addNotification: (message: string) => void;
removeNotification: (id: string) => void;
}
];
// 3. Create context (no default value — enforce Provider usage)
const AppContext = createContext();
// 4. Provider component with store + actions
export function AppProvider(props: ParentProps) {
const [state, setState] = createStore({
user: null,
theme: "light",
notifications: [],
});
const actions = {
setUser: (user: AppState["user"]) => setState("user", user),
toggleTheme: () =>
setState("theme", (prev) => (prev === "light" ? "dark" : "light")),
addNotification: (message: string) =>
setState("notifications", (prev) => [
...prev,
{ id: crypto.randomUUID(), message },
]),
removeNotification: (id: string) =>
setState("notifications", (prev) => prev.filter((n) => n.id !== id)),
};
return (
{props.children}
);
}
// 5. Custom hook with safety check
export function useApp() {
const context = useContext(AppContext);
if (!context) throw new Error("useApp must be used within AppProvider");
return context;
}
Usage in components:
function Header() {
const [state, { toggleTheme }] = useApp();
return (
{state.user?.name ?? "Guest"}
Theme: {state.theme}
);
}
Derived State with createMemo
ALWAYS use createMemo for values computed from other reactive state. Memos cache results and only recompute when dependencies change.
import { createSignal, createMemo } from "solid-js";
const [items, setItems] = createSignal([]);
const [taxRate, setTaxRate] = createSignal(0.21);
// CORRECT: Derived values via createMemo
const subtotal = createMemo(() =>
items().reduce((sum, item) => sum + item.price * item.qty, 0)
);
const tax = createMemo(() => subtotal() * taxRate());
const total = createMemo(() => subtotal() + tax());
// Memos chain: items/taxRate → subtotal → tax → total
// Changing taxRate recalculates tax and total, but NOT subtotal
Derived State Anti-Pattern
// WRONG: Using createEffect to sync derived state
const [count, setCount] = createSignal(0);
const [double, setDouble] = createSignal(0);
createEffect(() => {
setDouble(count() * 2); // Unnecessary signal + effect cycle
});
// CORRECT: createMemo — no extra signal, no effect
const double = createMemo(() => count() * 2);
Form State Pattern
Store-Based Form (Recommended for Multi-Field Forms)
import { createStore } from "solid-js/store";
import { createSignal, Show } from "solid-js";
interface FormData {
name: string;
email: string;
role: "admin" | "user" | "viewer";
}
interface FormErrors {
name?: string;
email?: string;
}
function UserForm() {
const [form, setForm] = createStore({
name: "",
email: "",
role: "user",
});
const [errors, setErrors] = createStore({});
const [submitting, setSubmitting] = createSignal(false);
const validate = (): boolean => {
const newErrors: FormErrors = {};
if (!form.name.trim()) newErrors.name = "Name is required";
if (!form.email.includes("@")) newErrors.email = "Invalid email";
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = async (e: SubmitEvent) => {
e.preventDefault();
if (!validate()) return;
setSubmitting(true);
try {
await saveUser(form);
} finally {
setSubmitting(false);
}
};
return (
setForm("name", e.currentTarget.value)}
/>
{errors.name}
setForm("email", e.currentTarget.value)}
/>
{errors.email}
setForm("role", e.currentTarget.value as FormData["role"])}
>
Admin
User
Viewer
{submitting() ? "Saving..." : "Save"}
);
}
State Composition Strategies
splitProps for Prop Separation
import { splitProps } from "solid-js";
function Button(props: {
variant: "primary" | "secondary";
size: "sm" | "md" | "lg";
onClick: () => void;
children: any;
class?: string;
disabled?: boolean;
}) {
const [local, others] = splitProps(props, ["variant", "size", "children"]);
return (
{local.children}
);
}
Multiple Contexts (Feature Slicing)
ALWAYS create separate contexts for unrelated state domains. This prevents unnecessary re-computations.
// auth-context.tsx
export function AuthProvider(props: ParentProps) { /* ... */ }
export function useAuth() { /* ... */ }
// theme-context.tsx
export function ThemeProvider(props: ParentProps) { /* ... */ }
export function useTheme() { /* ... */ }
// Compose at app root
function App() {
return (
);
}
External State Integration with from()
import { from } from "solid-js";
// Bridge RxJS observable into SolidJS signal
const currentUser = from(userService.currentUser$);
// Bridge custom subscription
const windowWidth = from((set) => {
const handler = () => set(window.innerWidth);
window.addEventListener("resize", handler);
handler(); // Set initial value
return () => window.removeEventListener("resize", handler);
});
reconcile() for External Data Sync
import { createStore, reconcile } from "solid-js/store";
const [state, setState] = createStore({ todos: [] as Todo[] });
// Sync external data — only changed properties trigger updates
websocket.on("todos-updated", (newTodos: Todo[]) => {
setState("todos", reconcile(newTodos));
});
Reference Links
- [references/methods.md](references/methods.md) -- Pattern signatures for context+store, derived state, form state patterns
- [references/examples.md](references/examples.md) -- Working code examples: global state, derived state, form management, counter/theme providers
- [references/anti-patterns.md](references/anti-patterns.md) -- Redux patterns, prop drilling, global mutable variables, unnecessary state
Official Sources
- https://docs.solidjs.com/concepts/signals
- https://docs.solidjs.com/concepts/stores
- https://docs.solidjs.com/concepts/context
- https://docs.solidjs.com/reference/basic-reactivity/create-signal
- https://docs.solidjs.com/reference/basic-reactivity/create-memo
- https://docs.solidjs.com/reference/store-utilities/create-store
- https://docs.solidjs.com/reference/reactive-utilities/from
- https://docs.solidjs.com/reference/store-utilities/reconcile
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/SolidJS-Claude-Skill-Package
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.