Gotchas
Platform-specific behavior and common pitfalls when using mapcn-react-native.
Using Maps Inside a ScrollView (Android)
On Android, embedding a Map inside a React Native ScrollViewcan cause gesture conflicts. Vertical scroll gestures from the parent ScrollView often "win" over map pan and zoom, so the map flickers while the screen scrolls.
ScrollView. If your map fills the screen (no parent ScrollView), you do not need any special handling.Solution: ScrollView Gesture Wrapper
The simplest fix is to temporarily disable the parent ScrollView while the user is interacting with the map area. You can do this with a small wrapper component that notifies the parent when touch events start and end.
In the example app, this helper is implemented as ScrollViewMapWrapper and used around all maps inside a ScrollView. It is kept local to the example app and is not part of the public library API.
Example: ScrollView Wrapper Pattern
This example shows the core pattern using a dedicated wrapper around the map container. The ScrollView uses a scrollEnabled state that is toggled based on touch events on the map area.
import { useState } from "react";
import { ScrollView, View, Text } from "react-native";
import { Map } from "@/components/ui/map";
import { ScrollViewMapWrapper } from "@/components/scroll-view-map-wrapper";
export function MapInScrollView() {
const [scrollEnabled, setScrollEnabled] = useState(true);
return (
<ScrollView className="flex-1" scrollEnabled={scrollEnabled}>
<View className="px-6 py-8 w-full gap-6">
<View>
<Text className="text-3xl font-bold mb-2">Map in ScrollView</Text>
<Text className="text-muted-foreground">
Scroll the page outside the map, pan and zoom inside the map.
</Text>
</View>
<ScrollViewMapWrapper
onScrollEnabledChange={setScrollEnabled}
className="h-[500px] rounded-xl overflow-hidden border border-border"
>
<Map zoom={12} center={[-122.4194, 37.7749]} />
</ScrollViewMapWrapper>
</View>
</ScrollView>
);
}Wrapper Implementation (Example App)
This is the implementation used in the example app. You can copy this into your project and adapt it as needed. It works for both MapLibre and Mapbox versions of the components.
import React, { useCallback, ReactNode } from "react";
import { View, ViewStyle } from "react-native";
interface ScrollViewMapWrapperProps {
children: ReactNode;
onScrollEnabledChange?: (enabled: boolean) => void;
style?: ViewStyle;
className?: string;
}
// Helper for using map components inside a ScrollView without gesture conflicts
export function ScrollViewMapWrapper({
children,
onScrollEnabledChange,
style,
className,
}: ScrollViewMapWrapperProps) {
const handleTouchStart = useCallback(() => {
onScrollEnabledChange?.(false);
}, [onScrollEnabledChange]);
const handleTouchEnd = useCallback(() => {
onScrollEnabledChange?.(true);
}, [onScrollEnabledChange]);
return (
<View
style={style}
className={className}
onTouchStart={handleTouchStart}
onTouchEnd={handleTouchEnd}
onTouchCancel={handleTouchEnd}
>
{children}
</View>
);
}Notes and Best Practices
- Set a fixed height for the map container when used inside a ScrollView (for example,
h-[400px]orh-[500px]). - Always re-enable scrolling in both
onTouchEndandonTouchCancelto avoid leaving the ScrollView disabled after an interrupted gesture. - Wrap the immediate parent of the map rather than the entire screen, so only the map area controls the scroll locking behavior.
- Test on physical Android devices where gesture behavior is most noticeable. iOS generally behaves better with nested gestures, but using this pattern keeps behavior consistent across platforms.