Skip to main content
Version: 6.0

Using React

Mappedin SDK version 6 is currently in a beta state while Mappedin perfects new features and APIs. Open the v6 release notes to view the latest changes.
Using Mappedin JS with your own map requires a Pro license. Try a demo map for free or refer to the Pricing page for more information.

Mappedin publishes a version of the JS package which is compatible with React applications. The package can be found here on NPM @mappedin/react-sdk.

The Mappedin React SDK exports convenient JSX components to render and manipulate the 3D map. Additionally, everything included in the @mappedin/mappedin-js is exported from the React SDK under the default export.

Local Development

For local development, start a project using Vite. Refer to the Vite Getting Started guide for setup details. Guides are written in TypeScript (JavaScript), as the SDK is written in Typescript and uses comprehensive types.

1. Create a Project

Run these shell commands to set up a new project and install Mappedin JS with React.

With Yarn

yarn create vite mappedin-quickstart --template react-ts
cd mappedin-quickstart
yarn add @mappedin/react-sdk@beta

With NPM

npm create vite@latest mappedin-quickstart -- --template react-ts
cd mappedin-quickstart
npm add @mappedin/react-sdk@beta

2. Update index.html, App.tsx

Modify & update the contents of index.html to match the following.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Mappedin JS v6 Getting Started</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
width: 100%;
height: 100%;
}
#root {
height: 100%;
width: 100%;
position: relative;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

Modify & update the contents of App.tsx file under the src directory to match the following.

import React from 'react';
import { MapView, useMapData, useMap, Label } from '@mappedin/react-sdk';
import '@mappedin/react-sdk/lib/esm/index.css';

function MyCustomComponent() {
const { mapData } = useMap();

return mapData.getByType('space').map((space) => {
return <Label target={space.center} text={space.name} />;
});
}

export default function App() {
// See Demo API key Terms and Conditions
// https://developer.mappedin.com/v6/demo-keys-and-maps/
const { isLoading, error, mapData } = useMapData({
key: 'mik_yeBk0Vf0nNJtpesfu560e07e5',
secret: 'mis_2g9ST8ZcSFb5R9fPnsvYhrX3RyRwPtDGbMGweCYKEq385431022',
mapId: '65c0ff7430b94e3fabd5bb8c',
});

if (isLoading) {
return <div>Loading...</div>;
}

if (error) {
return <div>{error.message}</div>;
}

return mapData ? (
<MapView mapData={mapData}>
<MyCustomComponent />
</MapView>
) : null;
}

3. Run the Project

To run the demo (with hotloading), use the following command:

With Yarn

yarn run dev

With NPM

npm run dev

This should result in a prompt showing the project being served at http://127.0.0.1:5173 (default port). The 3D rendered map can be zoomed, panned and rotated via mouse or fingers.

MapView

The MapView component contains the DOM element of the 3D map and the context wrapper for control of the map. Components which reference the 3D map must be children of the MapView component to have access to the context. The Mappedin CSS must be imported alongside the MapView.

import React from 'react';
import { MapView, useMapData, useMap, Label } from '@mappedin/react-sdk';
import '@mappedin/react-sdk/lib/esm/index.css';

function MyCustomComponent() {
const { mapView, mapData } = useMap();

return mapData.getByType('space').map((space) => {
return <Label target={space.center} text={space.name} />;
});
}

export default function App() {
// See Demo API key Terms and Conditions
// https://developer.mappedin.com/v6/demo-keys-and-maps/
const { isLoading, error, mapData } = useMapData({
key: 'mik_yeBk0Vf0nNJtpesfu560e07e5',
secret: 'mis_2g9ST8ZcSFb5R9fPnsvYhrX3RyRwPtDGbMGweCYKEq385431022',
mapId: '65c0ff7430b94e3fabd5bb8c',
});

if (isLoading) {
return <div>Loading...</div>;
}

if (error) {
return <div>{error.message}</div>;
}

return mapData ? (
<MapView mapData={mapData} style={{ width: '650px', height: '650px' }}>
<MyCustomComponent />
</MapView>
) : null;
}

