# MapLibre Tile: a column-oriented vector tile format rebuilt for GPU rendering

> The MapLibre Tile specification redefines the Mapbox Vector Tile layout as a column-oriented format with recursively applied encodings, up to 6x better compression, support for elevation, m-values and nested complex types, plus decoders in Rust, Java, TypeScript and C++. It suits anyone building or serving planet-scale 2D and 2.5D basemaps, and the specification was deemed stable in October 2025.

**maplibre/maplibre-tile-spec** — Next generation map vector tiles format

- Repository: https://github.com/maplibre/maplibre-tile-spec
- Website: https://maplibre.org/maplibre-tile-spec/
- Stars: 561 · Forks: 74
- Language: Rust
- License: Apache-2.0
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/maplibre-maplibre-tile-spec

## What MLT changes about the tile format

The MapLibre Tile specification is mainly inspired by the Mapbox Vector Tile specification, but has been redesigned from the ground up to address the challenges of rapidly growing geospatial data volumes and complex next-generation geospatial source formats, as well as to make use of the capabilities of modern hardware and APIs.

The design target is stated plainly: MLT is specifically designed for modern and next generation graphics APIs to enable high-performance processing and rendering of large, planet-scale 2D and 2.5D basemaps.

Six features follow from that. Improved compression ratio, up to 6x on large encoded tiles, based on a column oriented layout with recursively applied custom lightweight encodings, which leads to reduced latency, storage and egress costs and improved cache utilisation. Better decoding performance, from fast lightweight encodings which can be used in combination with SIMD and vectorization instructions. Support for linear referencing and m-values, to handle upcoming source formats such as Overture Maps and GeoParquet. Support for 3D coordinates, meaning elevation. Support for complex types, including nested properties, lists and maps. And improved processing performance, based on storage and in-memory formats designed for modern GL APIs so they can be loaded into GPU buffers with little or no additional processing, on both CPU and GPU.

The compression and decoding claims belong together, and that pairing is the interesting part of the design. A format that compresses well but takes a long time to decode has moved the cost rather than removed it. MLT's argument is that a column-oriented layout with simple per-column encodings is fast to decode precisely because it is regular, and that the same regularity is what lets SIMD instructions do the work.

The specification is deemed stable as of October 2025, with the note that as a living standard, experimental features may continue to evolve and that implementations and integrations are being developed, with a reference to the implementation status page for the current state.

## The MVT compatibility rules that will bite you

MLTv1 can represent common MVT content, but its column-oriented layout is stricter than the MVT per-feature tag/value model, and the readme lists what that means concretely.

Layer names must be non-empty. A property must have one type across a layer, so a property that is a string on one feature and a number on another in the same layer is not representable. Missing properties become typed nulls rather than absent keys. Duplicate keys on one feature cannot be represented as multiple values. And optimized encoders may reorder features.

Those five rules are the migration surface, and each one is a place where an existing MVT pipeline can silently produce something MLT cannot express.

The per-property type rule is the one that bites hardest in practice. MVT allows each feature to carry its own value types for a key, and a lot of real data has a field that is a number in most rows and null or string in a few. Under MLT that has to be resolved to one type before encoding, with the outliers coerced or dropped, and the decision has to be made somewhere in your pipeline.

Missing properties becoming typed nulls is the rule that changes the client side, because a client checking for the presence of a key is now checking against a null value instead. Duplicate keys becoming impossible removes a pattern that some encoders used to exploit.

The readme points at the MVT compatibility notes in docs/overview.md for details, and that document is the right first read for anyone planning an MVT to MLT conversion, since it is where the edge cases are written down.

The status note is worth reading too: stable as of October 2025, but experimental features may continue to evolve and implementations and integrations are being developed. So the format has a stability commitment and a moving edge at the same time, and the implementation status page is what tells you which side of that line a given feature sits on.

## Five implementations in one repository, and the just task runner

The repository is not just the specification. The directory structure is the implementation map.

There is /spec for the MLT specification and related documentation. There is /test for test MVT tiles and the expected MLT conversion results. There is /java for a Java encoder converting MVT to MLT as well as a decoder parsing MLT to an in-memory representation. There is /js for a Javascript decoder, and /rust for a Rust decoder.

The top-level entries show how much has grown beyond that list. There are cpp/, ts/, hotpath/, inspector/ and qgis/ directories alongside the original ones, plus a MODULE.bazel, a justfile, a release-plz.toml, a biome.json, a .pre-commit-config.yaml and a SECURITY_POLICY.txt. The inspector is a web tool for looking at tiles, and the qgis directory suggests work toward the QGIS side of the ecosystem.

The build is driven by just, a modern alternative to make, and the readme says the project is easier to develop with it. To get a list of available commands, run just, and to run tests, use just test.

The justfile itself is a module aggregator. It declares mod cpp, mod java, mod inspector, mod rust and mod ts, so each language has its own module of recipes, and the top-level recipes fan out to all of them.

```bash
# Run all tests in every language, including integration tests
test: test-int
    {{just}} rust::test
    {{just}} java::test
    {{just}} ts::test
    {{just}} cpp::test
```

The integration test recipe is the interesting one, because it is how a cross-language format specification keeps its implementations honest.

```bash
# Run integration tests, ensuring that the output matches the expected output
test-int: _clean-int-test _test-run-int (_diff-dirs "test/output" "test/expected")
```

So the integration tests generate output into test/output and diff it against test/expected, and a bless recipe regenerates the expectations.

```bash
# Run integration tests, and override what we expect the output to be with the actual output
bless: _clean-int-test _test-run-int
    {{just}} rust::bless
    # after rust::bless, which regenerates the synthetic tiles the inspector indexes
    {{just}} inspector::bless
    rm -rf test/expected && mv test/output test/expected
```

