npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@yoyomobility/expo-yandex-maps

v1.2.2

Published

Yandex Maps SDK for Expo

Readme

expo-yandex-maps

Yandex Maps SDK for Expo

API documentation

Traffic-aware routes (v1.1.0+)

Three primitives for traffic-aware route rendering. Color mapping stays on the app side — the library never hardcodes colors.

Per-segment polyline colors

<PolylineView
  points={section.points}
  strokeColor="#808080" // fallback when strokeColors is not set
  strokeColors={segmentColors} // length MUST be points.length - 1
  gradientLength={15} // meters of smooth transition between segments, default 0 (hard edges)
/>

strokeColors overrides strokeColor when set. On a length mismatch the library warns in dev and falls back to the scalar strokeColor.

Jam data in findRoutes

Driving sections (vehicles: ['car']) include a jams array with one JamType ('unknown' | 'blocked' | 'free' | 'light' | 'hard' | 'veryHard') per segment: jams.length === points.length - 1, aligned with the returned (simplified) geometry — segments merged by simplification aggregate worst-wins. The field is omitted when MapKit returns no jam data (offline routing, regions without coverage). jams is a snapshot taken when the route was built — refetch the route to refresh it.

const { routes } = await map.current.findRoutes(points, ["car"], {});
const section = routes[0].sections[0];
// section.jams?.[i] describes the segment between points[i] and points[i + 1]
section.jams?.map((jam) => myTheme.jamColors[jam]); // → strokeColors

simplificationPreset is now honored on both platforms (previously iOS ignored it and returned the full geometry), so iOS and Android produce the same segments.

Traffic level without the tiles overlay

await map.current.setTrafficDataEnabled(true); // subscribe to onTrafficChanged, no tiles
await map.current.setTrafficVisible(true); // tiles overlay, independent of the above

// onTrafficChanged={(e) => e.nativeEvent}
// { status: 'changed' | 'loading' | 'expired',
//   data?: { level: number /* 0..10 */, color: 'red' | 'yellow' | 'green' } }

data.level describes the congestion of the visible map region (like the badge in Yandex Maps), not of a route. setTrafficVisible and setTrafficDataEnabled are independent: disabling one never drops the subscription held by the other, and repeated calls never duplicate it.

setTrafficStyle(style: string): Promise<boolean> applies a MapKit JSON style to the traffic tiles, e.g. [{"stylers": {"opacity": 0.35, "saturation": -0.7}}].

Geocoder error handling (v1.1.0+)

geocode / reverseGeocode no longer hang silently: failed HTTP requests reject with GeocodingApiError carrying the HTTP status and the parsed Yandex body (yandexResponse), e.g. Geocoding request failed with status 403: Invalid api key. Requests time out after GeocodingApi.REQUEST_TIMEOUT_MS (15 s by default, status: 0), and a missing geocodeKey fails fast with a descriptive message.

import { GeocodingApiError } from "expo-yandex-maps";

try {
  const address = await map.geocode(point);
} catch (e) {
  if (e instanceof GeocodingApiError) console.warn(e.status, e.message);
}

Note: the HTTP Geocoder needs its own key of type "JavaScript API and HTTP Geocoder" from the Yandex Developer console — a MapKit SDK key returns 403.

Camera and marker results (v1.2.0+)

Every camera and marker animation promise now resolves with the outcome of the movement instead of undefined (additive — callers that ignore the result are unaffected):

const { completed, position } = await map.setCameraPosition(target, 0.5, 0);
// completed: false — the flight was interrupted (user gesture or the next
// programmatic move); position — the camera at resolve time.

const fit = await map.fitMarkers(routePoints);
// FitMarkersResult extends CameraMoveResult with an atomic measurement taken
// inside the native callback (no bridge race between getScreenRect and
// getScreenPoints):
// fit.focusRect     — the focus rect the content was fitted into (physical px)
// fit.screenBounds  — screen bbox of the fitted points, or null when a bbox
//                     corner is not projectable (tilt + interrupted flight)

const res = await map.focusRect(rect, 0.4, { fitVisible: true });
// res.completed   — false when superseded by the next focusRect call
// res.appliedRect — the rect actually applied after clamping to the window;
//                   null — the rect was dropped as degenerate

const move = await marker.animatedMoveTo(point, 500); // { completed, point }
const turn = await marker.animatedRotateTo(90, 300); // { completed, angle }
// completed: false — superseded by the next command or the marker unmounted;
// never a rejection.

Camera movement lifecycle events

onCameraPositionChangeStart fires once at the beginning of a logical camera movement, onCameraPositionChangeEnd once at its end. Per-frame moves produced by the declarative focusRect stream (Reanimated + bottom sheet) coalesce natively: intermediate ends are debounced ~200 ms and collapse into a single start/end pair per drag, so consumers like "geocode on camera stop" or isMapMoving flags don't fire 60 times a second. onCameraPositionChange stays per-frame and, since 1.2.0, fires for every native camera callback on both platforms, finished: true ones included (previously iOS skipped change for finished callbacks, which made duration-0 moves invisible to it) — filter by finished if you only want intermediate frames. Gestures and ordinary programmatic moves are not affected. If a new movement starts while a coalesced end is still pending, that end flushes immediately, so it never fires mid-movement with a stale position.

Camera events carry reason: "GESTURES", "APPLICATION", or "FOCUS_RECT" for moves produced by the declarative focusRect stream — uniform across platforms since 1.2.0 (migration note: iOS previously sent lowercase "gestures"/"application"). Filter on it when a "map is moving" indicator should ignore the sheet dragging the rect:

