Supercluster: fast geospatial point clustering in JavaScript, and when it is the wrong tool
A very fast geospatial point clustering library for browsers and Node.
At a glance
- What is it?
- Supercluster is a JavaScript library for clustering large sets of GeoJSON points for browsers and Node. Its strength is the precomputed index; its constraint is that the index is immutable once loaded.
- Who is it for?
- Use Supercluster when you have a large, mostly static set of GeoJSON points and need cluster queries per bounding box and zoom, in a browser or in Node. Do not use it when your points change frequently, since the README states the index is immutable once loaded, or when you need server-side spatial queries beyond clustering.
- Can I use it commercially?
- Yes. ISC is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 27 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem Supercluster solves, and who it is for
Rendering millions of individual markers on a map is not workable. The browser has to draw each one, hit-testing becomes slow, and at low zoom levels the markers overlap into a solid mass. Supercluster addresses this by grouping nearby points into clusters before they reach the map, so the renderer draws a small number of cluster features instead of the full set. The README describes it as "a very fast JavaScript library for geospatial point clustering for browsers and Node" and states it was built to power clustering in Mapbox GL JS. The intended user is a JavaScript developer who already has points in GeoJSON form and needs to show them on an interactive map at multiple zoom levels. The demo directory contains an index.html, index.js and a worker.js, which suggests the maintainers expect the library to be run inside a Web Worker so the main thread stays responsive. That detail matters: clustering a multi-million point dataset on the main thread will block rendering, and the repository layout shows the intended pattern.
How the index works: load once, query by bbox and zoom
The mechanism is a precomputed spatial index. You construct a Supercluster instance with options, call load(points) with an array of GeoJSON Feature objects, and then query it. The README states that each feature's geometry must be a GeoJSON Point or MultiPoint, and that a MultiPoint is clustered as an individual point per coordinate, each inheriting the feature's properties and id. After load, the README says the index is immutable. Queries come in two shapes. getClusters(bbox, zoom) takes a bounding box array in the order [westLng, southLat, eastLng, northLat] and an integer zoom, and returns an array of clusters and points as GeoJSON Features. getTile(z, x, y) returns a geojson-vt-compatible JSON tile object with cluster and point features, or null where there is no data. There is also getTileRaw(z, x, y), which the README describes as the same tile with each feature flat, shaped like {type: 4, x, y, tags} with coords inline rather than wrapped in a nested geometry array. For interaction, getChildren(clusterId) returns the children of a cluster on the next zoom level, getLeaves(clusterId, limit, offset) returns the points inside a cluster with pagination, and getClusterExpansionZoom(clusterId) returns the zoom at which a cluster splits into several children, which is what a click-to-zoom handler needs. The underlying spatial structure is a KD-tree: the package depends on kdbush, and the nodeSize option controls the size of the KD-tree leaf node, which the README says affects performance.
Installing Supercluster and running a first query
Install with npm or Yarn. The README gives both commands. The package.json declares "type": "module" and an exports field pointing at index.js, so the ES module import is the supported entry point in Node.
npm install superclusteryarn add superclusterOnce installed, import the default export and construct an index. The README's opening example sets radius to 40 and maxZoom to 16, then loads points and queries a world bounding box at zoom 2. The returned value is an array of GeoJSON Features, and each feature's properties carry the cluster_id value you pass to getChildren, getLeaves or getClusterExpansionZoom.
import Supercluster from 'supercluster';
const index = new Supercluster({radius: 40, maxZoom: 16});
index.load(points);
const clusters = index.getClusters([-180, -85, 180, 85], 2);In the browser, the README offers two paths. You can import from a CDN as an ES module, or use an ordinary script tag pointing at the minified build. The script tag example in the README pins version 8.0.0 on unpkg, which is older than the current 9.1.0 release; if you use the script tag route, pick the version you actually want to ship.
<script src="https://unpkg.com/[email protected]/dist/supercluster.min.js"></script>Aggregating properties with map and reduce
Clusters can carry aggregated values, not just counts. The README documents two options: map, a function returning cluster properties for a single point, and reduce, a function merging the properties of two clusters. The documented example builds a sum property that accumulates a myValue field. Two conditions are stated explicitly and both are easy to violate. First, map must return a new object rather than existing properties of a point, otherwise it will get overwritten. Second, reduce must not mutate its second argument. These are not stylistic preferences; the README presents them as requirements for correct operation, and a reduce that mutates its input will produce wrong aggregates that are hard to trace back to the source.
const index = new Supercluster({
map: (props) => ({sum: props.myValue}),
reduce: (accumulated, props) => { accumulated.sum += props.sum; }
});The immutability constraint is the main limitation
The README states plainly that once loaded, the index is immutable. There is no documented add, remove or update method. If a point moves, or a new point arrives, you rebuild the index and call load again with the full array. For a dataset of a few thousand points that is fine. For millions of points that change continuously, such as live vehicle positions, the rebuild cost lands on every update cycle, and the library gives you no incremental path around it. This is the case where Supercluster is the wrong tool. A second constraint is the geometry requirement: input must be Point or MultiPoint. Polygon or LineString features need to be reduced to representative points before loading, and the README does not discuss that conversion. A third is the maxZoom cap of 30, which the README notes in the options table alongside the default of 16. If your map goes deeper than 30, clustering stops being generated at that level. Finally, the README does not document rollback or version migration behaviour, so pinning a version is the only documented way to control upgrades.
How Supercluster compares with server-side clustering
The obvious alternative is to cluster on the server and send pre-clustered tiles to the client. PostGIS with ST_ClusterDBSCAN or a tile server built on it takes that approach. The difference is where the index lives and what it costs. Supercluster builds its index in JavaScript memory, in the browser or in Node, and answers queries locally with no network round trip per pan or zoom. A server-side approach keeps the authoritative dataset in a database, so points can change without rebuilding a client index, but every viewport change becomes a request. Supercluster's getTile method exists precisely so the library can slot into a tile-serving pipeline when you do want that shape, which means the two approaches are not mutually exclusive. The choice comes down to update frequency: static or slowly changing data favours the in-memory index, and data that changes per second favours the database.
Maintenance status, licence and upgrade cost
The repository is not archived, and the last push was on 2026-09-03, the same date as the v9.1.0 release. The release history shows a long gap: v8.0.1 was published on 2023-04-27, and v9.0.0 arrived on 2026-08-10. That pattern suggests the library is stable rather than rapidly changing, and the v9 line brought a breaking change. The README's TypeScript section says type declarations now ship with the library, that you should remove @types/supercluster if you have it, and that types previously exposed as namespace members are now named exports. The example imports Options, PointFeature, ClusterFeature, Tile and RawTile as named type imports from supercluster. If you are upgrading from v8, that import change is the first thing to check. The licence is ISC, a permissive licence, and package.json declares it under the license field. ISC is short and permissive, similar in effect to MIT, but this is not legal advice; read the LICENSE file in the repository before distributing the library in a product.
Editorial conclusion
Use Supercluster when you have a large, mostly static set of GeoJSON points and need cluster queries per bounding box and zoom, in a browser or in Node. Do not use it when your points change frequently, since the README states the index is immutable once loaded, or when you need server-side spatial queries beyond clustering. Before adopting, verify the options you depend on against the README table, confirm your input is Point or MultiPoint geometry, and check the ISC licence terms for your distribution.
Frequently asked questions
How do I install Supercluster?
Install it with npm install supercluster or yarn add supercluster. In Node you import the default export from 'supercluster'; in the browser the README shows either a CDN ES module import or a script tag pointing at the minified build.
What input format does Supercluster expect?
The README states that load takes an array of GeoJSON Feature objects, and each feature's geometry must be a GeoJSON Point or MultiPoint. A MultiPoint is clustered as an individual point per coordinate, each inheriting the feature's properties and id.
Can I add or remove points after loading them into Supercluster?
No. The README states that once loaded, the index is immutable. There is no documented method for adding, removing or updating individual points, so changed data means calling load again with the full array.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mapbox-supercluster)