useMapData and useMap

The useMapData hook fetches and hydrates new Map Data from Mappedin servers. It is recommended to only do this once at the beginning of your application to limit the amount of network requests for the user.

It returns an object containing isLoading, error, and mapData. These can be used to monitor the state of the fetch and display feedback for the user.

// See Demo API key Terms and Conditions
// https://developer.mappedin.com/v6/demo-keys-and-maps/
const { isLoading, error, mapData } = useMapData({
key: 'mik_yeBk0Vf0nNJtpesfu560e07e5',
secret: 'mis_2g9ST8ZcSFb5R9fPnsvYhrX3RyRwPtDGbMGweCYKEq385431022',
mapId: '65c0ff7430b94e3fabd5bb8c',
});

The useMap hook returns an object containing the mapView and mapData once the 3D map has been rendered. Once the MapData has been initially fetched and passed to the <MapView>, useMap should be used to reference the hydrated data. useMap must be used in a child component of the <MapView>.

function MyCustomComponent() {
const { mapView, mapData } = useMap();

return mapData.getByType('space').map((space) => {
return <Label target={space.center} text={space.name} />;
});
}

useEvent

The useEvent hook enables subscribing to interactive map events and triggering a callback when they occur.

For example, the following snippet subscribes to the click event and updates state using the click coordinate. Labels are rendered based on the updated state. The click event contains all the interactive elements that the click action passed through.

See full JavaScript guides about Interactivity and the Camera for more information about events.

function MyCustomComponent() {
const { mapView, mapData } = useMap();
const [labels, setLabels] = useState([]);

useEvent('click', (event) => {
setLabels((prevLabels) => [...prevLabels, { target: event.coordinate, text: 'Hello, World!' }]);
});

return labels.map((label, idx) => <Label key={idx} {...label} />);
}

Other useful events include hover, camera-change, and floor-change.

The hover event is very similar to the click event and will contain all the interactive elements that are underneath the mouse cursor.

useEvent('hover', (event) => {
console.log('hover', event);
});

The camera-change event fires when the camera is adjusted. It contains the new bearing, pitch, zoomLevel and camera center coordinate.

useEvent('camera-change', (event) => {
console.log('camera-change', event.bearing, event.pitch, event.zoomLevel, event.center);
});

The floor-change event fires whenever the building floor is changed. It contains the floor and the reason that the change occurred.

useEvent('floor-change', (event) => {
console.log('floor-change', event.floor, event.reason);
});

Labels

Refer to Labels guide for more information and examples

The Label component renders a2D point of interest label on the map. Typically, they are used to show the name of each Space in the building. Labelling each Space can be achieved by iterating through mapData.getByType('space') and returning a Label for each item.

function MyCustomComponent() {
const { mapData } = useMap();

// Get all spaces with names
const spaces = mapData.getByType('space').filter(space => space.name);

return (
<>
{spaces.map(space => (
<Label
key={space.externalId}
target={space}
text={space.name} // label text
options={{ interactive: true }} // makes the label interactive
/>
))}
</>
);
}

Dynamic Visibility

Labels can be dynamically enabled or disabled using the enabled option. When a label is disabled, it will be hidden from view but remain in the map's memory:

const [isEnabled, setIsEnabled] = useState(false);

return (
<Label
target={space}
text="Dynamic Label"
options={{
enabled: isEnabled // label will be hidden when false
}}
/>
);

The CodeSandbox examples below demonstrates how to dynamically enable and disable Labels based on user zoom level. Zoom in and out to show secondary labels:

CodeSandbox - Dynamic Label visibility on zoom

Markers

Refer to Markers guide for more information and examples

