skills/react/SKILL.md
Conventions for building React projects — file organization, tooling, agent API, browser testing, and dev workflow.
npx skillsauth add hayeah/dotfiles reactInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
How we set up, organize, and work with React projects.
Stack:
@tailwindcss/vite pluginmobx-react-lite — state managementScaffold with:
bunx create-vite myproject --template react-ts
cd myproject
bun install
bun add wouter mobx mobx-react-lite framer-motion
bun add -d tailwindcss @tailwindcss/vite
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [react(), tailwindcss()],
})
Keep index.css minimal — pages style themselves independently:
@import "tailwindcss";
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
html, body, #root { width: 100%; height: 100%; }
Vite supports ?raw for importing text files (shaders, SVGs, etc.):
import shaderSrc from './myShader.frag?raw'
Add type declarations in src/raw.d.ts:
declare module '*.frag?raw' { const src: string; export default src }
declare module '*.vert?raw' { const src: string; export default src }
Group code by feature/route path, not by semantic type:
src/
App.tsx
main.tsx
index.css
types.d.ts
raw.d.ts
pages/
HomePage.tsx
pixelate/ # one folder per experiment/feature
PixelatePage.tsx # page component
webgl.ts # helpers used by this page
chuckClose.frag # assets co-located
kusamaDot.frag
dashboard/
DashboardPage.tsx
DashboardStore.ts
MetricsChart.tsx
Rules:
src/shaders/, src/stores/, src/components/ — that forces you to jump between directories for a single featuresrc/src/lib/Use wouter with route params. Each experiment/feature gets a sub-route:
import { Route, Switch, Redirect } from 'wouter'
export function App() {
return (
<Switch>
<Route path="/" component={HomePage} />
<Route path="/pixelate/:effect" component={PixelatePage} />
<Route path="/pixelate"><Redirect to="/pixelate/chuck-close" /></Route>
<Route>404</Route>
</Switch>
)
}
window.__agent)Every page exposes a window.__agent object for browser automation. This is a per-route convention — each page registers on mount, cleans up on unmount.
__DOC__ stringMust be the first thing after imports. The agent reads it from source to know what's available:
const __DOC__ = `
# MyPage
## window.__agent
- store — MobX observable
- store.count (number)
- store.query (string)
- $canvas — the canvas element
- loadImage(src) — load an image URL
`
$ prefix for DOM element refs: $canvas, $searchInput, $scrollAreaconst store = observable({ count: 0, query: '' })
export const MyPage = observer(() => {
const canvasRef = useRef<HTMLCanvasElement>(null)
useEffect(() => {
window.__agent = {
store,
get $canvas() { return canvasRef.current },
doSomething() { /* ... */ },
}
return () => { window.__agent = null }
}, [])
// ...
})
In src/types.d.ts:
declare interface Window { __agent: any }
/browserUse the browser skill to screenshot and interact with pages during development.
# Basic screenshot
browser screenshot --open http://localhost:19003/mypage -o "$(tmpfile mypage.png)"
# Wait for content to load
browser screenshot --open http://localhost:19003/mypage -w 2000 -o "$(tmpfile mypage.png)"
# Modify state then screenshot
browser screenshot --open http://localhost:19003/mypage \
-o "$(tmpfile mypage.png)" \
-w 2000 \
-e '__agent.store.count = 42'
browser screenshot --open http://localhost:19003/mypage \
-o "$(tmpfile steps.png)" \
--steps '
- wait: "2000"
- eval: __agent.store.mode = "dark"
wait: "500"
'
browser eval --open http://localhost:19003/mypage '__agent.store'
browser eval --open http://localhost:19003/mypage '__agent.doSomething()'
__agent to drive state changes for screenshot comparisonsRegister the project in ~/.config/devport/devport.toml:
[service."myproject"]
cwd = "~/github.com/hayeah/myproject"
command = ["bunx", "--bun", "vite", "--port", "${PORT}", "--host", "0.0.0.0"]
port = 19003
restart = "never"
[service."myproject".health]
type = "http"
url = "/"
expect_status = [200]
startup_timeout = "15s"
Key points:
0.0.0.0 so the server is accessible externally (for tunnels, mobile testing)devport start --key myproject to start, devport restart --key myproject after config changesdevport freeport to pick an available portbunx --bun vite)npx tsc --noEmit/browser skill + __agent APItools
Web UI development — Vite+ toolchain setup and browser-based E2E testing workflow.
tools
Tooling and style guide for TypeScript projects.
development
Capture tmux pane content and export as text, HTML, SVG, PNG, or JPG. Use when you need a screenshot or text dump of a tmux pane for sharing, feeding to AI, or archiving terminal state.
testing
Copy-edit text. Fix grammar and/or tidy text into a concise listicle.