mrkeyoor.com_
Wed 23 Sept 00:34 UTC
npmDataupdated 22 Sept 2026

rbush review

RBush 4.0.1 is an in-memory R-tree for two-dimensional, axis-aligned bounding boxes. Inserted objects carry minX, minY, maxX, and maxY, then search returns every box intersecting a query rectangle. The index also supports collision checks, bulk loading, identity or value-based removal, custom item shapes, and JSON transfer between processes. It does not calculate polygon intersection, distance, geographic wraparound, persistence, or synchronization. Version 4 made ESM the supported package format and dropped transpilation for IE11; 4.0.1 points the main entry at the ESM build and updates quickselect.

Verdict

RBush 4.0.1 installed in 0.8 seconds, used 1 MB on disk, and bundled to 2.3 KB gzipped in our sandbox, making it cheap for mutable rectangle intersection queries. Skip it for exact geometry, static-only points, or a runtime that cannot meet the package's ESM direction.

We installed it

Lab card: what happened when we installed rbushScreenshot of rbush documentation
Install✓ · 0.8s2 packages on disk · 1 MB
ImportESM import works · require() works · ESM package with exports map
Browser2.3 KBgzipped (5.7 KB minified), bundled with esbuild
Typesno TypeScript types found
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does rbush install cleanly?

Yes. In a fresh container with an empty cache, npm install rbush finished in 0.8s, leaving 2 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does rbush add to a browser bundle?

