3D

The Mapbox Standard style renders 3D buildings, landmarks, and trees with dynamic lighting - no styling work required.

Standard 3D

Point mapStyle at the Standard style and you get extruded buildings, landmark meshes, 3D trees, and terrain out of the box. Add pitch to actually see them.

"use client";

import { useState } from "react";

import { Button } from "@/components/ui/button";
import { Map, type LightPreset } from "@/components/ui/map";

const presets: LightPreset[] = ["day", "dusk", "dawn", "night"];

// Downtown Austin. Interaction is constrained: a small zoom range and a tight
// pan box so the camera can be nudged but not flung around or zoomed deep into
// heavy landmark meshes.
const camera = {
  center: [-97.7426, 30.2668] as [number, number],
  zoom: 15.8,
  minZoom: 14,
  maxZoom: 15.8,
  pitch: 52,
  bearing: -20,
  maxBounds: [
    [-97.78, 30.24],
    [-97.7, 30.3],
  ] as [[number, number], [number, number]],
};

export function Standard3DExample() {
  const [preset, setPreset] = useState<LightPreset>("day");

  return (
    <div className="relative h-[520px] w-full">
      <Map
        mapStyle="mapbox://styles/mapbox/standard"
        lightPreset={preset}
        center={camera.center}
        zoom={camera.zoom}
        minZoom={camera.minZoom}
        maxZoom={camera.maxZoom}
        maxBounds={camera.maxBounds}
        pitch={camera.pitch}
        bearing={camera.bearing}
        pitchWithRotate={false}
        dragRotate={false}
      />
      <div className="bg-background/80 absolute top-3 left-3 z-10 flex gap-1 rounded-md border p-1 backdrop-blur">
        {presets.map((value) => (
          <Button
            key={value}
            type="button"
            size="sm"
            variant={preset === value ? "default" : "ghost"}
            className="capitalize"
            onClick={() => setPreset(value)}
          >
            {value}
          </Button>
        ))}
      </div>
    </div>
  );
}

The camera above is deliberately constrained - a narrow zoom range and a maxBounds box - so the map can be nudged but not flung across the world or zoomed deep into heavy landmark geometry.

Light Presets

Standard ships four light presets. They change colour temperature, shadow direction, and lighting intensity across the whole map:

<Map
  mapStyle="mapbox://styles/mapbox/standard"
  lightPreset="dusk"
  pitch={52}
/>

A common pattern is to drive the preset from the clock - dawn 6-8, day 8-17, dusk 17-20, night otherwise - or simply from your site theme.

Loading

A 3D basemap is heavy: it pulls building geometry, landmark meshes, and terrain before the first frame looks right. <Map> handles this for you - children are only rendered once the map has loaded, and a spinner overlay covers the container until then.

Pass loader to supply your own overlay, or false to opt out entirely:

<Map
  mapStyle="mapbox://styles/mapbox/standard"
  loader={<MySkeleton />}
/>

Performance

Keep heavy 3D on pages where the visitor has opted into it. A Standard 3D map is the most expensive thing on any page it appears on, so mounting one eagerly above the fold - or several at once - is the fastest way to make a site feel slow.

Rule of thumb: one 3D map per page, mounted only when it scrolls into view, behind a loader. Everywhere else, prefer a flat style like light-v11 and let the data be the thing that impresses.

Cost is not a reason to avoid it: switching styles or light presets within a session does not spend extra map loads, and 3D is included on the free tier like everything else.