Skip to main content

React globe component

CanvasGlobe provides a React component for interactive globes and flat world maps without Three.js, WebGL, map tiles, or an API key.

npm install react-canvas-globe canvas-globe react

react-canvas-globe is the official React package. It uses the same renderer, options, types, and commercial license as canvas-globe.

The component​

import { CanvasGlobe } from "react-canvas-globe";

export function Visitors({ markers }) {
return <CanvasGlobe markers={markers} preset="hologram" tooltip />;
}

Every option is a prop. The component diffs them shallowly on each render and calls setOptions with only what changed, so you can pass inline objects freely.

Sizing​

The canvas defaults to width: 100% with an aspectRatio derived from the mode and projection. Override with style:

<Globe mode="map" style={{ maxWidth: 720, aspectRatio: "360 / 139" }} />

Imperative calls​

The forwarded ref is the GeoGlobe instance.

import { useRef } from "react";
import { CanvasGlobe } from "react-canvas-globe";

export function Explorer({ markers }) {
const globe = useRef(null);

return (
<>
<CanvasGlobe
ref={globe}
markers={markers}
tooltip
onClick={(m) => globe.current.flyTo(m.lon, m.lat, { zoom: 3 })}
/>
<button onClick={() => globe.current.fitToMarkers()}>Fit all</button>
<button onClick={() => globe.current.exportImage({ preset: "og" })}>Export</button>
</>
);
}

Callbacks are always fresh​

onHover, onClick, onCountryHover, onCountryClick and onRender are read through a ref, so they never need to be memoised and never cause a rebuild.

<CanvasGlobe onClick={(m) => setSelected(m)} />

Keeping expensive props stable​

Options are diffed by identity. A new array every render means setOptions fires every render.

const arcs = useMemo(() => routes.map(toArc), [routes]);

<CanvasGlobe markers={markers} arcs={arcs} />;

This matters most for arcs, whose great-circle points are cached per object: see Performance.

Cleanup​

The component destroys its instance on unmount. Nothing to do.

Next.js and SSR​

The modules are safe to import on the server because they do not touch the DOM until construction. The canvas still needs a browser. In the App Router, mark the file:

"use client";

import { CanvasGlobe } from "react-canvas-globe";

Or load it lazily to keep it out of the server bundle entirely:

import dynamic from "next/dynamic";

const Globe = dynamic(
() => import("react-canvas-globe").then((m) => m.CanvasGlobe),
{ ssr: false, loading: () => <div style={{ aspectRatio: 1 }} /> },
);

TypeScript​

import { useRef } from "react";
import { CanvasGlobe } from "react-canvas-globe";
import type { GeoGlobe, Marker } from "canvas-globe";

const globe = useRef<GeoGlobe | null>(null);
const markers: Marker[] = [{ lat: 23.03, lon: 72.58, count: 12 }];

<CanvasGlobe ref={globe} markers={markers} />;

A complete example​

"use client";

import { useMemo, useRef, useState } from "react";
import { CanvasGlobe } from "react-canvas-globe";
import { fromCSV } from "canvas-globe";

export function SignupMap({ csv }) {
const globe = useRef(null);
const [selected, setSelected] = useState(null);
const markers = useMemo(() => fromCSV(csv), [csv]);

return (
<figure>
<CanvasGlobe
ref={globe}
scene="signups"
markers={markers}
counter={{ value: markers.length, label: "customers" }}
tooltip={(m) => `${m.label}: ${m.count}`}
onClick={(m) => {
setSelected(m);
globe.current.flyTo(m.lon, m.lat, { zoom: 3 });
}}
style={{ maxWidth: 560 }}
/>
<figcaption>{selected ? `${selected.label}: ${selected.count}` : "Click a city"}</figcaption>
</figure>
);
}

Talk to a real person

Live human support. Choose what you need help with.