High-performance React component for inspecting deeply nested objects with virtualization, grouping, and deterministic value getters.
React Object View targets React 19 projects (Node 22+ / Yarn 4 recommended) and ships a TypeScript-first API with ESM + UMD bundles.
- Virtualized tree view – only visible rows render, so 100k+ nodes stay smooth.
- Resolver system – promises, maps, sets, errors, dates, regexes, iterables, grouped proxies, typed arrays, buffers, and custom classes.
- Sticky path headers – pin ancestor rows while scrolling so nested contexts stay visible.
- Grouping for huge payloads –
arrayGroupSize&objectGroupSizebucket massive collections (objects must be enumerated first—see note below). - TypeScript-native – published
.d.tsand React 19 JSX runtime support. - Zero dependencies – lightweight and self-contained (besides React).
- Styling hooks – CSS variables + theme presets plus
className/styleescape hatches. - Generic tree APIs – build custom tree views for non-object data structures (files, ASTs, etc.).
- Copy to clipboard – built-in action buttons to copy primitive values or JSON-serialized objects.
- Change awareness – optional flashing highlights updated values.
- Interactive hover – highlights the indentation guide for the current parent context.
- Line numbers – optional gutter with 0-based indices for debugging.
npm install react-obj-view
# or
yarn add react-obj-viewimport { ObjectView } from "react-obj-view";
import "react-obj-view/dist/react-obj-view.css";
const user = {
name: "Ada",
stack: ["TypeScript", "React"],
meta: new Map([["lastLogin", new Date()]]),
};
export function DebugPanel() {
return (
<ObjectView
valueGetter={() => user}
name="user"
expandLevel={2}
/>
);
}const valueGetter = useCallback(() => user, [user]);
<ObjectView valueGetter={valueGetter} />;Wrap dynamic data in useMemo/useCallback so the virtual tree only re-walks when the underlying value actually changes.
| Prop | Type | Default | Description |
|---|---|---|---|
valueGetter |
() => unknown |
required | Lazily supplies the data that should be rendered. |
name |
string |
undefined |
Optional root label shown before the first colon. |
expandLevel |
number | boolean |
false |
Depth of initial expansion; true expands everything (up to depth 20). |
objectGroupSize |
number |
0 |
Enable grouping for objects when they exceed this many keys. Objects must be fully enumerated to detect size, so only enable this when you need grouped previews and can afford the enumeration cost. |
arrayGroupSize |
number |
0 |
Splits very large arrays into range buckets ([0…999]) for faster navigation. |
resolver |
Map<any, ResolverFn> |
undefined |
Merge in custom resolvers keyed by constructor. |
highlightUpdate |
boolean |
false |
Flash updated values via useChangeFlashClasses. |
stickyPathHeaders |
boolean |
true |
Pins the current node's ancestor label while you scroll through its children; disable to revert to free-scrolling rows. |
preview |
boolean |
true |
Show inline previews (Array(5), 'abc…') on collapsed rows. |
nonEnumerable |
boolean |
false |
Include non-enumerable properties during traversal. |
includeSymbols |
boolean |
false |
Include symbol keys when enumerating or previewing objects. |
showLineNumbers |
boolean |
false |
Display a gutter with zero-based line numbers. |
lineHeight |
number |
14 |
Row height (in px) used by the virtual scroller. Keep this in sync with your CSS/fonts; mismatches cause rows to drift/overlap because virtualization still uses the old size. |
style |
React.CSSProperties |
undefined |
Inline styles applied to .big-objview-root (theme presets are plain objects). |
className |
string |
undefined |
Extra class hooked onto .big-objview-root. |
actionRenders |
React.FC<ObjectViewRenderRowProps> |
DefaultActions |
Custom component to render row actions (copy, expand, etc.). See example. |
👉 Need more detail? Check the API Documentation.
The package exports several ready-made palettes:
import { ObjectView } from "react-obj-view";
import { themeMonokai } from "react-obj-view";
<ObjectView valueGetter={getter} style={themeMonokai} />;Prefer CSS? Override the variables directly:
.big-objview-root {
--bigobjview-color: #e5e9f0;
--bigobjview-bg-color: #1e1e1e;
--bigobjview-type-string-color: #c3e88d;
--bigobjview-type-number-color: #f78c6c;
}<ObjectView valueGetter={getter} className="object-view" />Note: The built-in themeDefault adapts automatically to light and dark modes using CSS light-dark() and the user's prefers-color-scheme. Other presets (e.g., One Dark, Dracula, Monokai) are static and do not change automatically. The demo’s light/dark/auto toggle affects the page chrome only — ObjectView does not auto-switch its theme; choose a preset explicitly if you want a specific look.
Want full control? Build your own palette with the exported helpers:
import {
createTheme,
extendTheme,
themeDefault,
} from "react-obj-view";
// Build from scratch (every CSS variable from the table below is required)
const midnight = createTheme({
"--bigobjview-color": "#e8eaed",
"--bigobjview-bg-color": "#0b1116",
"--bigobjview-change-color": "#ff8a65",
"--bigobjview-fontsize": "12px",
"--bigobjview-type-boolean-color": "#18ffff",
"--bigobjview-type-number-color": "#ffab40",
"--bigobjview-type-bigint-color": "#ff6d00",
"--bigobjview-type-string-color": "#ffee58",
"--bigobjview-type-object-array-color": "#40c4ff",
"--bigobjview-type-object-object-color": "#7e57c2",
"--bigobjview-type-object-promise-color": "#ec407a",
"--bigobjview-type-object-map-color": "#00e5ff",
"--bigobjview-type-object-set-color": "#26c6da",
"--bigobjview-type-function-color": "#80cbc4",
"--bigobjview-type-object-regexp-color": "#ef5350",
"--bigobjview-type-object-date-color": "#8bc34a",
"--bigobjview-type-object-error-color": "#ff7043",
"--bigobjview-action-btn": "#444",
"--bigobjview-action-success": "#00e676",
"--bigobjview-action-error": "#ff5252",
});
// …or extend an existing preset
const midnightCondensed = extendTheme(midnight, {
"--bigobjview-fontsize": "11px",
lineHeight: 12,
});Refer to the “Styling reference” table in the docs whenever you need the full list of supported CSS variables.
Line-height tip: If your theme tweaks fonts or padding, expose a shared CSS variable (e.g.
--rov-row-height) and set both.row { height: var(--rov-row-height) }and thelineHeightprop from the same value so scrolling math stays correct.
class ApiEndpoint {
constructor(
public method: string,
public url: string,
public status: number,
public responseTime: number,
) {}
}
const resolver = new Map([
[
ApiEndpoint,
(endpoint, cb, next, isPreview) => {
if (isPreview) {
cb('summary', `${endpoint.method} ${endpoint.url}`, true);
cb('status', endpoint.status, true);
return;
}
cb('responseTime', `${endpoint.responseTime}ms`, true);
next(endpoint);
},
],
]);
<ObjectView valueGetter={() => data} resolver={resolver} />;<ObjectView
valueGetter={() => largeObject}
objectGroupSize={250}
arrayGroupSize={500}
/>- Arrays get chunked up immediately because their length is known.
- Objects must be enumerated to count keys. Use grouping when the trade-off (initial enumeration vs. quicker navigation) makes sense for the payload.
Each row includes built-in action buttons:
// Primitives get a "Copy" button
const config = { apiKey: "sk-abc123", timeout: 5000 };
<ObjectView valueGetter={() => config} />
// Hover over any row to see Copy / Copy JSON buttons
// Copy actions show success/error feedback
// Automatically resets after 5 seconds- Copy button for strings, numbers, bigints – copies the raw value
- Copy JSON button for objects, arrays, dates – serializes via
JSON.stringify()
The viewer highlights the indentation guide for the current parent context on hover, making it easier to trace parent-child relationships:
<ObjectView valueGetter={() => deeplyNested} expandLevel={3} />
// Hover over any row to see visual feedback
// CSS custom properties (--active-index, --active-parent) enable theme customizationNo configuration needed—the feature is built-in and adapts to your theme.
Enable a gutter with 0-based line numbers for easier debugging:
<ObjectView
valueGetter={() => largeData}
showLineNumbers={true}
lineHeight={18}
/>The library now exports generic tree APIs for non-object data:
import { walkingFactory, type WalkingAdapter } from 'react-obj-view';
// Define your domain (e.g., file system, AST, org chart)
type FileNode = {
name: string;
type: 'folder' | 'file';
children?: FileNode[];
};
// Implement the adapter
const fileAdapter: WalkingAdapter<...> = {
valueHasChild: (node) => node.type === 'folder' && !!node.children?.length,
iterateChilds: (node, ctx, ref, cb) => {
node.children?.forEach(child => cb(child, child.name, { ... }));
},
// ... other methods
};
const fileTreeFactory = () => walkingFactory(fileAdapter);See Generic Tree Stack for a complete walkthrough with React integration.
- Always pass the correct
lineHeight(or follow the CSS-variable approach) when changing typography. - The component sets its container height to
lineHeight * size. If you clamp the container via CSS, ensure the scroll parent can actually scroll; otherwise virtualization can’t measure the viewport.
The repository ships a large Vitest suite (utilities, walkers, resolvers, components, integration scenarios).
npm test # run everything once
npm run test:watch # watch mode
npm run test:ui # launch Vitest UI
npm run test:coverageSee TESTING.md for coverage numbers, structure, and tips.
- Debug panels – Inspect Redux/Context refs without spamming console logs.
- API/LLM explorers – Visualize nested JSON or streaming responses with circular references.
- State machines & devtools – Pair with hot reloaders or feature flags to watch state change in real time.
- Data-heavy dashboards – Embed next to chart/table widgets so analysts can drill into raw payloads.
| Library | Scenario | Mean time* | Command |
|---|---|---|---|
| react-obj-view | Flatten ~100k-node payload (see bench/perf.bench.ts) |
23.7 ms (42.3 ops/s) | npx vitest bench bench/perf.bench.ts |
| react-obj-view | Flatten ~1M-node payload | 253 ms (4.0 ops/s) | npx vitest bench bench/perf.bench.ts |
| react-obj-view | Flatten ~2M-node payload | 525 ms (1.9 ops/s) | npx vitest bench bench/perf.bench.ts |
*Measured on macOS (Apple M3 Max, Node 22.11, Vitest 4.0.8). Each sample instantiates a fresh walkingToIndexFactory, generates 10k/100k/200k user records (~100k/~1M/~2M nodes total), and walks the tree. Adjust bench/perf.bench.ts to mirror your datasets if you need environment-specific numbers.
Third-party libraries aren’t benchmarked here; run their official examples under the same conditions for apples-to-apples comparisons.
- Usage Guide – end-to-end patterns, resolver recipes, styling guidance.
- API Documentation – deeper dive into props, hooks, and resolver authoring.
- Generic Tree Stack – explains
tree-core,react-tree-view, and the virtual-scroller for building custom viewers. - Object Tree Adapter & React Viewer – details how the built-in
ObjectViewcomposes the generic stack with resolvers. - Live demo – try grouping, previews, and change flashes in the browser.
git clone https://github.com/vothanhdat/react-obj-view
cd react-obj-view
yarn install
yarn devMIT © Vo Thanh Dat