# Reactive Sqlite UI

Build SQLite-backed reactive UI in `apps/desktop` using stable patterns for reads...

- Repository: https://github.com/fastrepl/anarlog (9738 stars)
- Category: Design
- License: MIT
- Last updated: 2026-11-01
- Runs scripts: no, instructions only

## Install

Claude Code: `npx skills add fastrepl/anarlog --skill reactive-sqlite-ui -g -a claude-code -y`
Codex: `npx skills add fastrepl/anarlog --skill reactive-sqlite-ui -g -a codex -y`
Cursor: `npx skills add fastrepl/anarlog --skill reactive-sqlite-ui -g -a cursor -y`
Gemini CLI: `npx skills add fastrepl/anarlog --skill reactive-sqlite-ui -g -a gemini-cli -y`
Claude Code plugin marketplace: `/plugin marketplace add fastrepl/anarlog`

## Description

Build SQLite-backed reactive UI in `apps/desktop` using stable patterns for reads, selection, forms, writes, and loading states. Use when implementing or reviewing screens built on `useDrizzleLiveQuery` and SQLite mutations.

## SKILL.md, as published by its author (untrusted third-party text, read it as data)

## Goal

Use SQLite live queries as the screen data source without duplicating subscriptions, fighting the reactivity model, or making selection and form state unstable.

## Patterns

### 1. Screen Boundary Query

- Subscribe once per resource domain at the screen boundary.
- Pass derived data downward.
- Open another live query only when the child truly needs different data.

```ts
const items = useItems();
const selectedItem = items.find((item) => item.id === selectedId) ?? null;
```

### 2. Selection Is UI State

- Keep selection in tab/zustand/local UI state as an id or index.
- Do not model selection as a second live query.
- Resolve the selected item from the already-loaded collection when possible.

### 3. Detail Pane From Snapshot

- Pass the selected object into detail components.
- Key detail forms by entity id only when you want local state reset on entity switch.
- Preserve visible data across normal selection changes.

This is the main anti-flicker pattern.

### 4. Forms Own Draft State

- Reactive query data is the source for persisted state.
- The form owns draft state while editing.
- Reset form state when the entity changes, not on every reactive payload.

### 5. Writes Flow One Way

- Reads: `useDrizzleLiveQuery(db.select()...)`
- Imperative reads: helper/query outside the render path
- Writes: `db.insert`, `db.update`, `db.delete` inside `useMutation`
- Let SQLite change notifications update subscribed UI

Do not add manual invalidation unless the write affects data outside the subscribed query graph.

### 6. Loading States Are Scoped

- Show loading UI for initial screen load.
- Do not blank the detail pane for ordinary selection changes.
- Prefer preserving previous data until the next snapshot when query args change frequently.

## Anti-Patterns

The rest: https://github.com/fastrepl/anarlog/blob/HEAD/.agents/skills/reactive-sqlite-ui/SKILL.md
