Understand every update
Profiling
Build Octane with profiling metadata to see component timing, render counts, render causes, and scheduling delay in Chrome without changing production behavior in normal builds.
Profiling is a client build specialization. The compiler keeps component and hook source identity, while the runtime records bounded structured events and adds Octane tracks to Chrome's Performance panel. It works with Vite, Rspack, and Rsbuild.
Chrome 134 or newer supports the custom duration tracks used here. Older browsers can still use the structured profiler API and exported trace.
Enable profiling
Set profile: true on the Octane build integration. Keep the flag off for the
build you deploy to users; profiling deliberately trades some speed and bundle
size for diagnostics.
For Vite, a named mode makes it easy to produce a production-optimized profile build:
// vite.config.ts
import { defineConfig } from 'vite';
import { octane } from '@octanejs/vite-plugin';
export default defineConfig(({ mode }) => {
const profiling = mode === 'profile';
return {
plugins: [octane({ profile: profiling })],
build: { sourcemap: profiling },
};
});vite build --mode profile
octane-previewIf the app also uses @octanejs/mdx, pass the same value to octaneMdx() so
document components receive profiling metadata:
import { octaneMdx } from '@octanejs/mdx/vite';
plugins: [octaneMdx({ profile: profiling }), octane({ profile: profiling })];Use the same flag with low-level Rspack:
// rspack.config.mjs
import { OctaneRspackPlugin } from '@octanejs/rspack-plugin';
export default {
mode: 'production',
plugins: [new OctaneRspackPlugin({ profile: true })],
devtool: 'source-map',
};Or with the full Rsbuild integration:
// rsbuild.config.ts
import { defineConfig } from '@rsbuild/core';
import { pluginOctane } from '@octanejs/rsbuild-plugin';
export default defineConfig({
plugins: [pluginOctane({ profile: true })],
output: { sourceMap: { js: 'source-map' } },
});The flag is independent of development metadata and HMR. A production-mode
build with hmr: false can still be profiled, and Rspack's webpack-dialect HMR
continues to work when it is enabled. Full Vite and Rsbuild apps instrument only
the browser graph; the SSR bundle stays unprofiled.
Record in Chrome
- Open Chrome DevTools and select Performance.
- In the panel settings, enable Show custom tracks.
- Start recording, interact with the application, and stop recording.
- Expand the Octane tracks.
Custom-track spans show the component, mount/update phase, inclusive duration, and colors that distinguish errors and suspension. The structured event buffer and exported trace add source identity, instance and attempt numbers, render causes, self duration, and schedule-to-render delay. Suspended and errored attempts close normally and are labelled separately from completed work.
Octane uses Chrome custom timestamps for the compact timeline rather than logging every render. Per-render logging changes the timings it is trying to measure and quickly overwhelms the console.
Console API
Profile builds expose a bounded event buffer as
globalThis.__OCTANE_PROFILER__ for DevTools. Recording starts automatically,
so you can inspect it without adding an import to application code:
const profiler = globalThis.__OCTANE_PROFILER__;
console.table(profiler.summary());
profiler.why('TodoRow');
const events = profiler.getEvents();
const trace = profiler.exportTrace();
profiler.stop();
profiler.clear();
profiler.start({ bufferSize: 10_000, timeline: true });Application-side profiling tools can instead import { profiler } from
octane/profiling. An explicit import keeps the recorder module in that build,
even when profile is false, so keep it in a profiling-only entry if normal
builds must have zero profiler code.
summary() aggregates attempts, completed renders, bails, total and average
self time, maximum inclusive time, queue delay, and dominant causes by
component. why() returns recent events for a component name or stable
component ID. exportTrace() produces Chrome Trace Event data that can be
loaded into Perfetto for offline analysis.
The default buffer retains the newest 10,000 events. Pass bufferSize to
start() to resize it; the oldest events are discarded first. Pass
timeline: false when you need the structured data without Chrome custom-track
timestamps.
stop() pauses event and clock collection without removing the
instrumentation. Call start() to begin recording again, or call clear()
followed by start() to begin a fresh session.
Reading the data
Octane renders eagerly: a component invocation can execute setup code, apply direct DOM changes, and render descendants before it returns. The profiler therefore reports render and DOM work, not a React-style pure render phase and not browser paint time.
- Inclusive time includes nested component work.
- Self time subtracts directly nested component spans.
- Attempts count actual component-function invocations.
- Completed, suspended, and errored classify their outcomes.
- Bails count memo or implicit bails where the component body did not run.
- Queue delay measures schedule-to-render time only for queued updates. The
event's
scheduledfield distinguishes a genuine zero-delay schedule from a synchronous cascade, where both otherwise report zero milliseconds.
Effects and browser style, layout, and paint remain separate work. Use the surrounding Chrome events to understand how component updates lead into them.
Render causes
One render can have several causes because Octane batches work and drains ancestors before descendants. The profiler preserves the bounded cause set instead of keeping only the last update.
Common causes include mounting, root renders, state and reducer hooks, parent cascades, memo/implicit bails, and conservatively-labelled unknown updates. Hook causes include their compiler-known name and source location when available. A cause identifies the cell or path that scheduled work; asynchronous callbacks cannot always retain a precise initiating call site and are labelled conservatively.
Render-phase updates belong to the following attempt. If an ancestor renders a queued child first, the child's direct state cause is kept alongside the parent cascade cause.
Cost and privacy
With profiling omitted or false, the compiler emits no component or hook
metadata and the bundler specializes the runtime with profiling disabled. When
application code does not explicitly import octane/profiling, normal
production bundles can remove the recorder, track labels, clock reads, and
profiling allocations.
Profile builds keep a bounded buffer. By default events contain project-relative or package-relative source paths, component and hook names, timing, and outcome metadata. They do not retain prop values, state values, reducer actions, DOM nodes, browser events, promises, thrown values, or absolute machine paths.
Profile timings include instrumentation overhead and should not be presented as normal production timings. Compare changes using the same profile build, browser, interaction, and recording mode.