Framework Integration
This guide explains how to integrate the Lazarillo 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>Lazarillo SDK - Vanilla</title>
<!-- MapLibre GL JS (REQUIRED) -->
<script src="https://unpkg.com/maplibre-gl@5.7.0/dist/maplibre-gl.js"></script>
</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 "@lzdevelopers/my-maps-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/@lzdevelopers/my-maps-js/dist/styles.css" />// No need to call loadStyles() when CSS is included manually
import { initializeSDK } from "@lzdevelopers/my-maps-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 "@lzdevelopers/my-maps-js"
// Custom function to load styles from different path
function loadCustomStyles(cssPath: string) {
if (document.querySelector("link[data-lazarillo-styles]")) {
return
}
const link = document.createElement("link")
link.rel = "stylesheet"
link.href = cssPath
link.setAttribute("data-lazarillo-styles", "true")
document.head.appendChild(link)
}
// Load from custom path
loadCustomStyles("./assets/custom-lazarillo-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 Lazarillo CSS
module.exports = {
entry: {
main: ["./src/index.ts", "@lzdevelopers/my-maps-js/dist/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 LazarilloSDK,
type LazarilloMap,
} from "@lzdevelopers/my-maps-js"
interface MapComponentProps {
apiKey: string
institutionId: string
parentPlaceId?: string
}
const LazarilloMapComponent: React.FC<MapComponentProps> = ({
apiKey,
institutionId,
parentPlaceId,
}) => {
const mapContainer = useRef<HTMLDivElement>(null)
const [sdk, setSdk] = useState<LazarilloSDK | null>(null)
const [map, setMap] = useState<LazarilloMap | 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.lazarilloId === placeId)
if (place) {
await map.addMarker({
id: `marker-${place.lazarilloId}`,
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="lazarillo-map-wrapper">
<div ref={mapContainer} style={{ width: "100%", height: "500px" }} />
{map && <button onClick={() => addMarker("some-place-id")}>Add Marker</button>}
</div>
)
}
export default LazarilloMapComponentComponent Usage
// App.tsx
import React from "react"
import LazarilloMapComponent from "./components/LazarilloMapComponent"
function App() {
return (
<div className="App">
<h1>My App with Lazarillo</h1>
<LazarilloMapComponent
apiKey="your-api-key"
institutionId="your-institution-id"
parentPlaceId="optional-parent-place-id"
/>
</div>
)
}
export default AppCustom Hook (Optional)
// hooks/useLazarilloMap.ts
import { useState, useEffect, useRef } from "react"
import { initializeSDK, type LazarilloSDK, type LazarilloMap } from "@lzdevelopers/my-maps-js"
interface UseLazarilloMapProps {
apiKey: string
institutionId: string
parentPlaceId?: string
}
export const useLazarilloMap = ({ apiKey, institutionId, parentPlaceId }: UseLazarilloMapProps) => {
const mapContainer = useRef<HTMLDivElement>(null)
const [sdk, setSdk] = useState<LazarilloSDK | null>(null)
const [map, setMap] = useState<LazarilloMap | 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 "@lzdevelopers/my-maps-js/dist/styles.css";
/* Your custom overrides */
:root {
--lazarillo-primary-color: #your-brand-color;
}Option 2: Import in component file
Import directly in your React component:
// LazarilloMapComponent.tsx
import React, { useEffect, useRef, useState } from "react"
import "@lzdevelopers/my-maps-js/dist/styles.css" // Import CSS directly
import { initializeSDK, type LazarilloSDK, type LazarilloMap } from "@lzdevelopers/my-maps-js"
const LazarilloMapComponent: 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 "./LazarilloMap.module.css"
import "@lzdevelopers/my-maps-js/dist/styles.css"
// Or using styled-components with createGlobalStyle
import styled, { createGlobalStyle } from "styled-components"
import lazarilloCSS from "@lzdevelopers/my-maps-js/dist/styles.css"
const GlobalLazarilloStyles = createGlobalStyle`
@import url('@lzdevelopers/my-maps-js/dist/styles.css');
/* Custom overrides */
.lazarillo-map {
border-radius: 8px;
}
`Option 4: Dynamic loading with loadStyles()
import { loadStyles } from "@lzdevelopers/my-maps-js"
const LazarilloMapComponent: 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 Lazarillo CSS
entry: {
main: ["./src/index.js", "@lzdevelopers/my-maps-js/dist/styles.css"],
},
}Angular
Service
// services/lazarillo.service.ts
import { Injectable } from "@angular/core"
import {
initializeSDK,
loadStyles,
type LazarilloSDK,
type LazarilloMap,
} from "@lzdevelopers/my-maps-js"
@Injectable({
providedIn: "root",
})
export class LazarilloService {
private sdk: LazarilloSDK | null = null
initializeSDK(apiKey: string): LazarilloSDK {
if (this.sdk) {
return this.sdk
}
this.sdk = initializeSDK(apiKey, { lang: "en" })
return this.sdk
}
createMap(
mapContainer: HTMLElement,
parentPlaceId?: string,
onMapReady?: () => void
): LazarilloMap {
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/lazarillo-map.component.ts
import { Component, Input, ViewChild, ElementRef, OnInit, OnDestroy } from "@angular/core"
import { LazarilloService } from "../services/lazarillo.service"
import { type LazarilloMap } from "@lzdevelopers/my-maps-js"
@Component({
selector: "app-lazarillo-map",
template: `
<div class="map-container">
<div #mapContainer [style.width]="'100%'" [style.height]="height" class="lazarillo-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 LazarilloMapComponent implements OnInit, OnDestroy {
@Input() apiKey!: string
@Input() institutionId!: string
@Input() parentPlaceId?: string
@Input() height: string = "500px"
@ViewChild("mapContainer", { static: true }) mapContainer!: ElementRef<HTMLDivElement>
map: LazarilloMap | null = null
isLoading = true
error: string | null = null
constructor(private lazarilloService: LazarilloService) {}
ngOnInit() {
try {
// Initialize SDK
this.lazarilloService.initializeSDK(this.apiKey)
// Create map
this.map = this.lazarilloService.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.lazarilloService.getPlaces(this.parentPlaceId)
const place = places.find((p) => p.lazarilloId === placeId)
if (place) {
await this.map.addMarker({
id: `marker-${place.lazarilloId}`,
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 { LazarilloMapComponent } from "./components/lazarillo-map.component"
@NgModule({
declarations: [LazarilloMapComponent],
imports: [BrowserModule],
providers: [],
exports: [LazarilloMapComponent],
})
export class LazarilloModule {}Usage
<!-- app.component.html -->
<app-lazarillo-map
apiKey="your-api-key"
institutionId="your-institution-id"
[parentPlaceId]="'optional-parent-place-id'"
height="600px"
>
</app-lazarillo-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/@lzdevelopers/my-maps-js/dist/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 "~@lzdevelopers/my-maps-js/dist/styles.css";
/* Your custom overrides */
:root {
--lazarillo-primary-color: #your-brand-color;
}Option 3: Component-level styles
Import in a specific component:
@Component({
selector: "app-lazarillo-map",
templateUrl: "./lazarillo-map.component.html",
styleUrls: [
"./lazarillo-map.component.css",
"../../../node_modules/@lzdevelopers/my-maps-js/dist/styles.css",
],
})
export class LazarilloMapComponent {
// Component logic
}Option 4: Dynamic loading with loadStyles()
import { loadStyles } from "@lzdevelopers/my-maps-js"
@Component({
selector: "app-lazarillo-map",
templateUrl: "./lazarillo-map.component.html",
})
export class LazarilloMapComponent implements OnInit {
ngOnInit() {
// Load styles programmatically
loadStyles()
}
}Vue.js
Composition API (Vue 3)
<!-- LazarilloMap.vue -->
<template>
<div class="map-wrapper">
<div ref="mapContainer" :style="{ width: '100%', height: height }" class="lazarillo-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 LazarilloSDK,
type LazarilloMap,
} from "@lzdevelopers/my-maps-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<LazarilloSDK | null>(null)
const map = ref<LazarilloMap | 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.lazarilloId}`,
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)
<!-- LazarilloMapOptions.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 "@lzdevelopers/my-maps-js"
// MapLibre types are available for TypeScript projects
export default {
name: "LazarilloMap",
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 "@lzdevelopers/my-maps-js/dist/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 "@lzdevelopers/my-maps-js/dist/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 "@lzdevelopers/my-maps-js/dist/styles.css" // Import at component level
import { initializeSDK, type LazarilloSDK, type LazarilloMap } from "@lzdevelopers/my-maps-js"
// Component logic...
</script>Option 4: Using CSS preprocessors
For SCSS/SASS projects:
<style lang="scss">
// Import in your component styles
@import "@lzdevelopers/my-maps-js/dist/styles.css";
// Custom overrides with SCSS
$lazarillo-primary: #your-brand-color;
.lazarillo-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 '@lzdevelopers/my-maps-js/dist/styles.css';`,
},
},
},
})Option 6: Dynamic loading with loadStyles()
<script setup lang="ts">
import { onMounted } from "vue"
import { loadStyles } from "@lzdevelopers/my-maps-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: ["@lzdevelopers/my-maps-js"],
},
build: {
commonjsOptions: {
include: [/@lzdevelopers\/my-maps-js/, /node_modules/],
},
},
})CSS Loading Options
Method 1: Automatic Loading with loadStyles()
import { loadStyles } from "@lzdevelopers/my-maps-js"
// Call once in your application
loadStyles()Method 2: Manual HTML Import
<link rel="stylesheet" href="./node_modules/@lzdevelopers/my-maps-js/dist/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-lazarillo-styles", "true")
document.head.appendChild(link)
}
loadCustomStyles("./assets/my-custom-lazarillo-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 {
--lazarillo-primary-color: #your-brand-color;
--lazarillo-marker-size: 24px;
}
/* Custom marker styles */
.lazarillo-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.