Framework Integration
This guide explains how to integrate the MapVX Web SDK in different frameworks and environments.
Vanilla JavaScript/TypeScript
HTML Setup
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>MapVX SDK - Vanilla</title>
<!-- MapVX SDK styles -->
<link rel="stylesheet" href="./node_modules/@mapvx/web-js/dist/umd/styles.css" />
</head>
<body>
<div id="mapContainer" style="width: 100%; height: 500px;"></div>
<script src="./dist/your-app.js"></script>
</body>
</html>TypeScript/JavaScript
Option 1: Automatic CSS Loading
import { initializeSDK, loadStyles } from "@mapvx/web-js"
// Load default styles
loadStyles()
function initMap() {
const sdk = initializeSDK("your-api-key", { lang: "en" })
const mapContainer = document.getElementById("mapContainer") as HTMLElement
const map = sdk.createMap(mapContainer, {
parentPlaceId: "parent-place-id",
onMapReady: () => {
console.log("Map ready!")
},
})
}
initMap()Option 2: Manual CSS Import
<!-- Include CSS manually in your HTML -->
<link rel="stylesheet" href="./node_modules/@mapvx/web-js/styles.css" />// No need to call loadStyles() when CSS is included manually
import { initializeSDK } from "@mapvx/web-js"
function initMap() {
const sdk = initializeSDK("your-api-key", { lang: "en" })
const mapContainer = document.getElementById("mapContainer") as HTMLElement
const map = sdk.createMap(mapContainer, {
parentPlaceId: "parent-place-id",
onMapReady: () => {
console.log("Map ready!")
},
})
}
initMap()Option 3: Custom CSS Path
import { initializeSDK } from "@mapvx/web-js"
// Custom function to load styles from different path
function loadCustomStyles(cssPath: string) {
if (document.querySelector("link[data-mapvx-styles]")) {
return
}
const link = document.createElement("link")
link.rel = "stylesheet"
link.href = cssPath
link.setAttribute("data-mapvx-styles", "true")
document.head.appendChild(link)
}
// Load from custom path
loadCustomStyles("./assets/custom-mapvx-styles.css")
// Continue with SDK initialization...Webpack Configuration Options
Option 1: Basic CSS Loading
// webpack.config.js
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: "ts-loader",
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".css"],
},
}Option 2: Extract CSS to separate file
const MiniCssExtractPlugin = require("mini-css-extract-plugin")
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: "styles.css",
}),
],
}Option 3: Auto-include MapVX CSS
module.exports = {
entry: {
main: ["./src/index.ts", "@mapvx/web-js/styles.css"],
},
module: {
rules: [
{
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
],
},
}React
Component Setup
import React, { useEffect, useRef, useState } from "react"
import { initializeSDK, loadStyles, type MapVXSDK, type MapVXMap } from "@mapvx/web-js"
interface MapComponentProps {
apiKey: string
institutionId: string
parentPlaceId?: string
}
const MapVXMapComponent: React.FC<MapComponentProps> = ({
apiKey,
institutionId,
parentPlaceId,
}) => {
const mapContainer = useRef<HTMLDivElement>(null)
const [sdk, setSdk] = useState<MapVXSDK | null>(null)
const [map, setMap] = useState<MapVXMap | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
let isMounted = true
const initializeMap = async () => {
try {
setIsLoading(true)
// Initialize SDK
const sdkInstance = initializeSDK(apiKey, { lang: "en" })
if (!isMounted) return
// Create map
const mapInstance = sdkInstance.createMap(mapContainer.current!, {
parentPlaceId: parentPlaceId || undefined,
onMapReady: () => {
if (isMounted) {
setSdk(sdkInstance)
setMap(mapInstance)
setIsLoading(false)
console.log("React map initialized")
}
},
})
if (!isMounted) return
} catch (err) {
if (isMounted) {
setError(err instanceof Error ? err.message : "Unknown error")
setIsLoading(false)
}
}
}
if (mapContainer.current) {
initializeMap()
}
return () => {
isMounted = false
}
}, [apiKey, institutionId, parentPlaceId])
const addMarker = async (placeId: string) => {
if (!sdk || !map) return
try {
const places = await sdk.getAvailablePlaces()
const place = places.find((p) => p.mapvxId === placeId)
if (place) {
await map.addMarker({
id: `marker-${place.mapvxId}`,
coordinate: place.position,
text: place.title,
textPosition: "bottom",
})
}
} catch (error) {
console.error("Error adding marker:", error)
}
}
if (isLoading) {
return <div className="map-loading">Loading map...</div>
}
if (error) {
return <div className="map-error">Error: {error}</div>
}
return (
<div className="mapvx-map-wrapper">
<div ref={mapContainer} style={{ width: "100%", height: "500px" }} />
{map && <button onClick={() => addMarker("some-place-id")}>Add Marker</button>}
</div>
)
}
export default MapVXMapComponentComponent Usage
// App.tsx
import React from "react"
import MapVXMapComponent from "./components/MapVXMapComponent"
function App() {
return (
<div className="App">
<h1>My App with MapVX</h1>
<MapVXMapComponent
apiKey="your-api-key"
institutionId="your-institution-id"
parentPlaceId="optional-parent-place-id"
/>
</div>
)
}
export default AppCustom Hook (Optional)
// hooks/useMapVXMap.ts
import { useState, useEffect, useRef } from "react"
import { initializeSDK, type MapVXSDK, type MapVXMap } from "@mapvx/web-js"
interface UseMapVXMapProps {
apiKey: string
institutionId: string
parentPlaceId?: string
}
export const useMapVXMap = ({ apiKey, institutionId, parentPlaceId }: UseMapVXMapProps) => {
const mapContainer = useRef<HTMLDivElement>(null)
const [sdk, setSdk] = useState<MapVXSDK | null>(null)
const [map, setMap] = useState<MapVXMap | null>(null)
const [isLoading, setIsLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
useEffect(() => {
let isMounted = true
const init = async () => {
try {
setIsLoading(true)
const sdkInstance = initializeSDK(apiKey, { lang: "en" })
if (!isMounted || !mapContainer.current) return
const mapInstance = sdkInstance.createMap(mapContainer.current, {
parentPlaceId: parentPlaceId || undefined,
onMapReady: () => {
if (isMounted) {
setSdk(sdkInstance)
setMap(mapInstance)
setIsLoading(false)
}
},
})
} catch (err) {
if (isMounted) {
setError(err instanceof Error ? err.message : "Error")
setIsLoading(false)
}
}
}
init()
return () => {
isMounted = false
}
}, [apiKey, institutionId, parentPlaceId])
return { mapContainer, sdk, map, isLoading, error }
}React CSS Loading Options
Option 1: Import in index.css or App.css (Recommended)
Add the import to your main CSS file:
/* src/index.css or src/App.css */
@import "@mapvx/web-js/styles.css";
/* Your custom overrides */
:root {
--mapvx-primary-color: #your-brand-color;
}Option 2: Import in component file
Import directly in your React component:
// MapVXMapComponent.tsx
import React, { useEffect, useRef, useState } from "react"
import "@mapvx/web-js/styles.css" // Import CSS directly
import { initializeSDK, type MapVXSDK, type MapVXMap } from "@mapvx/web-js"
const MapVXMapComponent: React.FC<MapComponentProps> = ({
apiKey,
institutionId,
parentPlaceId,
}) => {
// Component logic...
}Option 3: CSS Modules or Styled Components
For CSS Modules or styled-components projects:
// Using CSS Modules
import styles from "./MapVXMap.module.css"
import "@mapvx/web-js/styles.css"
// Or using styled-components with createGlobalStyle
import styled, { createGlobalStyle } from "styled-components"
import mapvxCSS from "@mapvx/web-js/styles.css"
const GlobalMapVXStyles = createGlobalStyle`
@import url('@mapvx/web-js/styles.css');
/* Custom overrides */
.mapvx-map {
border-radius: 8px;
}
`Option 4: Dynamic loading with loadStyles()
import { loadStyles } from "@mapvx/web-js"
const MapVXMapComponent: React.FC<MapComponentProps> = (props) => {
useEffect(() => {
// Load styles programmatically
loadStyles()
}, [])
// Component logic...
}Option 5: Webpack configuration
If you prefer to configure at the build level:
// webpack.config.js or next.config.js
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: ["style-loader", "css-loader"],
},
],
},
// Automatically include MapVX CSS
entry: {
main: ["./src/index.js", "@mapvx/web-js/styles.css"],
},
}Angular
Service
// services/mapvx.service.ts
import { Injectable } from "@angular/core"
import { initializeSDK, loadStyles, type MapVXSDK, type MapVXMap } from "@mapvx/web-js"
@Injectable({
providedIn: "root",
})
export class MapVXService {
private sdk: MapVXSDK | null = null
initializeSDK(apiKey: string): MapVXSDK {
if (this.sdk) {
return this.sdk
}
this.sdk = initializeSDK(apiKey, { lang: "en" })
return this.sdk
}
createMap(mapContainer: HTMLElement, parentPlaceId?: string, onMapReady?: () => void): MapVXMap {
if (!this.sdk) {
throw new Error("SDK not initialized")
}
return this.sdk.createMap(mapContainer, {
parentPlaceId: parentPlaceId || undefined,
onMapReady,
})
}
async getPlaces(parentPlaceId?: string) {
if (!this.sdk) {
throw new Error("SDK not initialized")
}
if (parentPlaceId) {
return await this.sdk.getSubPlaces(parentPlaceId)
} else {
return await this.sdk.getAvailablePlaces()
}
}
}Component
// components/mapvx-map.component.ts
import { Component, Input, ViewChild, ElementRef, OnInit, OnDestroy } from "@angular/core"
import { MapVXService } from "../services/mapvx.service"
import { type MapVXMap } from "@mapvx/web-js"
@Component({
selector: "app-mapvx-map",
template: `
<div class="map-container">
<div #mapContainer [style.width]="'100%'" [style.height]="height" class="mapvx-map"></div>
<div *ngIf="isLoading" class="loading">Loading map...</div>
<div *ngIf="error" class="error">Error: {{ error }}</div>
</div>
`,
styles: [
`
.map-container {
position: relative;
}
.loading,
.error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 1rem;
background: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.error {
background: #fee;
color: #c33;
}
`,
],
})
export class MapVXMapComponent implements OnInit, OnDestroy {
@Input() apiKey!: string
@Input() institutionId!: string
@Input() parentPlaceId?: string
@Input() height: string = "500px"
@ViewChild("mapContainer", { static: true }) mapContainer!: ElementRef<HTMLDivElement>
map: MapVXMap | null = null
isLoading = true
error: string | null = null
constructor(private mapvxService: MapVXService) {}
ngOnInit() {
try {
// Initialize SDK
this.mapvxService.initializeSDK(this.apiKey)
// Create map
this.map = this.mapvxService.createMap(
this.mapContainer.nativeElement,
this.parentPlaceId,
() => {
this.isLoading = false
console.log("Angular map initialized")
}
)
} catch (err) {
this.error = err instanceof Error ? err.message : "Unknown error"
this.isLoading = false
}
}
ngOnDestroy() {
// Cleanup if necessary
this.map = null
}
async addMarker(placeId: string) {
if (!this.map) return
try {
const places = await this.mapvxService.getPlaces(this.parentPlaceId)
const place = places.find((p) => p.mapvxId === placeId)
if (place) {
await this.map.addMarker({
id: `marker-${place.mapvxId}`,
coordinate: place.position,
text: place.title,
textPosition: "bottom",
})
}
} catch (error) {
console.error("Error adding marker:", error)
}
}
}Module
// app.module.ts
import { NgModule } from "@angular/core"
import { BrowserModule } from "@angular/platform-browser"
import { MapVXMapComponent } from "./components/mapvx-map.component"
@NgModule({
declarations: [MapVXMapComponent],
imports: [BrowserModule],
providers: [],
exports: [MapVXMapComponent],
})
export class MapVXModule {}Usage
<!-- app.component.html -->
<app-mapvx-map
apiKey="your-api-key"
institutionId="your-institution-id"
[parentPlaceId]="'optional-parent-place-id'"
height="600px"
>
</app-mapvx-map>Angular CSS Loading Options
Option 1: Using angular.json (Recommended)
Add the CSS file to your angular.json configuration:
{
"projects": {
"your-app": {
"architect": {
"build": {
"options": {
"styles": ["src/styles.css", "node_modules/@mapvx/web-js/styles.css"]
}
}
}
}
}
}Option 2: Import in styles.css/styles.scss
Add the import to your global styles file:
/* src/styles.css or src/styles.scss */
@import "~@mapvx/web-js/styles.css";
/* Your custom overrides */
:root {
--mapvx-primary-color: #your-brand-color;
}Option 3: Component-level styles
Import in a specific component:
@Component({
selector: "app-mapvx-map",
templateUrl: "./mapvx-map.component.html",
styleUrls: ["./mapvx-map.component.css", "../../../node_modules/@mapvx/web-js/styles.css"],
})
export class MapVXMapComponent {
// Component logic
}Option 4: Dynamic loading with loadStyles()
import { loadStyles } from "@mapvx/web-js"
@Component({
selector: "app-mapvx-map",
templateUrl: "./mapvx-map.component.html",
})
export class MapVXMapComponent implements OnInit {
ngOnInit() {
// Load styles programmatically
loadStyles()
}
}Vue.js
Composition API (Vue 3)
<!-- MapVXMap.vue -->
<template>
<div class="map-wrapper">
<div ref="mapContainer" :style="{ width: '100%', height: height }" class="mapvx-map"></div>
<div v-if="isLoading" class="loading">Loading map...</div>
<div v-if="error" class="error">Error: {{ error }}</div>
<button v-if="map" @click="addSampleMarker" class="add-marker-btn">Add Marker</button>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, watch } from "vue"
import { initializeSDK, loadStyles, type MapVXSDK, type MapVXMap } from "@mapvx/web-js"
interface Props {
apiKey: string
institutionId: string
parentPlaceId?: string
height?: string
}
const props = withDefaults(defineProps<Props>(), {
height: "500px",
})
const mapContainer = ref<HTMLDivElement>()
const sdk = ref<MapVXSDK | null>(null)
const map = ref<MapVXMap | null>(null)
const isLoading = ref(true)
const error = ref<string | null>(null)
const initializeMap = () => {
try {
isLoading.value = true
error.value = null
// Initialize SDK
sdk.value = initializeSDK(props.apiKey, { lang: "en" })
// Create map
if (mapContainer.value) {
map.value = sdk.value.createMap(mapContainer.value, {
parentPlaceId: props.parentPlaceId || undefined,
onMapReady: () => {
isLoading.value = false
console.log("Vue map initialized")
},
})
}
} catch (err) {
error.value = err instanceof Error ? err.message : "Unknown error"
isLoading.value = false
}
}
const addSampleMarker = async () => {
if (!sdk.value || !map.value) return
try {
const places = await sdk.value.getAvailablePlaces()
if (places.length > 0) {
const place = places[0]
await map.value.addMarker({
id: `marker-${place.mapvxId}`,
coordinate: place.position,
text: place.title,
textPosition: "bottom",
})
}
} catch (error) {
console.error("Error adding marker:", error)
}
}
onMounted(() => {
initializeMap()
})
onUnmounted(() => {
// Cleanup if necessary
map.value = null
sdk.value = null
})
// Reinitialize if critical props change
watch([() => props.apiKey, () => props.institutionId], () => {
initializeMap()
})
</script>
<style scoped>
.map-wrapper {
position: relative;
}
.loading,
.error {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 1rem;
background: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
z-index: 1000;
}
.error {
background: #fee;
color: #c33;
}
.add-marker-btn {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
padding: 0.5rem 1rem;
background: #007cbf;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
.add-marker-btn:hover {
background: #005a87;
}
</style>Options API (Vue 2/3)
<!-- MapVXMapOptions.vue -->
<template>
<div class="map-wrapper">
<div ref="mapContainer" :style="{ width: '100%', height: height }"></div>
<div v-if="isLoading" class="loading">Loading map...</div>
<div v-if="error" class="error">Error: {{ error }}</div>
</div>
</template>
<script>
import { initializeSDK, loadStyles } from "@mapvx/web-js"
// MapLibre types are available for TypeScript projects
export default {
name: "MapVXMap",
props: {
apiKey: {
type: String,
required: true,
},
institutionId: {
type: String,
required: true,
},
parentPlaceId: {
type: String,
default: null,
},
height: {
type: String,
default: "500px",
},
},
data() {
return {
sdk: null,
map: null,
isLoading: true,
error: null,
}
},
mounted() {
this.initializeMap()
},
methods: {
initializeMap() {
try {
this.isLoading = true
this.error = null
// Initialize SDK
this.sdk = initializeSDK(this.apiKey, { lang: "en" })
// Create map
this.map = this.sdk.createMap(this.$refs.mapContainer, {
parentPlaceId: this.parentPlaceId || undefined,
onMapReady: () => {
this.isLoading = false
console.log("Vue map initialized")
},
})
} catch (err) {
this.error = err.message || "Unknown error"
this.isLoading = false
}
},
},
}
</script>Vue.js CSS Loading Options
Option 1: Import in main.js/main.ts (Recommended)
Add the import to your main application file:
// main.js or main.ts
import { createApp } from "vue"
import App from "./App.vue"
import "@mapvx/web-js/styles.css" // Import CSS globally
const app = createApp(App)
app.mount("#app")Option 2: Import in App.vue
Import in your root component:
<template>
<div id="app">
<!-- Your app content -->
</div>
</template>
<script>
import "@mapvx/web-js/styles.css"
// Rest of your component logic
</script>Option 3: Component-level import
Import CSS directly in the component that uses the map:
<script setup lang="ts">
import "@mapvx/web-js/styles.css" // Import at component level
import { initializeSDK, type MapVXSDK, type MapVXMap } from "@mapvx/web-js"
// Component logic...
</script>Option 4: Using CSS preprocessors
For SCSS/SASS projects:
<style lang="scss">
// Import in your component styles
@import "@mapvx/web-js/styles.css";
// Custom overrides with SCSS
$mapvx-primary: #your-brand-color;
.mapvx-map {
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
&:hover {
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}
}
</style>Option 5: Vite configuration
For Vite-based Vue projects:
// vite.config.js
import { defineConfig } from "vite"
import vue from "@vitejs/plugin-vue"
export default defineConfig({
plugins: [vue()],
css: {
preprocessorOptions: {
scss: {
additionalData: `@import '@mapvx/web-js/styles.css';`,
},
},
},
})Option 6: Dynamic loading with loadStyles()
<script setup lang="ts">
import { onMounted } from "vue"
import { loadStyles } from "@mapvx/web-js"
onMounted(() => {
// Load styles programmatically
loadStyles()
})
</script>Vite (For any framework)
Configuration
// vite.config.js
import { defineConfig } from "vite"
export default defineConfig({
// ... other configurations
optimizeDeps: {
include: ["@mapvx/web-js"],
},
build: {
commonjsOptions: {
include: [/@mapvx\/web-js/, /node_modules/],
},
},
})CSS Loading Options
Method 1: Automatic Loading with loadStyles()
import { loadStyles } from "@mapvx/web-js"
// Call once in your application
loadStyles()Method 2: Manual HTML Import
<link rel="stylesheet" href="./node_modules/@mapvx/web-js/styles.css" />Method 3: Custom CSS Path
// Load from custom location
function loadCustomStyles(path) {
const link = document.createElement("link")
link.rel = "stylesheet"
link.href = path
link.setAttribute("data-mapvx-styles", "true")
document.head.appendChild(link)
}
loadCustomStyles("./assets/my-custom-mapvx-styles.css")CSS Customization Benefits
- 🎨 Easy Theming: Override CSS variables for quick customization
- 📦 Bundle Control: Choose when and how CSS is loaded
- 🚀 Performance: Load styles conditionally or lazy-load
- đź”§ Custom Builds: Create your own styled versions
CSS Override Example
/* Override default styles */
:root {
--mapvx-primary-color: #your-brand-color;
--mapvx-marker-size: 24px;
}
/* Custom marker styles */
.mapvx-marker {
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}Framework-Specific CSS Loading Summary
| Framework | Recommended Method | Alternative Methods |
|---|---|---|
| Angular | angular.json styles array | styles.css import, component-level, loadStyles() |
| React | index.css import | Component import, CSS Modules, styled-components, loadStyles() |
| Vue | main.js import | App.vue import, component-level, Vite config, loadStyles() |
| Vanilla | Manual <link> tag | Webpack auto-include, loadStyles() |
Integration Checklist
For any framework, make sure you have:
- âś… CSS Loading: Choose your preferred method (framework-native import recommended)
- âś… Map container available before initializing
- âś… Proper error handling
- âś… Cleanup on component unmount
- âś… Loading states for better UX
CSS Loading Decision Tree
- Building a framework app? → Use framework-native imports (
importin React/Vue,angular.jsonin Angular) - Need dynamic loading? → Use
loadStyles()function - Want maximum control? → Manual
<link>tag in HTML - Using a bundler? → Configure webpack/vite to auto-include CSS
Important: From version 2.22.0+, CSS is no longer automatically included. You must explicitly load styles using one of the methods above.
Last updated on