Skip to Content
ExamplesFramework Integration

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 MapVXMapComponent

Component 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 App

Custom 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

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

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

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

FrameworkRecommended MethodAlternative Methods
Angularangular.json styles arraystyles.css import, component-level, loadStyles()
Reactindex.css importComponent import, CSS Modules, styled-components, loadStyles()
Vuemain.js importApp.vue import, component-level, Vite config, loadStyles()
VanillaManual <link> tagWebpack 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

  1. Building a framework app? → Use framework-native imports (import in React/Vue, angular.json in Angular)
  2. Need dynamic loading? → Use loadStyles() function
  3. Want maximum control? → Manual <link> tag in HTML
  4. 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