Best for
- Use when writing or reviewing hot-path code in app-render, stream-utils, routing, caching, or any per-request code path.
vercel/next.js/.agents/skills/v8-jit/SKILL.md
V8 JIT optimization patterns for writing high-performance JavaScript in Next.js server internals. Use when writing or reviewing hot-path code in app-render, stream-utils, routing, caching, or any per-request code path. Covers hidden classes / shapes, monomorphic call sites, inline caches, megamorphic deopt, closure allocation, array packing, and profiling with --trace-opt / --trace-deopt.
Decision brief
Use this skill when writing or optimizing performance-critical code paths in Next.js server internals — especially per-request hot paths like rendering, streaming, routing, and caching.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/vercel/next.js --skill ".agents/skills/v8-jit"Inspect the Agent Skill "v8-jit" from https://github.com/vercel/next.js/blob/bcea67d4e96fbaf8c51f50951e497724d2a02cfb/.agents/skills/v8-jit/SKILL.md at commit bcea67d4e96fbaf8c51f50951e497724d2a02cfb. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Profiling and Verification” section in the pinned source before continuing.
V8 compiles JavaScript through multiple tiers:
Every JavaScript object has an internal "hidden class" (V8 calls it a Map, the spec calls it a Shape). Objects that share the same property names, added in the same order, share the same hidden class. This enables fast property access via inline caches.
Assign every property in the constructor, in the same order, for every
Span in src/trace/trace.ts initializes all fields in the constructor in a fixed order — name, parentId, attrs, status, id, start, now. This ensures all Span instances share one hidden class.
Permission review
The documentation asks the agent to run terminal commands or scripts.
node --trace-opt server.js 2>&1 | grep "my-function-name"The documentation asks the agent to run terminal commands or scripts.
node --trace-deopt server.js 2>&1 | grep "my-function-name"Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 88/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 141,364 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Use this skill when writing or optimizing performance-critical code paths in Next.js server internals — especially per-request hot paths like rendering, streaming, routing, and caching.
V8 compiles JavaScript through multiple tiers:
Code starts in Ignition and is promoted to higher tiers based on execution frequency and collected type feedback. Turbofan produces the fastest machine code but bails out (deopts) when assumptions are violated at runtime.
The key principle: help V8 make correct speculative assumptions by keeping types, shapes, and control flow predictable.
Every JavaScript object has an internal "hidden class" (V8 calls it a Map, the spec calls it a Shape). Objects that share the same property names, added in the same order, share the same hidden class. This enables fast property access via inline caches.
// GOOD — consistent shape, single hidden class transition chain
class RequestContext {
url: string
method: string
headers: Record<string, string>
startTime: number
cached: boolean
constructor(url: string, method: string, headers: Record<string, string>) {
this.url = url
this.method = method
this.headers = headers
this.startTime = performance.now()
this.cached = false // always initialize, even defaults
}
}
// BAD — conditional property addition creates multiple hidden classes
class RequestContext {
constructor(url, method, headers, options) {
this.url = url
this.method = method
if (options.timing) {
this.startTime = performance.now() // shape fork!
}
if (options.cache) {
this.cached = false // another shape fork!
}
this.headers = headers
}
}
Rules:
null / undefined / false as default values rather than
omitting the property.delete a property on a hot object — it forces a transition to
dictionary mode (slow properties).obj.newProp = x) on objects
used in hot paths.// GOOD — same key order, shares hidden class
const a = { type: 'static', value: 1 }
const b = { type: 'dynamic', value: 2 }
// BAD — different key order, different hidden classes
const a = { type: 'static', value: 1 }
const b = { value: 2, type: 'dynamic' }
Span in src/trace/trace.ts initializes all fields in the constructor in a
fixed order — name, parentId, attrs, status, id, _start, now.
This ensures all Span instances share one hidden class.
V8's inline caches (ICs) track the types/shapes seen at each call site or property access:
| IC State | Shapes Seen | Speed |
|---|---|---|
| Monomorphic | 1 | Fastest — single direct check |
| Polymorphic | 2–4 | Fast — linear search through cases |
| Megamorphic | 5+ | Slow — hash-table lookup, no inlining |
Once an IC goes megamorphic it does NOT recover (until the function is re-compiled). Megamorphic ICs also prevent Turbofan from inlining the function.
// GOOD — always called with the same argument shape
function processChunk(chunk: Uint8Array): void {
// chunk is always Uint8Array → monomorphic
}
// BAD — called with different types at the same call site
function processChunk(chunk: Uint8Array | Buffer | string): void {
// IC becomes polymorphic/megamorphic
}
Practical strategies:
Buffer → Uint8Array
once) and keep internal functions monomorphic.null and undefined for the same parameter — pick one
sentinel value.// Entry point dispatches once
function handleStream(stream: ReadableStream | Readable) {
if (stream instanceof ReadableStream) {
return handleWebStream(stream) // monomorphic call
}
return handleNodeStream(stream) // monomorphic call
}
This is the pattern used in stream-ops.ts and throughout the stream-utils
code (Node.js vs Web stream split via compile-time switcher).
Every closure captures its enclosing scope. Creating closures in hot loops or per-request paths generates GC pressure and can prevent escape analysis.
// BAD — closure allocated for every request
function handleRequest(req) {
stream.on('data', (chunk) => processChunk(chunk, req.id))
}
// GOOD — shared listener, request context looked up by stream
const requestIdByStream = new WeakMap()
function onData(chunk) {
const id = requestIdByStream.get(this)
if (id !== undefined) processChunk(chunk, id)
}
function processChunk(chunk, id) {
/* ... */
}
function handleRequest(req) {
requestIdByStream.set(stream, req.id)
stream.on('data', onData)
}
// BEST — pre-allocate the callback as a method on a context object
class StreamProcessor {
id: string
constructor(id: string) {
this.id = id
}
handleChunk(chunk: Uint8Array) {
processChunk(chunk, this.id)
}
}
// BAD — allocates a new object per iteration
for (const item of items) {
doSomething({ key: item.key, value: item.value })
}
// GOOD — reuse a mutable scratch object
const scratch = { key: '', value: '' }
for (const item of items) {
scratch.key = item.key
scratch.value = item.value
doSomething(scratch)
}
node-stream-helpers.ts hoists encoder, BUFFER_TAGS, and tag constants to
module scope to avoid re-creating them on every request. The bufferIndexOf
helper uses Buffer.indexOf (C++ native) instead of a per-call JS loop,
eliminating per-chunk allocation.
V8 tracks array "element kinds" — an internal type tag that determines how elements are stored in memory:
| Element Kind | Description | Speed |
|---|---|---|
PACKED_SMI | Small integers only, no holes | Fastest |
PACKED_DOUBLE | Numbers only, no holes | Fast |
PACKED_ELEMENTS | Mixed/objects, no holes | Moderate |
HOLEY_* | Any of above with holes | Slower (extra bounds check) |
Transitions are one-way — once an array becomes HOLEY or PACKED_ELEMENTS,
it never goes back.
new Array(n) creates a holey array.
Prefer [] and push(), or use Array.from({ length: n }, initFn).arr[100] = x on an empty array creates 100 holes.[1, 'two', {}] immediately becomes PACKED_ELEMENTS.// GOOD — packed SMI array
const indices: number[] = []
for (let i = 0; i < n; i++) {
indices.push(i)
}
// BAD — holey from the start
const indices = new Array(n)
for (let i = 0; i < n; i++) {
indices[i] = i
}
accumulateStreamChunks in app-render.tsx uses const staticChunks: Array<Uint8Array> = [] with push() — keeping a packed array of a single type
throughout its lifetime.
arguments object: using arguments in non-trivial ways (e.g.
arguments[i] with variable i, leaking arguments). Use rest params
instead.eval / with: prevents optimization entirely.for...in on hot objects; prefer
Object.keys() / Object.entries() when possible.// GOOD — predictable: always returns same type
function getStatus(code: number): string {
if (code === 200) return 'ok'
if (code === 404) return 'not found'
return 'error'
}
// BAD — returns different types
function getStatus(code: number): string | null | undefined {
if (code === 200) return 'ok'
if (code === 404) return null
// implicitly returns undefined
}
switch Dispatch// WATCH OUT — `node.type` IC can go megamorphic if many shapes hit one site
function render(node) {
switch (node.type) {
case 'div':
return { tag: 'div', children: node.children }
case 'span':
return { tag: 'span', text: node.text }
case 'img':
return { src: node.src, alt: node.alt }
// Many distinct node layouts can make this dispatch site polymorphic
}
}
This pattern is not always bad. Often the main pressure is at the shared
dispatch site (node.type), while properties used only in one branch stay
monomorphic within that branch. Reach for normalization/splitting only when
profiles show this site is hot and polymorphic.
Buffer.concat().string.indexOf() > regex for simple substring checks.new RegExp() inside a hot
function — hoist it to module scope.// GOOD — regex hoisted to module scope
const ROUTE_PATTERN = /^\/api\//
function isApiRoute(path: string): boolean {
return ROUTE_PATTERN.test(path)
}
// BAD — regex recreated on every call
function isApiRoute(path: string): boolean {
return /^\/api\//.test(path) // V8 may or may not cache this
}
Map and Set vs Plain ObjectsMap is faster than plain objects for frequent additions/deletions
(avoids hidden class transitions and dictionary mode).Set is faster than obj[key] = true for membership checks with
dynamic keys.Object.freeze({...}) are fine — V8 optimizes them as constant.# Trace which functions get optimized
node --trace-opt server.js 2>&1 | grep "my-function-name"
# Trace deoptimizations (critical for finding perf regressions)
node --trace-deopt server.js 2>&1 | grep "my-function-name"
# Combined: see the full opt/deopt lifecycle
node --trace-opt --trace-deopt server.js 2>&1 | tee /tmp/v8-trace.log
# Show IC state transitions (verbose)
node --trace-ic server.js 2>&1 | tee /tmp/ic-trace.log
# Print optimized code (advanced)
node --print-opt-code --code-comments server.js
# Profile a production build
node --cpu-prof --cpu-prof-dir=/tmp/profiles \
node_modules/.bin/next build
# Profile the server during a benchmark
node --cpu-prof --cpu-prof-dir=/tmp/profiles \
node_modules/.bin/next start &
# ... run benchmark ...
# Analyze in Chrome DevTools: chrome://inspect → Open dedicated DevTools
# Quick trace-deopt check on a specific test
node --trace-deopt $(which jest) --runInBand test/path/to/test.ts \
2>&1 | grep -i "deopt" | head -50
% Natives (Development/Testing Only)With --allow-natives-syntax:
function hotFunction(x) {
return x + 1
}
// Force optimization
%PrepareFunctionForOptimization(hotFunction)
hotFunction(1)
hotFunction(2) % OptimizeFunctionOnNextCall(hotFunction)
hotFunction(3)
// Check optimization status
// 1 = optimized, 2 = not optimized, 3 = always optimized, 6 = maglev
console.log(%GetOptimizationStatus(hotFunction))
delete on hot objectsMap/Set used for dynamic key collectionsarguments object — use rest paramstry/catch at function boundary, not inside tight loopsjoin() or Buffer.concat()string | null | undefined mixes)$dce-edge — DCE-safe require patterns (compile-time dead code)$runtime-debug — runtime bundle debugging and profiling workflowAlternatives
aAAaqwq/AGI-Super-Team
UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 9 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind, shadcn/ui). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: g
alirezarezvani/claude-skills
Reverse-engineer any codebase into a complete Product Requirements Document (PRD). Analyzes routes, components, state management, API integrations, and user interactions to produce business-readable documentation detailed enough for engineers or AI agents to fully reconstruct every page and endpoint. Works with frontend frameworks (React, Vue, Angular, Svelte, Next.js, Nuxt), backend frameworks (NestJS, Django, Express, FastAPI), and fullstack applications. Use when users mention: generate PRD,
alirezarezvani/claude-skills
Fullstack development toolkit with project scaffolding for Next.js, FastAPI, MERN, and Django stacks, code quality analysis with security and complexity scoring, and stack selection guidance. Use when the user asks to "scaffold a new project", "create a Next.js app", "set up FastAPI with React", "analyze code quality", "audit my codebase", "what stack should I use", "generate project boilerplate", or mentions fullstack development, project setup, or tech stack comparison.
vercel/next.js
Benchmark React or Next.js changes on Vercel Sandbox VMs with paired A/B statistics: react PR/commit vs base, or Next.js PR/commit vs base, measured end-to-end through the bench/render-pipeline app (rps, latency, p95; TTFB, RSS and document/Flight bytes when the Next side captures them) and, for React changes, through the react repo's flight-ssr-bench fixture (Node AND Edge web-streams paths, Fizz and Flight+Fizz). Use whenever the user asks to bench, perf test, or A/B a React PR, a react-server