Switching the map design (tiles)
How the map looks is decided by a single mapDesignType. Pick one of the presets shipped for each provider, or define your own from a style JSON or tile URL. Switching is just an assignment — the map view is never rebuilt.
01 · The design type
Every provider design implements MapDesignTypeInterface (MapDesignTypeProtocol on iOS). Learn these three members and the handling stays identical across providers.
02 · Presets
Each provider package exposes its standard designs as constants. The sample apps use these lists directly as their options.
iOS · React
React: MapboxDesign
React は Streets · Outdoors · Light · Dark · SatelliteStreets + OsmBright / MapTiler 系のスタイルJSON
iOS / React: ArcGISMapDesign
React: TomTomDesign
React はさらに StandardLight / Dark · DrivingLight / Dark · MonoLight / Dark
React
React
React
React
Only the rows that differ per platform are annotated; everywhere else the type name and the preset constant names are the same on all three platforms.
03 · Switching
Assign to mapDesignType on the map view state. The state is observed, so the map changes the moment you assign.
// state は MapViewStateInterface<*>。プロバイダごとの型に絞って代入する
when (state) {
is GoogleMapViewStateInterface ->
state.mapDesignType = GoogleMapDesign.Satellite
is MapLibreViewStateInterface ->
state.mapDesignType = MapLibreDesign.OsmBrightJa
is MapTilerViewStateInterface ->
state.mapDesignType = MapTilerDesign.Toner
}// 初期デザインは State の生成時に渡す
@StateObject private var mapLibreState = MapLibreViewState(
mapDesignType: MapLibreDesign.DemoTiles,
cameraPosition: viewModel.initCameraPosition
)
// 切り替えは @Published なプロパティへの代入だけ
mapLibreState.mapDesignType = MapLibreDesign.OsmBrightJaconst [mapViewState, setMapViewState] =
useState<MapViewStateInterface<MapDesignTypeInterface<unknown>> | null>(null);
const handleDesignChange = (designId: string) => {
const option = mapDesignOptions.find(item => item.design.id === designId);
if (!mapViewState || !option) return;
mapViewState.mapDesignType = option.design;
};04 · Defining a custom design
The preset types are the public constructors too. Give an id plus a style JSON URL (vector) or a tile URL template (raster) and your own style is treated exactly like a preset.
// MapLibreDesign は data class。スタイル JSON の URL を渡せば独自デザインになる
val GsiPale =
MapLibreDesign(
id = "gsi-pale",
styleJsonURL = "https://example.com/styles/gsi-pale/style.json",
attributionRules =
listOf(
AttributionRule(attribution = "地理院タイル"),
AttributionRule(attribution = "GEBCO", minZoom = 5, maxZoom = 8),
),
)private val mapLibreDesigns =
listOf(
MapDesignOption(label = "OsmBright", design = MapLibreDesign.OsmBright),
MapDesignOption(label = "GSI Pale", design = GsiPale),
)// ネイティブの mapType 定数をそのまま包み、出典表記だけ足す
val NormalWithNotice =
GoogleMapDesign.Custom(
id = MAP_TYPE_NORMAL,
attributionRules = listOf(AttributionRule(attribution = "自社データ 2026")),
)Where the map kind is a native constant — Google Maps — use GoogleMapDesign.Custom, passing the constant as id and supplying your own attribution. MapTiler, Mapbox and HERE expose constructors of the same shape.
// public init(id:styleJsonURL:attributionRules:) がそのまま独自デザインの入口
let gsiPale = MapLibreDesign(
id: "gsi-pale",
styleJsonURL: "https://example.com/styles/gsi-pale/style.json",
attributionRules: [
AttributionRule(attribution: "地理院タイル"),
AttributionRule(attribution: "GEBCO", minZoom: 5, maxZoom: 8),
]
)private let mapLibreDesigns = [
MapDesignOption(label: "DemoTiles", design: MapLibreDesign.DemoTiles),
MapDesignOption(label: "GSI Pale", design: gsiPale),
]
// 適用時はプロバイダの型にキャストする
if let design = option.design as? MapLibreDesign {
mapLibreState.mapDesignType = design
}Being a struct, define it as a let and share it anywhere. The ViewModel option holds design: Any, so narrow it with as? when applying.
const GSI_STANDARD_DESIGN = new LeafletDesign({
id: 'gsi-standard',
tileUrl: 'https://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png',
tileOptions: { minZoom: 5, maxZoom: 18, tileSize: 256 },
attributionRules: GSI_STANDARD_ATTRIBUTION_RULES,
});
export const LEAFLET_DESIGNS: MapDesignOption[] = [
{ label: 'OpenStreetMap', design: LeafletDesign.OpenStreetMap },
{ label: 'GSI Standard', design: GSI_STANDARD_DESIGN },
{ label: 'None', design: LeafletDesign.None },
];// new MapLibreDesign(id, styleJsonURL, attributionRules?)
const GSI_PALE = new MapLibreDesign(
'gsi-pale',
'https://example.com/styles/gsi-pale/style.json',
[{ attribution: GSI_ATTRIBUTION }],
);
export const MAPLIBRE_DESIGNS: MapDesignOption[] = [
{ label: 'OsmBrightJa', design: MapLibreDesign.OsmBrightJa },
{ label: 'GSI Pale', design: GSI_PALE },
];Leaflet designs are raster tiles, MapLibre designs are style JSON. Design metadata lives in the same package as each provider heavy runtime SDK, so the sample imports it per provider on demand to keep the chunks separate.
05 · Attribution rules
When you bring your own tiles, carry the provider attribution conditions as rules. Only the rules that apply to the current zoom and viewport are shown.
06 · Sample apps
All three sample apps ship a design switcher. The UI is idiomatic per platform; only the assignment is shared.
val mapViewState = rememberMapLibreMapViewState(
mapDesign = MapLibreDesign.DemoTiles,
cameraPosition = MapCameraPosition(
position = GeoPoint.fromLatLong(21.382314, -157.933097),
zoom = 12.0,
),
)
val designs = listOf(
"Demo" to MapLibreDesign.DemoTiles,
"OSM Bright" to MapLibreDesign.OsmBrightJa,
"Toner" to MapLibreDesign.MapTilerTonerJa,
)
MapLibreMapView(state = mapViewState, modifier = Modifier.weight(1f))
Row {
designs.forEach { (label, design) ->
// Just an assignment. The map is not rebuilt, so the camera stays where it is
Button(onClick = { mapViewState.mapDesignType = design }) { Text(label) }
}
}