KS
Killer-Skills

signal-state-management — how to use signal-state-management how to use signal-state-management, what is signal-state-management, signal-state-management vs react state, signal-state-management install, signal-state-management setup guide, reactive ui updates with signal-state-management, turso and supabase auth integration, signal-state-management alternative, signal-state-management and next.js

v1.0.0
GitHub

About this Skill

Ideal for Frontend Agents requiring advanced state management and reactive UI updates in Turso, Next.js, and Supabase Auth applications. signal-state-management is a technique for managing mutable state and computed values in client-side applications using signals.

Features

Manages client-side application state using simple signals
Creates reactive UI updates with computed signals
Batches state updates for performance optimization
Organizes state by domain using boards, posts, and members
Integrates signals with React components for efficient rendering
Optimizes re-renders using Turso and Supabase Auth

# Core Topics

DW225 DW225
[0]
[0]
Updated: 3/7/2026

Quality Score

Top 5%
51
Excellent
Based on code quality & docs
Installation
SYS Universal Install (Auto-Detect)
Cursor IDE Windsurf IDE VS Code IDE
> npx killer-skills add DW225/ree-board/signal-state-management

Agent Capability Analysis

The signal-state-management MCP Server by DW225 is an open-source Categories.community integration for Claude and other AI agents, enabling seamless task automation and capability expansion. Optimized for how to use signal-state-management, what is signal-state-management, signal-state-management vs react state.

Ideal Agent Persona

Ideal for Frontend Agents requiring advanced state management and reactive UI updates in Turso, Next.js, and Supabase Auth applications.

Core Value

Empowers agents to manage client-side application state, create reactive UI updates, and optimize re-renders using signals and computed values with libraries like @preact/signal, leveraging protocols such as reactive state updates and batching for performance.

Capabilities Granted for signal-state-management MCP Server

Implementing computed values for dynamic UI updates
Batching state updates for improved application performance
Organizing state by domain for enhanced data management

! Prerequisites & Limits

  • Requires knowledge of client-side state management
  • Specific to Turso, Next.js, and Supabase Auth applications
  • Dependent on @preact/signal library
Project
SKILL.md
10.4 KB
.cursorrules
1.2 KB
package.json
240 B
Ready
UTF-8

# Tags

[No tags]
SKILL.md
Readonly

Signal State Management

When to Use This Skill

Activate this skill when working on:

  • Managing client-side application state
  • Creating reactive UI updates
  • Implementing computed values
  • Batching state updates for performance
  • Organizing state by domain (boards, posts, members)
  • Integrating signals with React components
  • Optimizing re-renders

Core Patterns

Signal vs Computed Signal

Simple Signal: Holds mutable state

typescript
1import { signal } from "@preact/signals-react"; 2 3// ✅ Simple signal for primitive values 4export const currentBoardId = signal<string | null>(null); 5 6// ✅ Simple signal for complex state 7export const postsSignal = signal<Post[]>([]);

Computed Signal: Derives value from other signals

typescript
1import { signal, computed } from "@preact/signals-react"; 2 3export const postsSignal = signal<Post[]>([]); 4export const filterSignal = signal<PostFilter>("all"); 5 6// ✅ Computed signal automatically updates 7export const filteredPosts = computed(() => { 8 const posts = postsSignal.value; 9 const filter = filterSignal.value; 10 11 if (filter === "all") return posts; 12 return posts.filter((p) => p.type === filter); 13});

State Organization by Domain

Separate Files for Each Domain:

typescript
1// lib/signal/postSignals.ts 2import { signal, computed } from "@preact/signals-react"; 3 4// State 5export const postsSignal = signal<Post[]>([]); 6export const selectedPostId = signal<string | null>(null); 7 8// Computed values 9export const selectedPost = computed(() => { 10 const id = selectedPostId.value; 11 if (!id) return null; 12 return postsSignal.value.find((p) => p.id === id); 13}); 14 15export const postsByType = computed(() => { 16 const posts = postsSignal.value; 17 return { 18 wentWell: posts.filter((p) => p.type === "went_well"), 19 toImprove: posts.filter((p) => p.type === "to_improve"), 20 actionItems: posts.filter((p) => p.type === "action_items"), 21 }; 22}); 23 24// Actions 25export const addPost = (post: Post) => { 26 postsSignal.value = [...postsSignal.value, post]; 27}; 28 29export const updatePost = (id: string, updates: Partial<Post>) => { 30 postsSignal.value = postsSignal.value.map((p) => 31 p.id === id ? { ...p, ...updates } : p 32 ); 33}; 34 35export const deletePost = (id: string) => { 36 postsSignal.value = postsSignal.value.filter((p) => p.id !== id); 37};

