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. Label
s 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
The Label
component renders a 2D 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.
See full JavaScript guide on Labels for more information.
function MyCustomComponent() {
const { mapData } = useMap();
return mapData.getByType('space').map((space) => {
return <Label target={space.center} text={space.name} />;
});
}
Markers
The Marker
component pins a 2D DOM element to a coordinate on the map. React components can be provided as children of a Marker
with their own state and interactivity.
The following example creates a simple Marker
which will change its background color to "red" on click.
See full JavaScript guide on Markers for more information.
function CustomMarker() {
const [color, setColor] = useState('white');
return (
<div
style={{
backgroundColor: color,
padding: '10px',
border: '1px solid black',
}}
onClick={() => {
setColor('red');
}}
>
Hello, world!
</div>
);
}
function MyCustomComponent() {
const [coordinate] = useState(new Mappedin.Coordinate(43.46330802307551, -80.52367172398456));
return (
<Marker target={coordinate}>
<CustomMarker />
</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;
}
Navigation
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',
}}
/>
);
}
Full Featured Example
The following CodeSandbox implements all the features mentioned above.