remix-v2-perf-ssr-review
Reviews Remix v2 code for caching header misuse, missing server/client split, hydration mismatches (Date, Math.random, locale), prefetch hygiene, and asset bottlenecks. Use when reviewing routes that export headers, use .server.ts/.client.ts, or render dates/IDs in a Remix v2 codebase.
What this skill does
# Remix v2 Performance / SSR Code Review
Targets TypeScript route modules importing from `@remix-run/*`. See [remix-v2-perf-ssr](../remix-v2-perf-ssr/SKILL.md) for canonical patterns.
## Quick Reference
| Issue Type | Reference |
|------------|-----------|
| Missing `headers` export, unsafe `public` cache, child-drops-parent headers, missing `Vary: Cookie`, `Set-Cookie` + `public` | [references/caching-headers.md](references/caching-headers.md) |
| Server libs imported without `.server.ts`, `process.env.SECRET_*` leaks, `typeof window` substituted for `.server.ts` | [references/server-client-split.md](references/server-client-split.md) |
| `new Date()` in render, `Math.random()` in keys, locale formatting without explicit locale, missing `useId()`, blanket `suppressHydrationWarning` | [references/hydration.md](references/hydration.md) |
| `prefetch="render"` on every link, `defer` for fast data, missing `<Suspense>` around `<Await>`, prefetch to side-effect routes | [references/prefetch-streaming.md](references/prefetch-streaming.md) |
| `dangerouslySetInnerHTML` with untrusted data, missing `loading="lazy"`, missing `links` preload, stylesheet injected in body | [references/assets.md](references/assets.md) |
## Review Checklist
- [ ] Routes serving data export `headers` (even if the answer is `no-store`)
- [ ] Child routes serving personalized data export their own `headers` (otherwise they silently inherit the parent's policy)
- [ ] `Cache-Control: public` is never set on auth'd or cookie-bearing responses
- [ ] `Vary: Cookie` is set when cache decision depends on session
- [ ] Server-only libs (`prisma`, `bcrypt`, `node:fs`, `jsonwebtoken`) live in `*.server.ts` or `app/.server/`
- [ ] Secret env (`process.env.STRIPE_SECRET_KEY`, etc.) is read only inside loaders/actions or `.server` modules
- [ ] Client-exposed env is whitelisted into `window.ENV`, never raw `process.env`
- [ ] `typeof window === "undefined"` is not used as a substitute for `.server.ts` (treeshaking is unreliable)
- [ ] No `new Date()`, `Math.random()`, `Date.now()`, `crypto.randomUUID()` in JSX render path
- [ ] Locale formatting (`toLocaleDateString`, `Intl.DateTimeFormat`) passes an explicit locale
- [ ] Components generating IDs use `useId()`, not `Math.random()` or counters
- [ ] `suppressHydrationWarning` is scoped to a single element with a code comment explaining why
- [ ] `<Link prefetch="render">` is reserved for above-the-fold critical nav, not lists
- [ ] `<PrefetchPageLinks>` does not target routes whose loaders have side effects (analytics, mutations)
- [ ] Every `<Await>` is wrapped in `<Suspense>` and has an `errorElement`
- [ ] `defer()` is used only for genuinely slow data (>~50ms); fast data is awaited
- [ ] Below-the-fold images use `loading="lazy"` and have `width`/`height`
- [ ] Critical fonts/CSS are preloaded via the `links` export, not injected in body
## Valid Patterns (Do NOT Flag)
These are correct Remix v2 usage and must not be reported as issues:
- **Route without `headers` export when caching is intentionally off** — auth'd dashboards, account pages, and routes wrapped in a layout that already returns `no-store` may legitimately omit `headers`. Flag only if the route serves cacheable public content with no `headers`.
- **`new Date()` inside `useEffect`** — runs after hydration on the client only; no SSR mismatch possible. Same for `Date.now()`, `Math.random()`, `crypto.randomUUID()` inside effects.
- **`Math.random()` / `new Date()` inside event handlers** — handlers run after hydration. Only flag when the value is used during render.
- **`suppressHydrationWarning` on a single `<time>` (or similar) element with a clear comment** — accepted narrow escape for known-divergent values like absolute timestamps formatted client-side. Flag only when applied at a parent that wraps a large subtree or with no explanation.
- **`.client.ts` files for client-only libraries** — Stripe.js, map widgets, chart libs that read `window` belong in `*.client.ts` by convention; do not flag the file extension.
- **`useId()` with extra characters appended** — `` `${id}-input` `` is the documented pattern for multi-element components; do not flag as "non-stable id."
- **Raw ISO string rendered in SSR + reformatted in `useEffect`** — the canonical hydration-safe time pattern; flag only if the reformat happens in render.
- **`headers` export returning `{}` or `no-store`** — explicit "do not cache" is a deliberate decision and should not be flagged as misuse.
- **`<Link prefetch="intent">` on standard nav** — the recommended default; flag only when the loader has side effects.
- **`loaderHeaders` forwarded to the document via `headers` export** — co-locating data and document policy is the documented pattern, not duplication.
## Context-Sensitive Rules
Apply these only when the specific context applies:
| Issue | Flag ONLY IF |
|-------|--------------|
| Missing `headers` export | Route serves cacheable public content (not auth'd, not personalized, not intentionally `no-store`) |
| Child route missing `headers` | An ancestor exports `headers` AND its policy is broader than the child's cacheability (e.g., parent caches public + s-maxage, child serves personalized data) |
| `Cache-Control: public` | Loader actually reads session / user state (or response carries `Set-Cookie`) |
| `Vary: Cookie` missing | Loader branches response shape on a cookie (theme, locale, session) AND the cache is `public`/`s-maxage` |
| `new Date()` / `Math.random()` / `Date.now()` | Call site is in render path — NOT in `useEffect`, event handler, `<ClientOnly>`, or post-hydration code |
| Locale formatting without locale | Result is rendered into JSX (not used only inside an effect / handler) |
| `<Link prefetch="render">` | Link is inside a list / `.map()` iterator (not above-the-fold critical nav) |
| `<Link prefetch="intent">` to side-effect loader | Loader has observable side effects (analytics write, counter increment, log emit) AND doesn't branch on the `Purpose: prefetch` header |
| Server lib import without `.server.ts` | Importing file is reachable from the client graph (route module, non-`.server` util reached from a component) |
| `process.env.SECRET_*` reference | Reference is in a component body or in a non-`.server` module reached from the client graph |
| Missing `loading="lazy"` on image | Image is rendered below the fold (not in `<header>`, hero section, or above any `<main>` content) |
| Missing `width`/`height` on image | Project does NOT use a build-time image processor that injects dimensions |
## Hard gates (before writing findings)
Run these in order. **Do not draft user-facing findings until every gate passes** for the batch you are about to report.
1. **Location evidence** — **Pass:** Each issue lists the repo path and either a line range or a short verbatim quote from the file you read (not memory or diff-only guesswork). Cache, hydration, and `.server` claims without a concrete file path are not reportable.
2. **Exemption check** — **Pass:** For each issue, you can state in one line why it is *not* covered by [Valid Patterns (Do NOT Flag)](#valid-patterns-do-not-flag). In particular: confirm a missing `headers` export is not on an intentionally-uncacheable route, confirm `.client.ts` is not a legitimate client-only library, confirm `suppressHydrationWarning` is not scoped + commented.
3. **Hydration-context check** — **Pass:** Before flagging `new Date()`, `Math.random()`, `Date.now()`, `crypto.randomUUID()`, or locale formatting, confirm the call site is in the **render path** of a component. Calls inside `useEffect`, `useLayoutEffect`, event handlers, callbacks passed to `setTimeout`/`requestAnimationFrame`, or inside `<ClientOnly>{() => ...}</ClientOnly>` are post-hydration and must not be flagged.
4. **Parent/child headers chain check** — **Pass:** Before flagging "missing `headers` on a child" as silent cache inheritance, confirm an ancestor route in theRelated in Image & Video
watch
IncludedWatch a video (URL or local path). Downloads with yt-dlp, extracts auto-scaled frames with ffmpeg, pulls the transcript from captions (or Whisper API fallback), and hands the result to Claude so it can answer questions about what's in the video.
physical-ai-defect-image-generation
IncludedUse when the user wants to orchestrate defect image generation, run associated setup, or handle outputs on OSMO. The Day 0 path handles cold-start with USD-to-ROI, image-edit augmentation, and AnomalyGen to create initial PCBA datasets. The Day 1 path performs inference and labeling on real images. This skill helps with first-time asset setup, creation of finetuning checkpoints, and configuring deployment. Trigger keywords: defect image generation, dig workflow, dig pipeline, defect image detection workflow, aoi pipeline, aoi anomalygen, usd2roi anomalygen, day 0 pcba, day 1 pcba, day 1 real-photo alignment, day 1 manual roi, metal surface anomaly, glass defect, anomalygen finetune, setup_pcb, setup_metal, setup_glass, setup_pretrained, dig setup, dig datasets, dig pretrained checkpoint, dig image-edit endpoint.
accelint-react-best-practices
IncludedReact performance optimization and best practices. ALWAYS use this skill when working with any React code - writing components, hooks, JSX; refactoring; optimizing re-renders, memoization, state management; reviewing for performance; fixing hydration mismatches; debugging infinite re-renders, stale closures, input focus loss, animations restarting; preventing remounting; implementing transitions, lazy initialization, effect dependencies. Even simple React tasks benefit from these patterns. Covers React 19+ (useEffectEvent, Activity, ref props). Triggers - useEffect, useState, useMemo, useCallback, memo, inline components, nested components, components inside components, re-render, performance, hydration, SSR, Next.js, useDeferredValue, combined hooks.
elevenlabs-agents
IncludedBuild conversational AI voice agents with ElevenLabs Platform using React, JavaScript, React Native, or Swift SDKs. Configure agents, tools (client/server/MCP), RAG knowledge bases, multi-voice, and Scribe real-time STT. Use when: building voice chat interfaces, implementing AI phone agents with Twilio, configuring agent workflows or tools, adding RAG knowledge bases, testing with CLI "agents as code", or troubleshooting deprecated @11labs packages, Android audio cutoff, CSP violations, dynamic variables, or WebRTC config. Keywords: ElevenLabs Agents, ElevenLabs voice agents, AI voice agents, conversational AI, @elevenlabs/react, @elevenlabs/client, @elevenlabs/react-native, @elevenlabs/elevenlabs-js, @elevenlabs/agents-cli, elevenlabs SDK, voice AI, TTS, text-to-speech, ASR, speech recognition, turn-taking model, WebRTC voice, WebSocket voice, ElevenLabs conversation, agent system prompt, agent tools, agent knowledge base, RAG voice agents, multi-voice agents, pronunciation dictionary, voice speed control, elevenlabs scribe, @11labs deprecated, Android audio cutoff, CSP violation elevenlabs, dynamic variables elevenlabs, case-sensitive tool names, webhook authentication
humanizer
IncludedHumanize AI-generated text by detecting and removing patterns typical of LLM output. Rewrites text to sound natural, specific, and human. Uses 28 pattern detectors, 560+ AI vocabulary terms across 3 tiers, and statistical analysis (burstiness, type-token ratio, readability) for comprehensive detection. Use when asked to humanize text, de-AI writing, make content sound more natural/human, review writing for AI patterns, score text for AI detection, or improve AI-generated drafts. Covers content, language, style, communication, and filler categories.
generating-mermaid-diagrams
IncludedSalesforce architecture diagrams using Mermaid with ASCII fallback. Use this skill when generating text-based diagrams for Salesforce architecture, OAuth flows, ERDs, integration sequences, or Agentforce structure. TRIGGER when: user says "diagram", "visualize", "ERD", or asks for sequence diagrams, flowcharts, class diagrams, or architecture visualizations in Mermaid. DO NOT TRIGGER when: user wants PNG/SVG image output (use generating-visual-diagrams), or asks about non-Salesforce systems.