Skip to content
Back to skills

Solidjs Patterns

ASecurity

SolidJS reactivity + UI state patterns for Openrind Desktop

  • 3 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 26, 2026
developmentreactapi

Works with

  • api

Security analysis

A100/100

Scanned September 26, 2026

npx -y skills add openrind/openrind-shell --skill solidjs-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Solidjs Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Solidjs Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/openrind-solidjs-patterns/badge)](https://www.skillsdirectory.com/skills/openrind-solidjs-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: solidjs-patterns
description: SolidJS reactivity + UI state patterns for Openrind Desktop
---

## Why this skill exists

Openrind Desktop’s UI is SolidJS: it updates via **signals**, not React-style rerenders.
Most “UI stuck” bugs are actually **state coupling** bugs (e.g. one global `busy()` disabling an unrelated action), not rerender issues.

This skill captures the patterns we want to consistently use in Openrind Desktop.

## Core rules

- Prefer **fine-grained signals** over shared global flags.
- Keep async actions **scoped** (each action gets its own `pending` state).
- Derive UI state via `createMemo()` instead of duplicating booleans.
- Avoid mutating arrays/objects stored in signals; always create new values.

## Scoped async actions (recommended)

When an operation can overlap with others (permissions, installs, background refresh), don’t reuse a global `busy()`.

Use a dedicated signal per action:

```ts
const [replying, setReplying] = createSignal(false);

async function respond() {
  if (replying()) return;
  setReplying(true);
  try {
    await doTheThing();
  } finally {
    setReplying(false);
  }
}
```

### Why

A single `busy()` boolean creates deadlocks:

- Long-running task sets `busy(true)`
- A permission prompt appears and its buttons are disabled by `busy()`
- The task can’t continue until permission is answered
- The user can’t answer because buttons are disabled

Fix: permission UI must be disabled only by a **permission-specific** pending state.

## Signal snapshots in async handlers

If you read signals inside an async function and you need stable values, snapshot early:

```ts
const request = activePermission();
if (!request) return;
const requestID = request.id;

await respondPermission(requestID, "always");
```

## Derived UI state

Prefer `createMemo()` for computed disabled states:

```ts
const canSend = createMemo(() => prompt().trim().length > 0 && !busy());
```

## Lists

- Use setter callbacks for derived updates:

```ts
setItems((current) => current.filter((x) => x.id !== id));
```

- Don’t mutate `current` in-place.

## Practical checklist (SolidJS UI changes)

- Does any button depend on a global flag that could be true during long-running work?
- Could two async actions overlap and fight over one boolean?
- Is any UI state duplicated (can be derived instead)?
- Do event handlers read signals after an `await` where values might have changed?
- If you refactor props/types, did you update all intermediate component signatures and call sites?

## References

- SolidJS: https://www.solidjs.com/docs/latest
- SolidJS signals: https://www.solidjs.com/docs/latest/api#createsignal
- SolidJS memos: https://www.solidjs.com/docs/latest/api#creatememo

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…