The Marker component creates a DOM element to a coordinate on the map. React components can be provided as children of a Marker with their own state and interactivity.

function MyCustomComponent() {
const { mapData } = useMap();

// Get all spaces with names
const spaces = mapData.getByType('space').filter(space => space.name);

return (
<>
{spaces.map(space => (
<Marker
key={space.externalId}
target={space}
options={{ interactive: true }}
>
<div style={{
borderRadius: '10px',
backgroundColor: '#fff',
padding: '5px',
boxShadow: '0px 0px 1px rgba(0, 0, 0, 0.25)',
fontFamily: 'sans-serif'
}}>
{space.name}
</div>
</Marker>
))}
</>
);
}

Animated Markers

The AnimatedMarker component allows for smooth transitions when updating marker positions:

function AnimatedMarkers() {
const { mapData } = useMap();
const [markerCoordinate, setMarkerCoordinate] = useState<Mappedin.Coordinate | null>(
mapData?.mapCenter || null
);

// Handle map clicks to update the marker's coordinate
useEvent("click", (event) => {
setMarkerCoordinate(event.coordinate);
});

return (
<>
{markerCoordinate && (
<AnimatedMarker
target={markerCoordinate}
duration={1000}
options={{ interactive: true, anchor: "right" }}
>
<div>Animated Marker</div>
</AnimatedMarker>
)}
</>
);
}

The CodeSandbox examples below demonstrates how to display and animate a custom marker. Click around the map to animate the marker to the clicked location using default animation options:

CodeSandbox - Animate Markers

Dynamic Visibility

Like labels, markers can be dynamically enabled or disabled using the enabled option:

const [isEnabled, setIsEnabled] = useState(false);

return (
<Marker
target={space}
options={{
enabled: isEnabled // marker will be hidden when false
}}
>
<div>Dynamic Marker</div>
</Marker>
);

Paths

Paths can be drawn from A to B using the Path component. The Path component requires directions which must be created between two mapData objects.

The following example creates a Path from the first space in the data to the second.

See full JavaScript guide on Wayfinding for more information.

function MyCustomComponent() {
const { mapData, mapView } = useMap();

const space1 = mapData.getByType('space')[0];
const space2 = mapData.getByType('space')[1];
const directions = mapView.getDirections(space1, space2);

return directions ? <Path coordinate={directions.coordinates} options={{ color: 'blue' }} /> : null;
}

The Navigation component appears similar to the Path but with extra functionality to handle floor changes, start and end Markers, and styling.

See full JavaScript guide on Wayfinding for more information.

function MyCustomComponent() {
const { mapData, mapView } = useMap();

const space1 = mapData.getByType('space')[0];
const space2 = mapData.getByType('space')[1];
const directions = mapView.getDirections(space1, space2);

return directions ? <Navigation directions={directions} /> : null;
}

Shapes

The Shapes component takes a GeoJSON feature collection and renders it to the map in 3D.

function MyCustomComponent() {
return (
<Shape
geometry={featureCollection}
style={{
color: "red",
altitude: 0.2,
height: 2,
opacity: 0.7,
}}
/>
);
}

const featureCollection = {
type: "FeatureCollection",
features: [
{
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
..., // Some valid GeoJSON coordinates
],
},
},
],
};

Models

The Model component adds an instanced GLB/GLTF model to the map. The models prop accepts one or many targets to place the model at.

See full JavaScript guide on 3D Models for more information.

function MyCustomComponent() {
const { mapData } = useMap();

return (
<Model
models={mapData.getByType('space').map((space) => ({
target: space,
scale: [0.01, 0.01, 0.01],
rotation: [90, 0, 0],
opacity: 0.5,
}))}
options={{
url: 'https://raw.githubusercontent.com/KhronosGroup/glTF-Sample-Models/master/2.0/Duck/glTF-Binary/Duck.glb',
}}
/>
);
}

The following CodeSandbox implements all the features mentioned above.