Angular
kid-sid/claude-spellbook
A skill your agent uses when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change…
How to write idiomatic, efficient RxJS code. An agent skill from sanity-io/sanity.
$ npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install sanity-io/sanity rxjs-like-a-pro --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/sanity-io/sanity.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/rxjs-like-a-pro .claude/skills/rxjs-like-a-pro && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "rxjs-like-a-pro" agent skill from https://github.com/sanity-io/sanity/tree/main/.agents/skills/rxjs-like-a-pro into .claude/skills/rxjs-like-a-pro/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rxjs-like-a-pro", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/sanity-io/sanity/tree/main/.agents/skills/rxjs-like-a-proType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install sanity-io/sanity rxjs-like-a-pro --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sanity-io/sanity.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/rxjs-like-a-pro .agents/skills/rxjs-like-a-pro && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "rxjs-like-a-pro" agent skill from https://github.com/sanity-io/sanity/tree/main/.agents/skills/rxjs-like-a-pro into .agents/skills/rxjs-like-a-pro/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rxjs-like-a-pro", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install sanity-io/sanity rxjs-like-a-pro --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sanity-io/sanity.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/rxjs-like-a-pro .cursor/skills/rxjs-like-a-pro && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "rxjs-like-a-pro" agent skill from https://github.com/sanity-io/sanity/tree/main/.agents/skills/rxjs-like-a-pro into .cursor/skills/rxjs-like-a-pro/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rxjs-like-a-pro", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/sanity-io/sanity.git --path .agents/skills/rxjs-like-a-pro--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install sanity-io/sanity rxjs-like-a-pro --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sanity-io/sanity.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/rxjs-like-a-pro .gemini/skills/rxjs-like-a-pro && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "rxjs-like-a-pro" agent skill from https://github.com/sanity-io/sanity/tree/main/.agents/skills/rxjs-like-a-pro into .gemini/skills/rxjs-like-a-pro/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rxjs-like-a-pro", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install sanity-io/sanity rxjs-like-a-proInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/sanity-io/sanity.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/rxjs-like-a-pro .github/skills/rxjs-like-a-pro && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "rxjs-like-a-pro" agent skill from https://github.com/sanity-io/sanity/tree/main/.agents/skills/rxjs-like-a-pro into .github/skills/rxjs-like-a-pro/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rxjs-like-a-pro", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install sanity-io/sanity rxjs-like-a-pro --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/sanity-io/sanity.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/rxjs-like-a-pro .opencode/skills/rxjs-like-a-pro && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "rxjs-like-a-pro" agent skill from https://github.com/sanity-io/sanity/tree/main/.agents/skills/rxjs-like-a-pro into .opencode/skills/rxjs-like-a-pro/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "rxjs-like-a-pro", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
rxjs-like-a-proHow to write idiomatic, efficient RxJS code. An agent skill from sanity-io/sanity.
Rxjs Like A Pro is an agent skill from sanity-io/sanity, published by the product's own GitHub organization. How to write idiomatic, efficient RxJS code. Use this skill whenever the user is writing, refactoring, reviewing, or debugging code that uses RxJS — including any file that imports from 'rxjs' or 'rxjs/operators'. Trigger on mentions of observables, subscriptions, RxJS operators, or reactive streams. Even if the user doesn't say "RxJS" explicitly, activate when you see patterns like .pipe(), .subscribe(), Observable, Subject, BehaviorSubject, switchMap, mergeMap, or similar.
Its SKILL.md is about 3k tokens, which your agent loads only when the skill is triggered. The skill folder holds 5 other files, including reference files (for example `references/custom-operators.md`, `references/inner-observable-chains.md` and `references/loading-state-patterns.md`).
It sits in Development, covering Refactoring. It works with RxJS. The repository describes itself as: Sanity Studio – Rapidly configure content workspaces powered by structured content. The licence is MIT.
3 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit efa15fb. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
No scripts in the folder and no shell commands in SKILL.md (its code samples are typescript).
From the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Rxjs Like A Pro loads about 3k tokens when it runs, and up to ~5.9k if it reads all its reference files. Until then it costs about 127 tokens; SKILL.md has 1,063 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from sanity-io/sanity at commit efa15fb, republished under its MIT licence (© sanity-io). 1,063 words, ~3,005 tokens.
.claude/skills/rxjs-like-a-pro/SKILL.md (or your agent's skills folder). This skill also uses 4 other files; get the full folder from GitHub.This skill helps you write RxJS code that is idiomatic, composable, and free of common pitfalls. The core
philosophy: keep logic in the observable chain. Every time you reach for .subscribe(), ask whether the
work could instead be expressed as a transformation inside .pipe().
For detailed examples and patterns, read the relevant reference file:
references/loading-state-patterns.md — Deriving loading/error state in the chain, the withLoadingState
custom operator, and using scan to preserve previous results across loading states. Read when working with
async data fetching that needs loading indicators.references/massive-observable.md — How to refactor bloated new Observable() constructors into small
focused pieces. Read when you see a new Observable callback longer than ~10 lines.references/inner-observable-chains.md — Building rich inner observable sequences with timing, delays, and
animation phases. Read when composing multi-step async sequences or replacing setTimeout patterns.references/custom-operators.md — How to write inline and extracted custom operators with OperatorFunction.
Read when extracting reusable stream logic.The most common RxJS mistake is subscribing too early and then doing imperative work inside the callback — tracking state in variables, calling functions with side effects, or worse, subscribing to another observable inside the callback (the "subscribe-in-subscribe" pattern).
Why this matters: when you subscribe early, you lose the power of the reactive chain. You can no longer compose, retry, cancel, debounce, or share that work. You've escaped from the declarative world into imperative spaghetti, and every new requirement (add a retry, add a timeout, combine with another stream) means more manual state management.
// ❌ Bad: subscribe-in-subscribe with manual state tracking
let currentData: Data | null = null
let loading = false
input$.subscribe((value) => {
loading = true
fetchData(value).subscribe((data) => {
currentData = data
loading = false
})
})
// ✅ Good: everything is in the chain
const data$ = input$.pipe(switchMap((value) => fetchData(value)))For loading state, derive it inside the chain using startWith — see references/loading-state-patterns.md.
new Observable() AntipatternAnother common antipattern is stuffing an entire program into a single new Observable(subscriber => { ... })
constructor — setting up listeners, resolving promises, subscribing to other observables, managing retry
state, all in one giant callback. This is imperative code wearing an Observable costume.
The new Observable() constructor should be small and focused — a thin bridge from one non-reactive source
into the reactive world. For promise-based sources, use defer(() => promise) instead. Retry logic, error
handling, combining sources — all of that belongs in the operator chain.
See references/massive-observable.md for a full before/after example.
| Operator | Behavior | Use when |
|---|---|---|
switchMap | Cancels previous inner when new value arrives | User input, search-as-you-type, route changes — only the latest matters |
mergeMap | Runs all inner observables concurrently | Independent operations where all results are needed (logging, fire-and-forget) |
concatMap | Queues inner observables, runs in order | Order matters and nothing should be dropped (sequential writes, queues) |
exhaustMap | Ignores new values while inner is running | Preventing duplicate submissions (form submit clicks) |
Default to switchMap for most UI/request scenarios.
The inner observable doesn't have to be a single request — it can be an entire timeline of events using
concat, merge, timer, delay. See references/inner-observable-chains.md for animation and timing
examples.
Put catchError on the inner observable when you want the outer stream to keep running. Put it on the
outer stream only when you truly want to replace the entire stream on error:
// ❌ Bad: catchError on outer stream kills it for good
source$.pipe(
switchMap((value) => fetchData(value)),
catchError((err) => of(fallback)),
)
// ✅ Good: catchError inside switchMap — outer stream survives
source$.pipe(switchMap((value) => fetchData(value).pipe(catchError((err) => of(fallback)))))Same principle applies to retry — retry the inner operation, not the entire outer stream:
source$.pipe(
switchMap((value) =>
fetchData(value).pipe(
retry({count: 3, delay: 1000}),
catchError((err) => of(fallback)),
),
),
)The fewer manual subscriptions, the fewer chances to leak. In order of preference:
first(), take(n), takeUntil(destroy$)takeUntil with a notifier:const destroy$ = new Subject<void>();
someObservable$.pipe(
takeUntil(destroy$),
).subscribe(value => /* ... */);
// In teardown: destroy$.next(); destroy$.complete();takeUntil must be the last operator in the pipe. Operators after it (especially flattening operators)
can create inner subscriptions that takeUntil doesn't know about, causing leaks.
merge them into one and subscribe once.new Observable(...), of(), HTTP requests) create a new execution per subscriberSubject, fromEvent) share a single executionShare cold observables with shareReplay({ bufferSize: 1, refCount: true }). Always use refCount: true —
without it, the source subscription stays alive after all subscribers unsubscribe (memory leak).
Instead of mutable variables updated from multiple subscriptions, derive state from streams:
// ❌ Bad: mutable state, inconsistent windows
let items: Item[] = []
let filter = ''
items$.subscribe((i) => {
items = i
recompute()
})
filter$.subscribe((f) => {
filter = f
recompute()
})
// ✅ Good: always consistent
const filteredItems$ = combineLatest([items$, filter$]).pipe(
map(([items, filter]) => items.filter((item) => item.name.includes(filter))),
)combineLatest vs withLatestFrom: combineLatest emits when any input emits (all inputs drive
output). withLatestFrom emits only when the source emits (one driver, others are context).
startWith: combineLatest won't emit until every input has emitted at least once. Use startWith to
provide initial values and unblock the stream.
Subject, BehaviorSubject, ReplaySubject are escape hatches for bridging imperative and reactive code.
Appropriate for event buses and bridging callbacks. Not appropriate as general-purpose state containers —
if you're calling .next() in multiple places to keep a Subject in sync, use a derived stream instead.
Don't be afraid to write them — they're just functions with the signature
(source: Observable<A>) => Observable<B>. Extract repeated .pipe() chains into named operators with
OperatorFunction<In, Out>. See references/custom-operators.md for inline and extracted examples.
tap, Not in subscribeA good rule of thumb: .subscribe() should have no arguments. All side effects — logging, updating the DOM,
writing to a database, sending analytics — belong in tap inside the chain. The .subscribe() at the end
just activates the stream.
// ❌ Bad: side effects crammed into subscribe
source$.pipe(switchMap((value) => fetchData(value))).subscribe(
(data) => {
updateUI(data)
logAnalytics('data_loaded', data)
cache.set(data)
},
(err) => showError(err),
)
// ✅ Good: side effects in tap, subscribe just activates
source$
.pipe(
switchMap((value) => fetchData(value)),
tap((data) => updateUI(data)),
tap((data) => logAnalytics('data_loaded', data)),
tap((data) => cache.set(data)),
tap({error: (err) => showError(err)}),
)
.subscribe()Why this matters: when side effects are in the chain, they're composable. You can add, remove, or reorder
them. You can put a filter between them. You can share the stream and have different subscribers without
duplicating side-effect logic. When everything is stuffed into .subscribe(), you've lost all of that.
tap also accepts an observer object with lifecycle hooks — particularly useful for debugging:
source$.pipe(
tap({
subscribe: () => console.log('subscribed!'),
next: (value) => console.log('value:', value),
error: (err) => console.log('error:', err),
complete: () => console.log('complete'),
unsubscribe: () => console.log('unsubscribed'),
finalize: () => console.log('finalized (complete or unsubscribe)'),
}),
)The subscribe hook is especially handy for debugging "why isn't my stream emitting?" — it confirms whether
anything is actually subscribing.
firstValueFrom/lastValueFrom are appropriate for one-shot interop with promise-based APIs. They're a code
smell when used inside subscribe callbacks to avoid learning the reactive approach — that work belongs in the
chain with switchMap.
| Anti-pattern | Refactoring |
|---|---|
a$.subscribe(x => b$.subscribe(y => ...)) | a$.pipe(switchMap(x => b$)) (or mergeMap/concatMap/exhaustMap) |
| Mutable variable updated in subscribe | scan() or combineLatest to derive state |
setTimeout inside subscribe | delay(), timer(), or debounceTime() |
if guard in subscribe to skip values | filter() before subscribe |
try/catch inside subscribe | catchError() in the pipe |
| Manual request cancellation flags | switchMap (auto-cancels previous) |
| Multiple subscribes to same cold observable | shareReplay({ bufferSize: 1, refCount: true }) |
.subscribe() just to trigger side effects | tap() for side effects, keep the chain going |
Massive new Observable() constructor | Small focused constructors + defer() + operator composition |
await firstValueFrom() inside subscribe | switchMap — stay in the chain |
© sanity-io, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 4 other files (references) in .agents/skills/rxjs-like-a-pro of sanity-io/sanity.
Open the folder on GitHubat commit efa15fb
Rxjs Like A Pro next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Rxjs Like A Pro this skillsanity-io/sanity | 6.4k | — | ~3k | Automated safety check: Pass | MIT | |
| Angularkid-sid/claude-spellbook | 190 | — | ~5k | Automated safety check: Pass | MIT | |
| Angularericrisco/rsc-harness | 180 | — | ~3.4k | Automated safety check: Pass | MIT | |
| Guidelinesakash-network/node | 1.1k | 20 repos | ~577 | Automated safety check: Pass | MIT | |
| Migrate Core Code to Submodulestinyhumansai/openhuman | 42k | — | ~2.6k | Automated safety check: Pass | GPL-3.0 | |
| Component Refactoringlangflow-ai/langflow | 155k | — | ~3.5k | Automated safety check: Pass | MIT |
kid-sid/claude-spellbook
A skill your agent uses when building or refactoring Angular applications — choosing between signals, RxJS, and NgRx for state, configuring routing with guards and lazy loading, optimizing change…
ericrisco/rsc-harness
A skill your agent uses when building, refactoring, or debugging Angular (v20/21+): standalone components, signals, zoneless change detection, @if/@for/@defer control flow, inject() DI…
akash-network/node
Behavioral guidelines to reduce common LLM coding mistakes. An agent skill from akash-network/node.
tinyhumansai/openhuman
Plans and carries out moving non-host-specific code and its tests from the OpenHuman core into vendored tiny submodule libraries, then releases the submodule and re-pins the host.
langflow-ai/langflow
Refactor high-complexity React components in Langflow frontend.
DietrichGebert/ponytail
Makes the agent pick the laziest solution that works: skip unneeded work, reuse what exists, prefer the standard library and platform features, and keep diffs small.
sanity-io/sanity
Automates browser interactions for web testing, form filling, screenshots, and data extraction.
sanity-io/sanity
Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities.
sanity-io/sanity
React and Next.js performance optimization guidelines from Vercel Engineering.
sanity-io/sanity
Add existing screenshots or screen recordings to a GitHub pull request as a before/after or preview block.
sanity-io/sanity
React DevTools CLI for AI agents. An agent skill from sanity-io/sanity.
sanity-io/sanity
Test-driven development with red-green-refactor loop. An agent skill from sanity-io/sanity.
Works with
Categories
How to write idiomatic, efficient RxJS code. An agent skill from sanity-io/sanity. Rxjs Like A Pro is an agent skill from sanity-io/sanity, published by the product's own GitHub organization. How to write idiomatic, efficient RxJS code.
Rxjs Like A Pro fits situations like: the user is writing; debugging code that uses RxJS — including any file that imports from rxjs; mentions of observables; reactive streams.
Run `npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a claude-code`. Or copy the skill folder (.agents/skills/rxjs-like-a-pro in sanity-io/sanity) into .claude/skills/rxjs-like-a-pro in your project. Claude Code loads it when a task matches its description.
Run `npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a codex`. Or copy the skill folder (.agents/skills/rxjs-like-a-pro in sanity-io/sanity) into .agents/skills/rxjs-like-a-pro in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add sanity-io/sanity --skill rxjs-like-a-pro -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/rxjs-like-a-pro, .gemini/skills/rxjs-like-a-pro, .github/skills/rxjs-like-a-pro and .opencode/skills/rxjs-like-a-pro in your project.
SKILL.md names no scripts, command-line tools or credentials: Rxjs Like A Pro is instructions for the agent only.
SKILL.md contains no URLs. Any network use would come from the scripts or tools the agent runs. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Rxjs Like A Pro is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 3k tokens (SKILL.md is roughly 12k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 2.9k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Rxjs Like A Pro: Angular (kid-sid/claude-spellbook, 190 stars), Angular (ericrisco/rsc-harness, 180 stars), Guidelines (akash-network/node, 1.1k stars) and Migrate Core Code to Submodules (tinyhumansai/openhuman, 42k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
sanity-io (a GitHub organization, an official publisher) maintains it in sanity-io/sanity, which has 6,352 GitHub stars. The repository holds 34 skills in this directory. The repository was last updated on October 9, 2026.
Source: sanity-io/sanity on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.