2.3 KB gzipped (5.7 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does rbush work with both ESM and CommonJS?

Yes. Both import 'rbush' and require('rbush') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does rbush include TypeScript types?

No type declarations were found in our install, so TypeScript users need their own declarations.

rbush or flatbush: which should you use?

flatbush: Use it for immutable rectangles when a compact packed index matters more than insert and remove. RBush 4.0.1 installed in 0.8 seconds, used 1 MB on disk, and bundled to 2.3 KB gzipped in our sandbox, making it cheap for mutable rectangle intersection queries.

When should you not use rbush?

The data is a fixed point set. RBush's README recommends KDBush and reports 5 to 8 times faster point indexing for that case.

API stability4/5The small API has kept new RBush, insert, load, search, collides, remove, clear, all, toJSON, and fromJSON since the 3.x constructor change. Version 4 made packaging the main break by publishing ESM and dropping the supported CommonJS path; 4.0.1 adjusted main for TypeScript-tool compatibility. Exported JSON also requires the same node size on import, so it is a transfer format tied to tree construction rather than a versionless data store.
Docs5/5The README documents coordinate shape, node-size tradeoffs, bulk-load benefits and failure cases, identity removal, custom subclasses, search semantics, JSON transfer, and the separate nearest-neighbor package. It also tells static-point users to choose KDBush and quantifies that recommendation. The main gap is input safety: callers must infer their own policy for invalid numbers, reversed boxes, coordinate systems, and hostile imported tree JSON.
Maintenance4/5The repository was pushed on July 21, 2026, is not archived, has 2,769 stars, and GitHub reports 12 open issues and pull requests. The latest npm release remains 4.0.1 from August 2024, when it fixed ESM entry compatibility for some TypeScript setups and updated quickselect. Low release frequency is reasonable for a compact algorithm with a settled API, though consumers should not expect frequent new spatial features.
Ecosystem4/5The npm endpoint counted 5,696,121 downloads in the latest week. Companion packages cover nearest-neighbor lookup, packed static trees, static points, and Turf GeoJSON wrappers. RBush also runs in Node and modern browsers, with a 2.3 KB gzipped namespace bundle in our measurement. Geographic primitives and persistence sit outside the core, and our installed-package scan found no TypeScript declarations despite type-related package metadata.

Use it if

  • A mutable map or canvas repeatedly asks which boxes touch a viewport, cursor, or selection rectangle.
  • The index should cheaply narrow candidates before a separate exact geometry test.
  • Items arrive and leave after the initial bulk build, so a static packed index is unsuitable.
  • A server-built tree must be serialized and searched later in a modern browser.
Skip it if

Setup reality

We installed rbush 4.0.1 in 0.8 seconds in a fresh Node 22 container. Two packages occupied 1 MB, and npm audit found 0 known vulnerabilities. RBush has one direct dependency, no peers, and 64 KB unpacked. It is ESM with an exports map. Both require() and ESM import worked on our Node version. Our scan found no TypeScript types.

There are no credentials or config files. The default item contract is minX, minY, maxX, and maxY; extra application fields stay attached. RBush does not reject NaN, infinite values, reversed bounds, mixed coordinate systems, or antimeridian crossing. Normalize every box before insertion. A search reports box intersection, so follow it with the real polygon or circle predicate when the shape is only approximated by its bounds.

Bulk load an empty tree for the initial dataset. The README says this path is 2 to 3 times faster than repeated insertion and yields searches 20 to 30 percent faster in its benchmark. Loading scattered batches into an existing tree can make queries worse because each batch becomes its own small tree before merging. The default node size is 9; larger nodes favor insertion and hurt search, so benchmark your distribution before changing it.

Removal compares object identity unless an equals function is supplied, and even value removal traverses using the copy's box. Remove an item before changing its coordinates, then insert it again. JSON export exposes the tree structure rather than only its records; fromJSON needs the same node size. Our browser build was 5.7 KB minified and 2.3 KB gzipped. Each process or worker owns a separate mutable index, so authoritative updates still need an application-level broadcast or rebuild.

Patterns

Index one application object insert-box

import RBush from 'rbush';

const tree = new RBush();
const marker = {
  id: 'm42',
  minX: 20,
  minY: 40,
  maxX: 30,
  maxY: 50,
};
tree.insert(marker);

RBush does not validate coordinates. Reject NaN and reversed min/max values before inserting the object.

Build the initial index in one load bulk-build-tree

const tree = new RBush(9);
tree.load(features.map((feature) => ({
  minX: feature.left,
  minY: feature.top,
  maxX: feature.right,
  maxY: feature.bottom,
  feature,
})));

Bulk loading an empty tree gives the best documented build and query behavior. Scattered batches loaded later can worsen searches.

Find boxes touching a viewport search-viewport

const visible = tree.search({
  minX: viewport.left,
  minY: viewport.top,
  maxX: viewport.right,
  maxY: viewport.bottom,
});

The result includes every intersecting box, including partial overlaps and geometric false positives.

Ask whether any box intersects check-collision

const occupied = tree.collides({
  minX: candidate.x,
  minY: candidate.y,
  maxX: candidate.x + candidate.width,
  maxY: candidate.y + candidate.height,
});

collides returns a Boolean and can stop at the first hit. It does not compute shape-level collision.

Remove the inserted object remove-reference

const item = { id: 7, minX: 0, minY: 0, maxX: 10, maxY: 10 };
tree.insert(item);

tree.remove(item);

Default removal uses reference identity. A new object with the same fields will not match.

Remove a reconstructed item by ID remove-by-id

const copy = { id: 7, minX: 0, minY: 0, maxX: 10, maxY: 10 };
tree.remove(copy, (a, b) => a.id === b.id);

The copy's bounds guide traversal before equals runs. If those coordinates changed, RBush may never visit the stored item.

Reindex an object after movement move-indexed-item

tree.remove(item);
item.minX += dx;
item.maxX += dx;
item.minY += dy;
item.maxY += dy;
tree.insert(item);

Mutating bounds while the item is indexed leaves stale ancestor boxes and can make later queries miss it.

Adapt point arrays with a subclass index-custom-shape

class PointBush extends RBush {
  toBBox([x, y]) { return { minX: x, minY: y, maxX: x, maxY: y }; }
  compareMinX(a, b) { return a[0] - b[0]; }
  compareMinY(a, b) { return a[1] - b[1]; }
}
const points = new PointBush().load([[20, 50], [10, 30]]);

Search still takes the standard bounding-box shape. Use KDBush when the point set never changes.

Serialize the built index export-tree

const nodeSize = 16;
const tree = new RBush(nodeSize).load(items);
const serialized = JSON.stringify({ nodeSize, data: tree.toJSON() });

Store nodeSize with the JSON. It must match the constructor used during restoration.

Restore an index snapshot import-tree

const snapshot = JSON.parse(serialized);
const tree = new RBush(snapshot.nodeSize).fromJSON(snapshot.data);
const matches = tree.search(query);

fromJSON trusts the tree shape. Validate or regenerate snapshots received across an untrusted boundary.

Read all records then reset clear-tree

const records = tree.all();
console.log(records.length);

tree.clear();
console.log(tree.all().length);

all() traverses the entire tree and allocates an array; it is not a constant-time count.

Measure a different maximum node size benchmark-node-size

for (const nodeSize of [9, 16, 32]) {
  const candidate = new RBush(nodeSize).load(items);
  const started = performance.now();
  for (const box of queries) candidate.search(box);
  console.log(nodeSize, performance.now() - started);
}

The default is 9. Larger nodes trade faster insertion for slower search, and the result depends on the real spatial distribution.

Alternatives

PackageRegistryPick it when
flatbushnpmUse it for immutable rectangles when a compact packed index matters more than insert and remove.
kdbushnpmUse it for a static point set with range and radius queries.
@turf/geojson-rbushnpmUse it when GeoJSON features and Turf bounding-box helpers are the native data model.
rbush-knnnpmAdd it to an existing RBush tree when nearest-neighbor results are also required.

More data guides

numpy · fsspec · pandas · sqlalchemy · pyarrow · lxml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.