import { FlowMap, resolveReferenceGeography } from "semiotic/geo"
FlowMap visualizes directed flows between geographic locations. Nodes sit at real-world coordinates; edges connect them with lines whose width encodes flow magnitude. Choose between great-circle arcs or straight lines. Add a world basemap via resolveReferenceGeography for geographic context.
Quick Start
Provide nodes (with lon/lat) and flows (with source, target, value). Load a world basemap asynchronously withresolveReferenceGeography("world-110m") and pass the resolved features as areas. Edges auto-resolve their endpoints from the node list.
Loading world map...
JSX
import { FlowMap, resolveReferenceGeography } from "semiotic/geo" function WorldFlowMap() { const [worldAreas, setWorldAreas] = useState(null) useEffect(() => { resolveReferenceGeography("world-110m").then(features => { setWorldAreas(features) }).catch(err => console.error("Failed to load world map:", err)) }, []) if (!worldAreas) return <div>Loading world map...</div> return ( <FlowMap nodes={[ { id: "JFK", name: "New York JFK", lon: -73.779, lat: 40.640 }, { id: "LHR", name: "London Heathrow", lon: -0.461, lat: 51.470 }, { id: "NRT", name: "Tokyo Narita", lon: 140.386, lat: 35.772 }, // ...more airports ]} flows={[ { source: "JFK", target: "LHR", passengers: 18000 }, { source: "LAX", target: "NRT", passengers: 14000 }, // ...more routes ]} areas={worldAreas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc", strokeWidth: 0.5 }} valueAccessor="passengers" tooltip /> ) }
Examples
US Domestic Routes
The same component works at any scale. Here we show US domestic flights with the world basemap providing geographic context. Setzoomable to enable pan and zoom on non-tile maps.
Loading world map...
JSX
<FlowMap nodes={usAirports} flows={domesticFlights} areas={worldAreas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc", strokeWidth: 0.5 }} valueAccessor="passengers" projection="albersUsa" tooltip />
Colored Edges
Use edgeColorBy to color flows by a field, such as the source airport. With a world basemap, patterns in hub connectivity become immediately visible.
Loading world map...
JSX
<FlowMap nodes={airports} flows={flights} areas={worldAreas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc", strokeWidth: 0.5 }} valueAccessor="passengers" edgeColorBy="source" showLegend tooltip />
Straight Lines
Set lineType="line" for straight connections instead of great-circle arcs. This can be useful for schematic maps where geographic accuracy is less important than clarity.
Loading world map...
JSX
<FlowMap nodes={airports} flows={flights} areas={worldAreas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc", strokeWidth: 0.5 }} valueAccessor="passengers" lineType="line" tooltip />
Global Trade Routes
FlowMap handles worldwide data with the default Equal Earth projection. Great-circle arcs naturally follow the shortest path on the globe. Here we show major container shipping routes between world ports.
Loading world map...
JSX
<FlowMap nodes={[ { id: "Shanghai", lon: 121.474, lat: 31.230 }, { id: "Rotterdam", lon: 4.480, lat: 51.924 }, { id: "Singapore", lon: 103.851, lat: 1.290 }, // ...more ports ]} flows={[ { source: "Shanghai", target: "Los Angeles", value: 85 }, { source: "Shanghai", target: "Rotterdam", value: 72 }, // ...more routes ]} areas={worldAreas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc", strokeWidth: 0.5 }} edgeColorBy="source" showLegend tooltip />
Animated Particles
Add showParticles to animate dots flowing along each route, conveying directionality and volume. Customize appearance with particleStyle — set color to"source" to inherit each line's stroke color.
Loading world map...
JSX
<FlowMap nodes={airports} flows={flights} areas={worldAreas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc", strokeWidth: 0.5 }} valueAccessor="passengers" edgeColorBy="source" edgeOpacity={0.5} showParticles particleStyle={{ radius: 2, color: "source", speedMultiplier: 1.5 }} tooltip />
Arc Flow Style
Set flowStyle="arc" to render curved arcs that bulge perpendicular to the flow direction. This makes overlapping routes easier to distinguish and adds a visual sense of motion. Particles follow the arc path automatically.
Loading world map...
JSX
<FlowMap nodes={airports} flows={flights} areas={worldAreas} valueAccessor="passengers" edgeColorBy="source" flowStyle="arc" showParticles particleStyle={{ radius: 2, color: "source", speedMultiplier: 1.2 }} tooltip />
Offset Flow Style (Bidirectional)
Set flowStyle="offset" to draw each flow slightly offset from the direct centerline between its source and target. This separation makes overlapping routes easier to distinguish, especially when traffic exists in both directions (e.g. JFK→LHR and LHR→JFK).
Loading world map...
JSX
<FlowMap nodes={airports} flows={flights} areas={worldAreas} valueAccessor="passengers" edgeColorBy="source" flowStyle="offset" tooltip />
Props
| Prop | Type | Required | Default | Description |
|---|
nodes | array | Yes | — | Array of node objects with geographic coordinates. |
flows | array | Yes | — | Array of flow objects with source, target, and optional value fields. |
nodeIdAccessor | string | — | "id" | Field name to identify nodes. |
xAccessor | string | function | — | "lon" | Longitude accessor. |
yAccessor | string | function | — | "lat" | Latitude accessor. |
valueAccessor | string | — | "value" | Field on flows for edge width scaling. |
edgeWidthRange | [number, number] | — | [1, 8] | Min and max edge stroke width. |
lineType | "geo" | "line" | — | "geo" | "geo" for great-circle arcs, "line" for straight lines. |
flowStyle | "basic" | "offset" | "arc" | — | "basic" | Flow rendering style. "basic" draws straight or great-circle lines. "offset" shifts bidirectional flows apart so A→B and B→A don't overlap. "arc" renders curved arcs that bulge perpendicular to the flow direction. |
edgeColorBy | string | function | — | — | Field or function for edge color. |
edgeOpacity | number | — | 0.6 | Opacity applied to flow lines. |
colorScheme | string | array | — | "category10" | Color scheme for edges. |
projection | string | object | function | — | "equalEarth" | Map projection. |
areas | GeoJSON.Feature[] | — | — | Optional background geography (e.g. from resolveReferenceGeography). |
areaStyle | object | — | — | Style object applied to background geography areas. |
zoomable | boolean | — | true with tileURL, false otherwise | Enable pan and zoom interaction on the map. Defaults to true when tileURL is set. |
tooltip | boolean | function | object | — | — | Tooltip configuration. |
showLegend | boolean | — | false | Show a legend. |
title | string | — | — | Chart title. |
width | number | — | 600 | Chart width. |
height | number | — | 400 | Chart height. |
showParticles | boolean | — | false | Animate dots flowing along each route to convey directionality. |
particleStyle | object | — | — | Particle appearance: { radius, color, opacity, speedMultiplier, maxPerLine, spawnRate }. Set color to "source" to inherit each line's stroke. |
frameProps | object | — | — | Additional StreamGeoFrame props. |
Graduating to the Frame
For streaming flows, animated particles, or custom line rendering, useStreamGeoFrame directly.
Chart (simple)
JSX
import { FlowMap, resolveReferenceGeography } from "semiotic/geo" const [areas, setAreas] = useState(null) useEffect(() => { resolveReferenceGeography("world-110m") .then(setAreas) .catch(err => console.error( "Failed to load world map:", err)) }, []) <FlowMap nodes={airports} flows={flights} areas={areas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc", strokeWidth: 0.5 }} valueAccessor="passengers" lineType="geo" tooltip />
Frame (full control)
JSX
import { StreamGeoFrame, resolveReferenceGeography } from "semiotic/geo" <StreamGeoFrame projection="equalEarth" areas={worldAreas} areaStyle={{ fill: "#f0f0f0", stroke: "#ccc" }} points={airports} lines={flowsWithCoordinates} lineDataAccessor="coordinates" lineType="geo" xAccessor="lon" yAccessor="lat" lineStyle={(d) => ({ stroke: colorScale(d.source), strokeWidth: widthScale(d.value), strokeOpacity: 0.6, })} size={[600, 400]} />