onCameraPositionChange={(e) => {
  if (e.nativeEvent.reason !== "FOCUS_RECT") setIsMapMoving(true);
}}

Resetting focusRect

Setting the focusRect prop to null (or removing it) now resets the native focus rect entirely — previously null was ignored and the only "reset" was sending a full-window rect. The reset deliberately skips the fitVisible zoom compensation: it releases the camera, it does not reframe.

initialRegion is applied once

initialRegion moves the camera only on the first application and ignores re-application — expo-modules re-applies the whole props bag on every view update, which used to snap the camera back to the initial region on unrelated prop changes (and produced per-frame Cannot set prop 'initialRegion' conversion errors on Android after the prop was removed from JSX). Workarounds that removed the prop after the map loaded are no longer needed.

Prop application semantics (v1.2.1+)

On the New Architecture expo-modules re-applies the whole props bag on every view update — during a focusRect stream from a bottom sheet or any JS-driven animation inside the map subtree that is every frame. Two rules make this safe:

  • Re-applying the same value is a native no-op. Every heavy prop handler (mapStyle, userLocationIcon*, showUserPosition, followUser, nightMode, mapType, all MarkerView/PolylineView/PolygonView/ CircleView props) compares against the applied value and skips repeats. Before 1.2.1 mapStyle restyled the tiles per frame (visible flicker + gesture stutter) whenever anything in the map subtree animated, and a route polyline rebuilt its geometry and per-segment color palette per frame. Real changes pass through, so Reanimated animatedProps on any of these components keep working — each animation frame carries a new value.
  • Removing a prop from JSX keeps the last applied value. A removed prop stays in the Fabric props bag as null forever; all handlers ignore null instead of throwing per-pass conversion errors. Conditional passing ({...(cond && { mapStyle })}) is legal. To reset something, pass an explicit value — the exceptions with real null semantics are focusRect (null resets the native focus rect) and initialRegion (apply-once). Props declared with a native default (scrollGesturesEnabled and the other gesture toggles) fall back to that default when removed instead.

Also since 1.2.1: showUserPosition={false} / followUser={false} no longer create the user-location layer, and out-of-order icon downloads (userLocationIcon, marker source) can't overwrite a newer request.

Marker and camera improvements (v1.1.0+)

  • MarkerView: declarative direction prop (placemark azimuth in degrees, use with rotated) and onMarkerReady event fired when the native placemark is attached and imperative commands are safe to call.
  • animatedMoveTo / animatedRotateTo and camera methods (setCameraPosition, setCenter, setZoom, fitMarkers, fitAllMarkers) resolve their promises when the animation actually finishes, not when it starts.
  • Marker animation commands no longer produce unhandled promise rejections when the marker unmounts mid-animation.

Animated focusRect

MapKit applies focusRect instantly (the SDK has no animated setter), which looks like a jump when a bottom sheet opens. focusRect now interpolates the rect natively and can fit content into the final rect in the same motion:

await map.current.focusRect(
  rect, // ScreenRect, in physical pixels as before
  0.4, // seconds; omit or 0 → instant (previous behavior)
  { fitPoints: routePoints }, // camera animates in parallel so these points fit the final rect
);

// fitVisible keeps what the user currently sees instead of framing given
// points: the zoom shifts by log2 of the rect size ratio — symmetric and
// exactly reversible when the sheet goes back down.
map.current.focusRect(rect, 0, { fitVisible: true });

The promise resolves when the transition finishes. A new focusRect call cancels the previous animation.

For a map glued to a bottom sheet in real time use the declarative props on AnimatedMapView instead — values flow from Reanimated's UI thread with no JS-thread hop per frame:

const sheetPosition = useSharedValue(0); // <BottomSheet animatedPosition={sheetPosition} />
const scale = PixelRatio.get();

const focusRectProps = useAnimatedProps(() => ({
  focusRect: {
    topLeft: { x: 0, y: 0 },
    bottomRight: { x: width * scale, y: (sheetPosition.value > 0 ? sheetPosition.value : height) * scale },
  },
}));

<AnimatedMapView animatedProps={focusRectProps} focusRectFitVisible ... />

Live marker content: tracksViewChanges

The placemark renders a snapshot of its children, so animated child views (pulse effects) and async images freeze at the moment of capture — that is why an Image inside a marker used to need a key remount hack. While tracksViewChanges is set, the snapshot is retaken every UI frame:

<MarkerView point={point} tracksViewChanges={pulsing || !imageLoaded}>
  <Image source={{ uri }} onLoad={() => setImageLoaded(true)} />
</MarkerView>

Keep it enabled only while the content actually changes — each frame costs a main-thread view render (same trade-off as tracksViewChanges in react-native-maps). Default false.

Installation in managed Expo projects

For managed Expo projects, please follow the installation instructions in the API documentation for the latest stable release. If you follow the link and there is no documentation available then this library is not yet usable within managed projects — it is likely to be included in an upcoming Expo SDK release.

Installation in bare React Native projects

For bare React Native projects, you must ensure that you have installed and configured the expo package before continuing.

Add the package to your npm dependencies

npm install expo-yandex-maps

Configure for Android

Configure for iOS

Run npx pod-install after installing the npm package.

Contributing

Contributions are very welcome! Please refer to guidelines described in the contributing guide.