Mesh connections
The devices of a private network and how this device reaches each one, directly or through a relay; upgrades let go of the relay with a wobble.
Your data
Pass this device as device, the other devices as peers, your relay servers as relays and the current path to each peer as paths. Without peers and paths the component shows a demo laptop in Amsterdam with 6 demo peers, 3 demo relays and invented latencies, and plays a seeded sequence of upgrades and fallbacks. See Your data.
Playground
Change a prop and the component re-renders. Props marked remounts set an initial value, so the component starts over.
<GeoMesh live label="Mesh connections" />
Installation
pnpm dlx shadcn@latest add @lumesec/geo-meshFirst timeRegister the @lumesec registry once, or install by URL+
Adds @lumesec to your components.json:
pnpm dlx shadcn@latest registry add @lumesec=https://elements.lumesec.ai/r/{name}.jsonOr skip the setup and install by URL:
npx shadcn@latest add https://elements.lumesec.ai/r/geo-mesh.jsonUsage
"use client";
import * as React from "react";
import { GeoMesh, type MeshDevice, type MeshPath, type MeshRelay } from "@/components/lumesec/geo-mesh";
const device: MeshDevice = { id: "laptop", label: "Work laptop", location: "Amsterdam", lat: 52.37, lon: 4.9 };
const peers: MeshDevice[] = [
{ id: "build", label: "Build server", location: "Milan", lat: 45.46, lon: 9.19 },
{ id: "desktop", label: "Desktop", location: "Toronto", lat: 43.65, lon: -79.38 },
];
const relays: MeshRelay[] = [
{ id: "lhr", label: "London", lat: 51.51, lon: -0.13 },
{ id: "iad", label: "Ashburn", lat: 39.04, lon: -77.49 },
];
export function Example({ paths }: { paths: MeshPath[] }) {
const [peer, setPeer] = React.useState<string | null>(null);
return <GeoMesh device={device} peers={peers} relays={relays} paths={paths} value={peer} onValueChange={setPeer} />;
}Behaviour
The devices of a private network and how this device reaches each of them: directly, or through a relay server when a direct path cannot be opened. Relayed connections run as two legs through the relay; direct ones as a single line, with traffic moving both ways.
When a relayed connection becomes direct, two probes meet in the middle, the path lets go of the relay and straightens with a slight wobble, and the latency rolls down to the new value. A connection that falls back to a relay bends back through it.
API reference
Props
Also accepts every prop of <div> (React.ComponentProps<"div">), spread onto the root element.
deviceTypeMeshDeviceDefaultdemo laptopThis device: { id, label, location?, lat, lon }. Every path starts here, and the header names it. The demo laptop in Amsterdam is used only while peers and paths are not given; without a device no paths are drawn.
peersTypereadonly MeshDevice[]Default6 demo peersThe other devices: { id, label, location?, lat, lon }. location is shown under the name and read in the row's accessible name. Peers with a repeated id, the device's id or a non-finite coordinate are skipped.
relaysTypereadonly MeshRelay[]Default3 demo relaysRelay servers: { id, label, lat, lon }. The id is shown on the map and in the row chip (via lhr), the label in the detail line (Relayed through London). The demo relays are used only while peers and paths are not given.
pathsTypereadonly MeshPath[]The current path to each peer: { peer, relay: string | null, ms, traffic? }. relay: null is a direct connection; ms is the round-trip latency; traffic from 0 to 1 sets the packet rate. A peer without a path is offline. When a path changes, the map animates the change.
valueTypestring | nullSelected peer id (controlled), or null for none. Use with onValueChange.
defaultValueTypestring | nullDefaultnullSelected peer when uncontrolled.
onValueChangeType(value: string | null) => voidCalled with the peer id when a peer is selected, and with null when the selection is cleared.
liveTypebooleanDefaulttrueRuns the demo upgrades and fallbacks while the map is on screen. Has no effect when peers or paths is given.
labelTypestringDefault"Mesh connections"Accessible name of the map. Its aria-label adds the device name and the number of direct, relayed and offline peers.
Ref
ref points at the root HTMLDivElement.
Keyboard
| Keys | Action |
|---|---|
| Tab | Moves to the device list. The rows share one tab stop. |
| ArrowUp / ArrowDown | Moves focus one line up or down in the list, which has one to three columns depending on its width. |
| ArrowLeft / ArrowRight | Moves focus to the previous or next device. |
| Home / End | Moves focus to the first or last device. |
| Enter / Space | Selects the focused device, or clears the selection when it is already selected. |
| Escape | Clears the selection. |
Accessibility
- The peers are a
ullabelled "Devices" ofbuttonrows witharia-pressedand one roving tab stop. Each row's accessible name reads like "Build server, Frankfurt, direct, 9 milliseconds", "NAS, Dublin, relayed through London, 20 milliseconds" or "Phone, Madrid, offline". - The map has
role="img"and anaria-labelsuch as "Mesh connections from Work laptop: 5 direct, 1 relayed." Its markers arearia-hidden; a click or tap within 14 px of a peer marker selects that peer for pointer users, and a click on the empty map clears the selection. - A polite live region announces path changes such as "Build server is now direct, 13 ms." at most every 5 s. Changes in between are collected, and the latest three are read together.
- Every name, count, latency and relay id is DOM text. The canvases are
aria-hidden. - Reduced motion: there are no probes, flash, wobble, sparks, selection pulse or packets: a changed path crossfades from its old to its new shape over 150 ms, and the counts, chips and latencies swap without rolling.
Theming
Styled with Tailwind classes on your shadcn theme tokens, so light and dark follow your theme. The accent comes from --lumesec. See Theming.
This component reads --lumesec, --lumesec-glint, --lumesec-shine, --lumesec-info, --lumesec-warning, --foreground, --muted-foreground, --muted, --card and --border.
Notes
- A relayed connection runs as two legs through its relay, drawn as grey dashes (two dots on, one off) with a gap around the relay square. A direct connection is a single line of accent dots. Every path starts at this device and bulges upward like a flight arc.
- Packets run both ways on every path: outbound in
--lumesec, inbound in--lumesec-info, at 0.5 + 2.5 ×trafficper second in each direction, with at most 160 in flight. - When a relayed path becomes direct, two probes run toward each other along the direct line (at most 0.45 s). Where they meet the dot grid flashes in an uneven ring, the path lets go of the relay on an under-damped spring that overshoots by about 18 % and settles in about 0.7 s, and 12 sparks leave the relay in the direction the path pulled away. The row's chip and latency roll to the new values at that moment; until then the list keeps the relayed values.
- When a direct path falls back, it bends back through its relay without overshoot and is tinted
--lumesec-warningfor 1.2 s; the row's chip flashes in the same colour. A change of relay blends between the two relayed shapes. - Selecting a peer keeps its path and packets at full strength, dims the other paths to 0.3 and stops their packets, sends a short pulse from this device to the peer and names it on the map. The line under the map then reads like "Direct, 9 ms" or "Relayed through London, 32 ms"; with nothing selected it lists the relays.
- Peers missing from
pathsare offline: a hollow dot, no path, anofflinechip and no latency. Arelayid that is not inrelaysshows asvia <id>and its path is drawn in the relayed style along the direct line. With no peers the list reads "No other devices". - The map uses the Equal Earth projection centred on this device's meridian, cropped to 50° S to 72° N and zoomed to fit every device, relay and possible path (up to 6 times), so the view stays put while paths change. Its height follows its width; set only the width. Below 420 px the markers get smaller and the relay names leave the line under the map.
- The animation runs only while the map is on screen. Colours follow the theme and are re-read when it changes.
Related
Was this page helpful?