How to Add a Map to Your App: MapLibre for Web, React Native and Flutter
Who This Guide Is For
You want a map on your website, in your React project or inside your iOS/Android app, and you would rather not wire your product to an SDK that bills per request. This guide walks through a working Hitit Medya Maps setup on four platforms: plain HTML, React/Next.js, React Native and Flutter.
The web example was executed end to end before publishing; the endpoints and the licence key flow were tested against the live servers. The native examples follow the packages' official APIs. Copy, add your credentials, done.
30 SECOND SUMMARY
A map has three moving parts: the style JSON that defines the look, the TileJSON that describes the data contract, and the vector tiles that are the map itself. MapLibre wires those together for you; your only job is one line pointing at the style URL. In the browser your identity is your domain, no key required. Native apps have no browser and therefore no domain, so identity travels as a licence key shaped like hm_live_.... Both are set up in minutes through an access request.
Meet the Three Parts
MapLibre GL is an open source map engine; it does the job of the Google Maps SDK without locking you into an ecosystem. We host everything the engine needs:
- 01
Style JSON: how the map looks
Which layer gets which colour, which font goes where. We ship 13 ready themes; pick one from the gallery or have one designed to match your brand.
- 02
TileJSON: the data contract
A small JSON announcing tile addresses, zoom range and the layer schema. Our access gate lives here, and map loads are counted here.
- 03
Vector tiles: the map itself
Built from OpenStreetMap data, served from Cloudflare's global network. One map load fetches around 30 tiles; they all count as a single view, not thirty.
Adding a Map to a Website (Plain HTML)
The shortest path. If your domain is licensed you need no other credential; our server compares the requesting domain against the licence list.
<link href="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.css" rel="stylesheet" />
<script src="https://unpkg.com/maplibre-gl@5/dist/maplibre-gl.js"></script>
<div id="map" style="height: 420px"></div>
<script>
const map = new maplibregl.Map({
container: "map",
style: "https://tiles.hititmedya.com/v1/styles/hitit-light.json",
center: [32.85, 39.93],
zoom: 5.2,
});
map.addControl(new maplibregl.NavigationControl());
</script>
Swap hitit-light for any style id from the gallery: hitit-dark, hitit-grayscale, hitit-piri-reis...
If your domain is not licensed yet the map will not load and the console will show 403 access_denied; the response body points you straight to the access page. If your code is correct, the same page starts working the moment the licence is registered.
React and Next.js
Install the package: npm install maplibre-gl. The map is a DOM library, so the component must run on the client; with the Next.js App Router a "use client" directive at the top of the file is all it takes.
"use client";
import { useEffect, useRef } from "react";
import maplibregl from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
export function Map() {
const box = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!box.current) return;
const map = new maplibregl.Map({
container: box.current,
style: "https://tiles.hititmedya.com/v1/styles/hitit-light.json",
center: [32.85, 39.93],
zoom: 5.2,
});
return () => map.remove();
}, []);
return <div ref={box} style={{ height: 420 }} />;
}
The map.remove() in the cleanup matters: React 19 StrictMode runs effects twice in development, and a map that is never removed leaks a WebGL context.
Performance tip: initialise the map when it approaches the viewport, not on first paint. On our own site the MapLibre bundle is lazy loaded behind an IntersectionObserver; the engine only downloads when the user scrolls near the map.
Identity in Native Apps: the Licence Key
Everything so far relied on your domain. A native app has no browser, so requests carry no domain either. On iOS and Android, identity is a licence key carried in the URL:
hm_live_4jzqi3295c02xmrxxi22hl7h production key
hm_test_... development key
Only one step differs from the web setup: fetch the style JSON and append your key to the data source address inside it. Our server then propagates the key into the tile URLs of the TileJSON response, so that single line authenticates the whole request chain.
const res = await fetch("https://tiles.hititmedya.com/v1/styles/hitit-light.json");
const style = await res.json();
style.sources.hitit.url += "?key=hm_live_YOUR_KEY";
React Native
The community's official package is @maplibre/maplibre-react-native. Prepare the style as above and hand it to the component:
import { useEffect, useState } from "react";
import { MapView, Camera } from "@maplibre/maplibre-react-native";
const STYLE_URL = "https://tiles.hititmedya.com/v1/styles/hitit-light.json";
const KEY = "hm_live_YOUR_KEY";
export function Map() {
const [style, setStyle] = useState(null);
useEffect(() => {
fetch(STYLE_URL)
.then((r) => r.json())
.then((s) => {
s.sources.hitit.url += `?key=${KEY}`;
setStyle(JSON.stringify(s));
});
}, []);
if (!style) return null;
return (
<MapView style={{ flex: 1 }} mapStyle={style}>
<Camera centerCoordinate={[32.85, 39.93]} zoomLevel={5.2} />
</MapView>
);
}
Package APIs shift between versions; if a prop name differs, check the official docs in the sources below. What never changes is the identity model: appending ?key= to the source address.
Flutter
Package: maplibre_gl. Same pattern, in Dart:
import 'package:http/http.dart' as http;
import 'package:maplibre_gl/maplibre_gl.dart';
const styleUrl = 'https://tiles.hititmedya.com/v1/styles/hitit-light.json';
const key = 'hm_live_YOUR_KEY';
Future<String> prepareStyle() async {
final res = await http.get(Uri.parse(styleUrl));
return res.body.replaceFirst(
'https://tiles.hititmedya.com/planet.json',
'https://tiles.hititmedya.com/planet.json?key=$key',
);
}
// inside build:
MapLibreMap(
styleString: style, // result of prepareStyle()
initialCameraPosition: const CameraPosition(
target: LatLng(39.93, 32.85),
zoom: 5.2,
),
)
An Honest Note on Key Security
In every client side map service the key ends up discoverable inside the app bundle; that is true for the Google Maps SDK and it is true for us. The security model rests not on the key staying secret but on it being manageable:
- Every key is tied to a label, and its usage is tracked separately on our dashboard.
- Suspect a leak? Tell us; we suspend the key in seconds and issue a new one. Your next app release ships with the new key.
- Do NOT use keys in browser projects; domain identity is both safer and cache friendly. Keys are for native only.
Pricing and Access
Hitit Medya Maps is a licensed service; plans start with Starter at 100k map views per month, and higher tiers include custom map design. Current numbers and the request form live on the access page. If you are weighing this against Google Maps, we wrote up the trade-offs in this guide as well.
HITIT MEDYA MAPS
See it before you wire it
13 styles, live previews and the full technology stack in the open. Evaluate first, decide after.
Custom style design is included free on Scale and above.
Frequently Asked Questions
01Can I use the maps for free?
No, Hitit Medya Maps is a licensed service. We do not run a free tier because every request has an infrastructure cost, and we cover that cost with a transparent price rather than ads or your data. Plans start at 19 dollars a month.
02Where do I get a licence key?
Fill in the form on the access page; for web projects we license your domain, for native apps we generate and deliver your hm_live_ key. A hm_test_ key can be issued for the development phase.
03How is this different from the Google Maps SDK?
On the engine side MapLibre is open source and does not lock you in. On the service side we price by view bundles rather than per request, and the map's look can be designed entirely around your brand. We do not offer directions, live traffic or Street View; for those jobs we recommend Google.
04Which platforms are supported?
Anywhere MapLibre runs: browsers, React and every JS framework, React Native, Flutter, plus the native MapLibre SDKs for iOS and Android. Because the infrastructure serves standard TileJSON and vector tiles, your choice of engine does not depend on us.