Custom layers & expressions
Layer style objects and style-spec expressions are shared verbatim across both renderers -- no translation layer needed.
Shared style keys
Both MapLibre and Mapbox React Native bindings use the same camelCased paint/layout property names (lineColor, fillOpacity, circleRadius, heatmapWeight, ...) and the same expression grammar. This means a raw layer style object written for one renderer works unmodified on the other -- mapcn's style helpers (circlePaintFrom, linePaintFrom, fillPaintFrom in lib/mapcn/style.ts) produce these shared-shape objects directly, with no per-renderer branching.
Expressions
Expression (lib/mapcn/types.ts) is kept intentionally loose (Array<any>) rather than modeling the full style-spec grammar -- both renderers already implement and validate it, so mapcn doesn't re-implement that validation.
// Property-driven color
{ color: ["get", "category"] }
// Zoom-stop interpolation
["interpolate", ["linear"], ["zoom"], 10, 12, 16, 30]
// Discrete steps (what ClusterStep / choropleth scales compile to)
["step", ["get", "point_count"], "#4285F4", 100, "#F4B400", 750, "#DB4437"]StyleValue<T> (T | Expression) is the type most style props use, so any point/line/fill/text style field can be either a constant or an expression.
Adding a raw layer
Use MapSource/MapLayer directly (the same primitives MapGeoJSON/MapRoute/MapClusterLayer are built on) for anything mapcn's higher-level components don't cover:
import { MapLayer, MapSource } from "@/components/ui/mapcn/map-renderer";
<MapSource id="custom" data={geojson}>
<MapLayer id="custom-fill" type="fill" style={{ fillColor: ["get", "color"], fillOpacity: 0.4 }} />
</MapSource>;MapLayer's type covers the unified layer surface: "fill" | "line" | "circle" | "symbol" | "heatmap".
Live example
