A zero-dependency, TypeScript-native, interactive mapping library β Mapbox-class features with a Leaflet-style API.
-
πΊοΈ Interactive slippy maps β
TsMapgives you fractional zoom, bearing (rotation), pitch (tilt), and unifiedflyTo/easeTo/jumpTocamera animations. -
π§± Vector tiles + style spec β in-house MVT (
.pbf) decoder, full subset of the Mapbox GL Style Specification, and an expression engine that understandsinterpolate/step/case/match/coalesceand friends.setStyle,addSource,addLayer,setPaintProperty,setFilter,queryRenderedFeatures,querySourceFeaturesβ the works. -
ποΈ 3D β
fill-extrusion, atmosphericsetFog, a sky layer viasetSky, and DEM-based terrain viasetTerrain(auto-loads tiles from araster-demsource). ACustomLayerInterfacelets apps render raw WebGL2 alongside the built-in layers. -
π Globe β seamless transition between Mercator and globe projection around zoom 5.5, with an atmosphere halo.
-
π§ Services β geocoding (Nominatim, Photon, Mapbox, Maptiler, Google), directions (OSRM, Valhalla, Mapbox, Google), isochrones, and distance matrix adapters behind a common interface. Defaults are keyless.
-
π΄ Offline β IndexedDB-backed
TileCache,saveOfflineRegionfor pre-fetching bboxes, and a worker pool for off-main-thread tile decode. -
π§© Layer-scoped events β
map.on('click', 'layer-id', handler), Mapbox-style: handler only fires when a feature on the named style layer is hit. -
π― Zero runtime dependencies. Subpath exports (
ts-maps/services,ts-maps/style-spec, β¦) let you import just the slice you need. -
π§© Framework bindings β
@ts-maps/react,@ts-maps/vue,@ts-maps/svelte,@ts-maps/solid,@ts-maps/nuxt,@ts-maps/react-native(WebView-hosted). -
π TypeScript-native β full
isolatedDeclarationscompliance, strict types, and declaration files for every public module.
# Using npm
npm install ts-maps
# Using pnpm
pnpm add ts-maps
# Using yarn
yarn add ts-maps
# Using bun
bun add ts-mapsnpm install ts-maps ts-maps-react # React
npm install ts-maps ts-maps-vue # Vue
npm install ts-maps ts-maps-svelte # Svelte
npm install ts-maps ts-maps-solid # SolidJS
npm install ts-maps-nuxt # Nuxt moduleimport 'ts-maps/styles.css'
import { Marker, tileLayer, TsMap } from 'ts-maps'
const map = new TsMap('map', {
center: [40.758, -73.9855],
zoom: 13,
bearing: 0,
pitch: 0,
})
tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
}).addTo(map)
new Marker([40.758, -73.9855])
.addTo(map)
.bindPopup('Hello from ts-maps')
.openPopup()import { TsMap, vectorTileLayer } from 'ts-maps'
const map = new TsMap('map', { center: [51.5, -0.12], zoom: 6 })
vectorTileLayer({
url: 'https://tiles.example.com/{z}/{x}/{y}.pbf',
layers: [
{ id: 'water', type: 'fill', sourceLayer: 'water', paint: { 'fill-color': '#0ea5e9' } },
// Full style-spec expressions are accepted in `filter` and paint/layout:
{ id: 'primary-roads', type: 'line', sourceLayer: 'transportation',
filter: ['match', ['get', 'class'], ['primary', 'trunk'], true, false],
paint: { 'line-color': '#6b7280', 'line-width': ['interpolate', ['linear'], ['zoom'], 10, 0.5, 16, 3] } },
],
}).addTo(map)
// Layer-scoped events (Mapbox-style):
map.on('click', 'primary-roads', (e) => {
console.log('road clicked', e.features[0].properties)
})map.setPitch(50)
map.setBearing(30)
map.addSource('terrain-dem', {
type: 'raster-dem',
tiles: ['https://tiles.example.com/terrain-rgb/{z}/{x}/{y}.png'],
tileSize: 512,
encoding: 'mapbox',
})
map.setTerrain({ source: 'terrain-dem', exaggeration: 1.4 })
map.setSky({ 'sky-color': '#87ceeb', 'horizon-color': '#ffffff' })
map.setFog({ color: 'rgb(245, 247, 250)', 'horizon-blend': 0.1 })
// Query ground elevation anywhere:
const m = map.queryTerrainElevation({ lng: -74, lat: 40.7 })import { services } from 'ts-maps'
const geocoder = services.defaultGeocoder() // Nominatim, keyless
const hits = await geocoder.search('Tower Bridge, London')
const directions = services.defaultDirections() // OSRM, keyless
const routes = await directions.getDirections(
[{ lat: 51.5055, lng: -0.0754 }, { lat: 51.5074, lng: -0.1278 }],
{ profile: 'driving' },
)
const matrix = services.defaultMatrix() // OSRM /table endpoint
const m = await matrix.getMatrix(
[{ lat: 51.5055, lng: -0.0754 }, { lat: 51.5074, lng: -0.1278 }, { lat: 51.5155, lng: -0.1408 }],
{ profile: 'driving' },
)import { saveOfflineRegion, TileCache } from 'ts-maps'
const cache = new TileCache({ maxBytes: 200 * 1024 * 1024 })
await saveOfflineRegion({
bounds: [-0.20, 51.50, -0.05, 51.53],
zoomRange: [10, 14],
tileUrl: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
cache,
})
// Later: await cache.close() when the map is torn down.const dataUrl = await map.toDataURL() // PNG data URL of the current view
const blob = await map.toBlob('image/png') // or a BlobOnly need the services layer? Import from the subpath β a small slice instead of the full bundle:
import { defaultGeocoder } from 'ts-maps/services'
import { validateStyle } from 'ts-maps/style-spec'
import { TileCache } from 'ts-maps/storage'
import { LatLng, LatLngBounds } from 'ts-maps/geo'Available subpaths: services, style-spec, storage, geo,
geometry, symbols.
- Clone the repository:
git clone https://github.com/stacksjs/ts-maps.git
cd ts-maps- Install dependencies:
bun install- Start development:
bun run devPlease see our releases page for more information on what has changed recently.
Please see CONTRIBUTING for details.
For help, discussion about best practices, or any other conversation that would benefit from being searchable:
For casual chit-chat with others using this package:
Join the Stacks Discord Server
"Software that is free, but hopes for a postcard." We love receiving postcards from around the world showing where ts-maps is being used! We showcase them on our website too.
Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States π
We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
- Leaflet β the module layout and public API shape of the interactive map API follow its design.
- Mapbox GL JS β the style spec, expression engine, and vector-tile renderer are modeled after its design.
- Chris Breuer
- All Contributors
The MIT License (MIT). Please see LICENSE for more information.
Made with π
