Description:
react-native-better-clustering is a React Native map component that groups large marker sets with a C++ Supercluster engine on iOS and Android.
It keeps clustering work off the JavaScript and React Native bridge while preserving the familiar react-native-maps map and marker model.
Features
- Clusters map markers with a C++ Supercluster engine.
- Moves clustering computation away from the JavaScript and React Native bridge.
- Keeps the react-native-maps MapView and Marker programming model.
- Updates clusters during region changes with a configurable interval.
- Supports cluster press callbacks and marker change callbacks.
- Renders custom clusters with access to point counts and expansion regions.
- Spiders overlapping markers with configurable lines and colors.
- Lets individual markers opt out with cluster={false}.
Preview
How To Use It
Install Bare React Native Dependencies
Install the package and its native peers:
npm install react-native-better-clustering react-native-nitro-modules react-native-maps react-native-reanimated react-native-worklets
cd ios
pod install
cd ..Rebuild the native application after installation. Add the Worklets Babel plugin as the last plugin in a bare React Native project:
// babel.config.js
module.exports = {
presets: ["module:@react-native/babel-preset"],
plugins: [
// Keep the Worklets plugin last.
"react-native-worklets/plugin",
],
};Configure an Expo Development Build
Expo projects can install the same native dependencies with:
npx expo install react-native-better-clustering react-native-nitro-modules react-native-maps react-native-reanimated react-native-workletsThe Expo Babel preset handles the Worklets plugin. Add the react-native-maps configuration to app.json when Google Maps on Android needs an API key:
{
"expo": {
"plugins": [
[
"react-native-maps",
{
"googleMapsApiKey": "YOUR_GOOGLE_MAPS_API_KEY"
}
]
]
}
}Run the native prebuild and create a development build:
npx expo prebuild --clean
npx expo run:ios
# Or use npx expo run:androidRender a Clustered Map
Import the default MapView from the package and Marker from react-native-maps. The map accepts standard react-native-maps props plus clustering options.
import { StyleSheet, View } from "react-native";
import MapView from "react-native-better-clustering";
import { Marker } from "react-native-maps";
const initialRegion = {
latitude: 40.7128,
longitude: -74.006,
latitudeDelta: 0.35,
longitudeDelta: 0.35,
};
const places = [
{ id: "museum", title: "Museum", latitude: 40.7794, longitude: -73.9632 },
{ id: "market", title: "Market", latitude: 40.706, longitude: -74.0086 },
];
export default function PlacesMap() {
return (
<View style={styles.container}>
<MapView
style={styles.map}
initialRegion={initialRegion}
radius={50}
minPoints={2}
clusterColor="#0F52FF"
clusterTextColor="#FFFFFF"
onClusterPress={(cluster, markers) => {
// Use the cluster and its markers to open a detail panel or fit the map.
console.log("Cluster selected", cluster, markers.length);
}}
onMarkersChange={(markers) => {
// Store or summarize the markers currently visible in the viewport.
console.log("Visible markers", markers.length);
}}
>
{places.map((place) => (
<Marker
key={place.id}
identifier={place.id}
coordinate={{
latitude: place.latitude,
longitude: place.longitude,
}}
title={place.title}
/>
))}
</MapView>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1 },
map: { flex: 1 },
});onMarkersChange receives the markers in the current visible cluster state. Keep marker components memoized and give each marker a stable identifier when a large map updates frequently.
Exclude a Marker From Clustering
Set cluster={false} on a marker that should stay visible as an individual annotation:
<Marker
cluster={false}
identifier="user-location"
coordinate={{ latitude: 40.7128, longitude: -74.006 }}
title="Your location"
/>Configure Cluster Interaction
Use onClusterPress to respond to a selected cluster. The callback receives the cluster object and its marker list. The cluster object exposes point_count, point_count_abbreviated, and getExpansionRegion() for custom detail or zoom behavior.
The default press flow can be changed with preserveClusterPressBehavior. Use edgePadding when a fit-to-cluster action needs room around the map edges. Set selectedClusterId and selectedClusterColor when the selected cluster needs a distinct color.
Use a Custom Cluster Renderer
Pass renderCluster when the default cluster marker does not match the map design. The documented RenderClusterProps type supplies the cluster data and the press handler needed by a custom marker. Read point_count or point_count_abbreviated from the cluster object and call the provided press handler from the custom marker.
API Reference
Clustering Options
| Prop | Default or type | Purpose |
|---|---|---|
| radius | About 6% of screen width | Sets the cluster radius. |
| minPoints | 2 | Sets the minimum points needed for a cluster. |
| minZoom | 1 | Sets the minimum clustering zoom. |
| maxZoom | 20 | Sets the maximum clustering zoom. |
| extent | 512 | Sets the tile extent used by Supercluster. |
| nodeSize | 64 | Sets the Supercluster node size. |
| clusteringEnabled | true | Turns clustering on or off. |
| spiralEnabled | true | Controls the spiral layout for overlapping markers. |
| clusterUpdateIntervalMs | 100 | Sets the update interval during region changes. A value of 0 updates on settle. |
| tracksViewChanges | false | Controls marker view tracking. |
| renderCluster | RenderClusterProps renderer | Renders a custom cluster marker. |
Cluster Styling and Callbacks
| Prop | Purpose |
|---|---|
| clusterColor | Sets the default cluster color. |
| clusterTextColor | Sets cluster text color. |
| clusterFontFamily | Sets the cluster font family. |
| spiderLineColor | Sets the line color for spider layouts. |
| selectedClusterId | Identifies a selected cluster. |
| selectedClusterColor | Sets the selected cluster color. |
| onClusterPress | Receives a cluster and its marker list. |
| onMarkersChange | Receives the markers in the current visible state. |
| onRegionChangeComplete | Receives the region, change details, and current markers. |
Map and Performance Options
The component accepts standard react-native-maps MapView props. The additional documented options include:
- animationEnabled for iOS cluster transition animation.
- layoutAnimationConf for create, delete, and scale update animations.
- clusterFadeInDuration for cluster fade-in timing.
- edgePadding for cluster expansion fitting.
- width and height for map dimensions used by clustering calculations.
- mapRef and superClusterRef for external references.
- maxZoom and spiralEnabled for map edge and overlap behavior.
Advanced Exports
- react-native-better-clustering/hooks exports useClusterer.
- react-native-better-clustering/clusterer exports Clusterer for declarative rendering.
- react-native-better-clustering/engine exports Supercluster, createClusterEngine, and geometry helpers.
- react-native-better-clustering/geojson exports GeoJSON helpers.
- react-native-better-clustering/utils exports utility functions.
- react-native-better-clustering/compat provides the compatibility alias documented by the project.
Migration From react-native-map-clustering
The project documents a small migration path:
// Before
import MapView from "react-native-map-clustering";
// After
import MapView from "react-native-better-clustering";Existing Marker children and cluster props remain part of the migration model. Review the native setup requirements before rebuilding the application.





