Install from npm (external apps)
Guide for apps outside this monorepo that install @hungpvq/* from the npm registry (greenfield).
Related: Getting started · Minimal starter · Peers and bundle · Stable API
What to install
Recommended — meta package (pulls map-core, map-dataset, framework adapters, draggable, shared):
# Vue
npm install @hungpvq/vue-map maplibre-gl vue
# React
npm install @hungpvq/react-map maplibre-gl react react-dommaplibre-gl and the framework stay peers (single instance in the app). Do not rely on a second copy nested under @hungpvq/*.
Version family
Map packages release together under tag map@{version} (fixed group). Prefer aligning @hungpvq/map-* / vue-map-* / react-map-* on the same minor. Meta packages declare ^ ranges on the stack; pin or lockfile if you need stricter control.
Meta vs domain imports
From meta (@hungpvq/vue-map / react-map) | Still import from domain packages |
|---|---|
installMapApp (+ Vue createMapAppPlugin) | UI: Map, controls → vue-map-core / react-map-core |
./style.css (all shell CSS) | Dataset UI: LayerControl, hooks → vue-map-dataset / react-map-dataset |
| Transitive deps (core, dataset, draggable, shared, icons) | Builders / types: @hungpvq/map-core, @hungpvq/map-dataset (and subpaths) |
Meta is an install + bootstrap bag, not a barrel of every component.
Bootstrap (once per app)
// Vue
import { createApp } from 'vue';
import { installMapApp } from '@hungpvq/vue-map';
import '@hungpvq/vue-map/style.css';
const app = createApp(App);
installMapApp(app);
app.mount('#app');// React — call once at app entry (before first map mount)
import { installMapApp } from '@hungpvq/react-map';
import '@hungpvq/react-map/style.css';
installMapApp();Without installMapApp, layer menus / style / export / attribute table UI do not register.
Minimal map (copy-paste)
Same UI as Minimal starter, with meta CSS + bootstrap.
Vue
<script setup lang="ts">
import type { MapSimple } from '@hungpvq/map-core';
import { Map } from '@hungpvq/vue-map-core';
import { LayerControl, useMapDataset } from '@hungpvq/vue-map-dataset';
import { createGeoJsonDataset } from '@hungpvq/map-dataset/geojson';
import type { FeatureCollection } from 'geojson';
const sample: FeatureCollection = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: { name: 'A' },
geometry: { type: 'Point', coordinates: [106.7, 10.8] },
},
],
};
function onMapLoaded(map: MapSimple) {
const { addDataset } = useMapDataset(map.id);
addDataset(
createGeoJsonDataset({
name: 'Sample points',
geojson: sample,
type: 'point',
color: '#e74c3c',
}),
);
}
</script>
<template>
<Map @map-loaded="onMapLoaded">
<LayerControl position="top-left" show />
</Map>
</template>Entry:
import { createApp } from 'vue';
import { installMapApp } from '@hungpvq/vue-map';
import '@hungpvq/vue-map/style.css';
import App from './App.vue';
const app = createApp(App);
installMapApp(app);
app.mount('#app');React
import type { MapSimple } from '@hungpvq/map-core';
import { Map } from '@hungpvq/react-map-core';
import { LayerControl, useMapDataset } from '@hungpvq/react-map-dataset';
import { createGeoJsonDataset } from '@hungpvq/map-dataset/geojson';
import type { FeatureCollection } from 'geojson';
const sample: FeatureCollection = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: { name: 'A' },
geometry: { type: 'Point', coordinates: [106.7, 10.8] },
},
],
};
export function MinimalMap() {
const { addDataset, setMapId } = useMapDataset();
function onMapLoaded(map: MapSimple) {
setMapId(map.id);
void addDataset(
createGeoJsonDataset({
name: 'Sample points',
geojson: sample,
type: 'point',
color: '#e74c3c',
}),
);
}
return (
<Map onMapLoaded={onMapLoaded}>
<LayerControl position="top-left" show />
</Map>
);
}Entry (once):
import { installMapApp } from '@hungpvq/react-map';
import '@hungpvq/react-map/style.css';
installMapApp();Optional installs (feature peers)
Install only when you use the feature:
| Feature | Packages |
|---|---|
Print / exportFile | file-saver |
| Legend expression eval | @maplibre/maplibre-gl-style-spec |
| CreateControl file formats | shpjs, papaparse, @tmcw/togeojson, @xmldom/xmldom, jszip, topojson-client, … — Peers and bundle |
| Off-main-thread GIS parse | Vite plugin @hungpvq/map-dataset/vite — Worker |
| Draw / edit | @hungpvq/vue-map-draw or react-map-draw + @hungpvq/map-draw — Draw |
| Devtools panel | @hungpvq/vue-map-devtools or react-map-devtools — Devtools |
Defaults (single-app / single theme)
Out of the box:
- Theme:
scope: 'document'(html+prefers-color-schemewhenauto) - Stores / platform accessors: process-wide (
globalThis)
For independent multi-map chrome themes use ThemeControl / applyMapTheme with scope: 'map' — Map store.
Troubleshooting (external)
| Symptom | Check |
|---|---|
| Unstyled UI | Import @hungpvq/vue-map/style.css (or react-map) once; or the full a-la-carte CSS set |
| Empty layer menus / no style editor | Call installMapApp before mounting maps |
| Two MapLibre instances / broken GL | Only one maplibre-gl in the app (peer); avoid bundling a second copy |
| Peer warnings for turf / proj4 | Expected to come with @hungpvq/map-core — do not install @turf/turf for the library |
| CreateControl missing format | Install the optional GIS peer for that format |
| Types / exports missing | Import Stable symbols from documented packages — Stable API |
Next
- Full a-la-carte peer table: Peers and bundle
- Copy-paste map + layer only: Minimal starter
- Hub overview: Getting started