That is the mechanism that keeps five decoders in agreement. Each implementation decodes the same synthetic tiles, the outputs are compared byte for byte, and blessing accepts a new expected set when a deliberate change is made. The comment about the synthetic tiles the inspector indexes is a small detail that shows the test data is shared with the inspection tool rather than being a separate fixture.

## The inspector, the benchmarks and the docs build

The justfile exposes the rest of the workflow, and three recipes are worth looking at.

The bench recipe runs the benchmarks in three languages.

```bash
bench:
    {{just}} rust::bench
    {{just}} java::bench
    {{just}} ts::bench
```

Benchmarks in more than one language is unusual, and it points at the same concern as the integration tests. The performance claims in the readme are about decode speed, and a claim like the 6x compression improvement only means something if it holds for the implementation people will actually use.

The docs recipes build the documentation site through Docker with zensical.

```bash
docs: inspector::build
    docker run --rm -it -p 8000:8000 -v ${PWD}:/docs zensical/zensical:latest
```

The docs-build variant runs the Rust WebAssembly build first as well as the inspector build, then runs the same container without the interactive flags.

The inspector being a build dependency of the docs is worth noting. The inspector is part of the test data pipeline, so the documentation site depends on the tile inspection tool being built, which in turn means the tool for looking at a tile by hand is developed alongside the spec.

The homepage is maplibre.org/maplibre-tile-spec/, and the specification itself is at the versioned path /specification/v1/ within that site.

For research use, the readme asks you to cite the paper by MLT inventor Markus Tremmel, and it points to slides from FOSS4G 2024 Europe, a talk recording, and the ACM paper itself, MapLibre Tile: A Next Generation Vector Tile Format, by Tremmel and Roland Zink, presented at the 33rd ACM International Conference on Advances in Geographic Information Systems in 2025.

The community channel is the #maplibre-tile-format Slack channel at OSM US, with the invite link given in the readme.

## Licensing, and where MLT sits against MVT

The licensing is split in a way that is convenient to use and worth reading carefully.

All project documentation and specification content is licensed under CC0 1.0 Universal, a Public Domain Dedication. All code is dual licensed under the Apache License v2.0 and the MIT license, at your option. And tile test data in the /test directory has different licenses depending on the source of that data.

That split is the right shape for this kind of project. A specification that anyone can copy into their own implementation without attribution friction is how you get a format adopted, and a permissive dual licence on the reference code lets a company pick whichever of the two it has already cleared with legal.

The contribution terms follow the same pattern and are spelled out rather than left implicit. Unless you explicitly state otherwise, any code contribution intentionally submitted for inclusion is dual licensed as above without any additional terms or conditions, and any documentation or specification contributions are licensed under CC0 1.0 Universal.

The comparison with MVT is the interesting part of the format's story, and the readme is clear that MLT is a successor rather than a replacement in the sense of being compatible. It is inspired by MVT and can represent common MVT content, but the stricter column-oriented layout means the encoding is not interchangeable. An MVT consumer cannot read an MLT tile, and a pipeline that emits MLT cannot feed an existing MVT client.

So the practical decision is not whether MLT is better than MVT. It is whether the decode speed, the compression and the support for elevation, m-values and complex types are worth changing the format your clients read. For a renderer you control, on hardware you control, with tiles you serve yourself, the case is about performance and about the source formats that MVT cannot represent at all. For a project with third-party MVT consumers in the mix, it is not a switch you can flip.

## Conclusion

MapLibre Tile fits a tile pipeline built for planet-scale rendering where decode cost and egress dominate, and it does not fit a project that needs to stay byte-compatible with Mapbox Vector Tile consumers, because the column-oriented layout is stricter than the per-feature tag/value model. Before committing, read the MVT compatibility notes in docs/overview.md, check the implementation status page for what actually ships today, and confirm your tile production and client stack both handle the stricter rules on layer names, property types and missing values.

## FAQ

### What is the MapLibre Tile specification?

MapLibre Tile is a next generation vector tile format, inspired by the Mapbox Vector Tile specification but redesigned from the ground up around a column-oriented layout with recursively applied lightweight encodings, for high-performance rendering of planet-scale 2D and 2.5D basemaps.

### How is MapLibre Tile different from MVT in practice?

MLTv1 can represent common MVT content but is stricter: layer names must be non-empty, a property must have one type across a layer, missing properties become typed nulls, duplicate keys on one feature cannot be multiple values, and optimized encoders may reorder features.

### Which implementations does the maplibre-tile-spec repository contain?

The directory structure lists the specification and docs in /spec, test MVT tiles and expected conversion results in /test, a Java encoder and decoder in /java, a Javascript decoder in /js and a Rust decoder in /rust. The top level also carries cpp/, ts/, inspector/ and qgis/ directories.

### How do I run the MapLibre Tile tests?

The project uses just as its task runner. Run just to list the available commands and just test to run the tests, which fan out to the Rust, Java, TypeScript and C++ modules. The integration test recipe generates output and diffs it against the expected directory, and a bless recipe regenerates those expectations.

## Sources

- [License: Apache-2.0](https://github.com/maplibre/maplibre-tile-spec/blob/main/LICENSE)
- [maplibre/maplibre-tile-spec on GitHub](https://github.com/maplibre/maplibre-tile-spec)
- [Project website](https://maplibre.org/maplibre-tile-spec/)
- [README](https://github.com/maplibre/maplibre-tile-spec/blob/main/README.md)
- [Releases](https://github.com/maplibre/maplibre-tile-spec/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/maplibre-maplibre-tile-spec