Action Creator Pattern

Encapsulate State Updates:

typescript
1// lib/signal/boardSignals.ts 2import { signal } from "@preact/signals-react"; 3 4export const boardsSignal = signal<Board[]>([]); 5export const loadingSignal = signal<boolean>(false); 6export const errorSignal = signal<string | null>(null); 7 8// ✅ Action creators for complex operations 9export const loadBoards = async () => { 10 loadingSignal.value = true; 11 errorSignal.value = null; 12 13 try { 14 const boards = await fetchBoards(); 15 boardsSignal.value = boards; 16 } catch (error) { 17 errorSignal.value = "Failed to load boards"; 18 console.error(error); 19 } finally { 20 loadingSignal.value = false; 21 } 22}; 23 24export const createBoard = async (name: string) => { 25 try { 26 const newBoard = await createBoardAction(name); 27 // Optimistic update 28 boardsSignal.value = [...boardsSignal.value, newBoard]; 29 return newBoard; 30 } catch (error) { 31 errorSignal.value = "Failed to create board"; 32 throw error; 33 } 34};

Batch Updates for Performance

Update Multiple Signals Together:

typescript
1import { batch } from "@preact/signals-react"; 2 3// ❌ Bad: Triggers 3 re-renders 4const updateBoard = (id: string, data: BoardUpdate) => { 5 boardsSignal.value = updateBoardList(id, data); 6 selectedBoardId.value = id; 7 lastUpdatedSignal.value = Date.now(); 8}; 9 10// ✅ Good: Triggers 1 re-render 11const updateBoard = (id: string, data: BoardUpdate) => { 12 batch(() => { 13 boardsSignal.value = updateBoardList(id, data); 14 selectedBoardId.value = id; 15 lastUpdatedSignal.value = Date.now(); 16 }); 17};

Integration with React Components

Reading Signals:

typescript
1"use client"; 2 3import { postsSignal, filteredPosts } from "@/lib/signal/postSignals"; 4 5export function PostList() { 6 // ✅ Component re-renders when signal changes 7 const posts = filteredPosts.value; 8 9 return ( 10 <div> 11 {posts.map((post) => ( 12 <PostCard key={post.id} post={post} /> 13 ))} 14 </div> 15 ); 16}

Updating Signals:

typescript
1"use client"; 2 3import { updatePost } from "@/lib/signal/postSignals"; 4 5export function EditPostForm({ postId }: { postId: string }) { 6 const handleSubmit = (content: string) => { 7 // ✅ Update signal 8 updatePost(postId, { content }); 9 10 // Persist to database 11 updatePostAction(postId, content); 12 }; 13 14 return <form onSubmit={handleSubmit}>...</form>; 15}

Combining Signals with Server Actions

Pattern: Update signal first (optimistic), then persist

typescript
1"use client"; 2 3import { addPost, deletePost } from "@/lib/signal/postSignals"; 4import { createPost as createPostAction } from "@/lib/actions/post/createPost"; 5 6export function CreatePostButton({ boardId }: { boardId: string }) { 7 const handleCreate = async () => { 8 // Create temporary post for optimistic UI 9 const tempPost: Post = { 10 id: `temp-${Date.now()}`, 11 boardId, 12 content: "", 13 type: "went_well", 14 createdAt: new Date(), 15 }; 16 17 // ✅ Optimistic update 18 addPost(tempPost); 19 20 try { 21 // Persist to database 22 const savedPost = await createPostAction(boardId, "", "went_well"); 23 24 // Replace temp with real post 25 deletePost(tempPost.id); 26 addPost(savedPost); 27 } catch (error) { 28 // Rollback on error 29 deletePost(tempPost.id); 30 showError("Failed to create post"); 31 } 32 }; 33 34 return <button onClick={handleCreate}>Create Post</button>; 35}

Signal Performance Patterns

Avoid Unnecessary Signal Subscriptions:

typescript
1// ❌ Bad: Creates new computed signal on every render 2function PostCount() { 3 const count = computed(() => postsSignal.value.length); 4 return <div>{count.value}</div>; 5} 6 7// ✅ Good: Computed signal defined once outside component 8const postCount = computed(() => postsSignal.value.length); 9 10function PostCount() { 11 return <div>{postCount.value}</div>; 12}

Use Signal Peeking for Non-Reactive Reads:

typescript
1import { postsSignal } from "@/lib/signal/postSignals"; 2 3function logCurrentPosts() { 4 // ✅ Read without subscribing (doesn't trigger re-render) 5 console.log("Current posts:", postsSignal.peek()); 6}

Anti-Patterns

❌ Mutating Signal Values Directly

Bad:

typescript
1// ❌ Never mutate signal values directly 2postsSignal.value.push(newPost);

Good:

typescript
1// ✅ Create new array 2postsSignal.value = [...postsSignal.value, newPost];

❌ Creating Signals Inside Components

Bad:

typescript
1function MyComponent() { 2 // ❌ Creates new signal on every render 3 const localSignal = signal(0); 4 return <div>{localSignal.value}</div>; 5}

Good:

typescript
1// ✅ Define signals outside components 2const counterSignal = signal(0); 3 4function MyComponent() { 5 return <div>{counterSignal.value}</div>; 6}

❌ Not Using Batch for Multiple Updates

Bad:

typescript
1// ❌ Triggers 3 re-renders 2const resetFilters = () => { 3 filterSignal.value = "all"; 4 sortSignal.value = "date"; 5 searchSignal.value = ""; 6};

Good:

typescript
1// ✅ Triggers 1 re-render 2import { batch } from "@preact/signals-react"; 3 4const resetFilters = () => { 5 batch(() => { 6 filterSignal.value = "all"; 7 sortSignal.value = "date"; 8 searchSignal.value = ""; 9 }); 10};

❌ Forgetting .value Accessor

Bad:

typescript
1// ❌ Comparing signal object, not value 2if (currentBoardId === "board-123") { 3 // This will never be true 4}

Good:

typescript
1// ✅ Access signal value 2if (currentBoardId.value === "board-123") { 3 // Correct comparison 4}

Integration with Other Skills

Project-Specific Context

Key Files

  • lib/signal/boardSignals.ts - Board listing and management
  • lib/signal/postSignals.ts - Post state within boards
  • lib/signal/memberSignals.ts - Board member management
  • components/board/PostProvider.tsx - Signal updates from real-time

Domain-Specific Signals

Board Management:

typescript
1// lib/signal/boardSignals.ts 2export const boardsSignal = signal<Board[]>([]); 3export const currentBoardId = signal<string | null>(null); 4export const currentBoard = computed(() => 5 boardsSignal.value.find((b) => b.id === currentBoardId.value) 6);

Post Management:

typescript
1// lib/signal/postSignals.ts 2export const postsSignal = signal<Post[]>([]); 3export const postFilter = signal<PostType | "all">("all"); 4export const filteredPosts = computed(() => { 5 const filter = postFilter.value; 6 if (filter === "all") return postsSignal.value; 7 return postsSignal.value.filter((p) => p.type === filter); 8});

Member Management:

typescript
1// lib/signal/memberSignals.ts 2export const membersSignal = signal<Member[]>([]); 3export const currentUserRole = computed(() => { 4 const members = membersSignal.value; 5 const userId = currentUserId.value; 6 return members.find((m) => m.userId === userId)?.role || "guest"; 7});

Real-Time Integration

Update Signals from Ably Messages:

typescript
1// components/board/PostProvider.tsx 2useChannel(`board:${boardId}`, (message) => { 3 switch (message.name) { 4 case "post:create": 5 addPost(message.data); 6 break; 7 8 case "post:update": 9 updatePost(message.data.id, message.data); 10 break; 11 12 case "post:delete": 13 deletePost(message.data.id); 14 break; 15 } 16});

Testing Signals

Reset Signals in beforeEach:

typescript
1import { postsSignal, filterSignal } from "@/lib/signal/postSignals"; 2 3beforeEach(() => { 4 postsSignal.value = []; 5 filterSignal.value = "all"; 6}); 7 8test("filters posts by type", () => { 9 postsSignal.value = [ 10 { id: "1", type: "went_well", content: "Test" }, 11 { id: "2", type: "to_improve", content: "Test" }, 12 ]; 13 14 filterSignal.value = "went_well"; 15 expect(filteredPosts.value).toHaveLength(1); 16});

Last Updated: 2026-01-10

Related Skills

Looking for an alternative to signal-state-management or building a Categories.community AI Agent? Explore these related open-source MCP Servers.

View All

widget-generator

Logo of f
f

widget-generator is an open-source AI agent skill for creating widget plugins that are injected into prompt feeds on prompts.chat. It supports two rendering modes: standard prompt widgets using default PromptCard styling and custom render widgets built as full React components.

149.6k
0
Design

chat-sdk

Logo of lobehub
lobehub

chat-sdk is a unified TypeScript SDK for building chat bots across multiple platforms, providing a single interface for deploying bot logic.

73.0k
0
Communication

zustand

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication

data-fetching

Logo of lobehub
lobehub

The ultimate space for work and life — to find, build, and collaborate with agent teammates that grow with you. We are taking agent harness to the next level — enabling multi-agent collaboration, effortless agent team design, and introducing agents as the unit of work interaction.

72.8k
0
Communication