# useDocyrusMapView URL: /docs/native/hooks/use-docyrus-map-view Map backed by a Docyrus data source. Detects the location field and builds markers from logos, icons or the default pin, with clustering, a viewport filter, basemaps and drag-to-move with reverse geocoding. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-docyrus-map-view ``` **Dependencies:** - [@docyrus/api-client](https://www.npmjs.com/package/@docyrus/api-client) - [@docyrus/app-utils](https://www.npmjs.com/package/@docyrus/app-utils) - [@tanstack/react-query](https://tanstack.com/query/latest) - [react-native-maps](https://github.com/react-native-maps/react-native-maps) - [expo-location (optional)](https://docs.expo.dev/versions/latest/sdk/location/) This is a port of the web hook with the same signature. It builds on [`useDocyrusDataGrid`](/docs/native/hooks/use-docyrus-data-grid) for the saved views, toolbar, filters and search, then requests only the columns the map needs. The rendering is rewritten for native. **`DocyrusMapView`** draws everything on the compound [rn-map](/docs/native/docyrus/map), which uses react-native-maps. It includes: - clustered markers showing a logo, a Docyrus icon or the default pin - callouts - drag-to-move - zoom and locate controls - a basemap switcher - a "Search this area" pill The web names `DocyrusLeafletMapView` and `DocyrusGoogleMapView` are exported as aliases. `DocyrusGoogleMapView` forces the Google provider. ## Usage ```tsx import { useDocyrusClient } from '@docyrus/signin/react-native'; import { DocyrusMapView, useDocyrusMapView } from '@/hooks/docyrus-native/use-docyrus-map-view'; export function OrganizationsMap() { const client = useDocyrusClient(); const mapView = useDocyrusMapView({ client: client!, appSlug: 'base', dataSourceSlug: 'organization', locationFieldSlug: 'map_location', titleFieldSlug: 'name', descriptionFieldSlugs: ['address', 'city', 'status'], markerImageFieldSlug: 'company_logo', markerIconFieldSlug: 'company_icon', enableViewportFilter: true, viewportFilterMode: 'manual', enableMarkerDrag: true, basemaps: ['street', 'satellite', 'hybrid', 'terrain'], googleMapsApiKey: process.env.EXPO_PUBLIC_GOOGLE_MAPS_API_KEY, persistState: true }); return ( <> {mapView.toolbar} ); } ``` ## API Reference ### Options (`UseDocyrusMapViewOptions`) Extends every [`useDocyrusDataGrid`](/docs/native/hooks/use-docyrus-data-grid) option except `data`, `enableItemsQuery`, `showSelectColumn` and `enableRowMarkers`. | Prop | Type | Default | Description | |------|------|---------|-------------| | `client` | `RestApiClient` | — | Authenticated Docyrus API client (required) | | `appSlug` | `string` | — | App slug (required) | | `dataSourceSlug` | `string` | — | Data source slug (required) | | `data` | `TData[]` | — | Pre-loaded rows. When set, the items query is skipped | | `collection` | `DocyrusDataGridCollection` | — | Custom `list` adapter | | `locationFieldSlug` | `string` | first `field-locationSelect` | Location field | | `titleFieldSlug` | `string` | `name`, `title`, … or the first text field | Marker title field | | `descriptionFieldSlugs` | `string[]` | `description`, `details`, `address`, `city`, … | Fields for the callout description | | `markerImageFieldSlug` | `string` | — | Image / file field used as the marker image | | `markerIconFieldSlug` | `string` | `field-icon` / `icon` | Docyrus icon field | | `markerIconColorFieldSlug` | `string` | — | Icon color field (hex, `rgb()` or a Tailwind token) | | `markerIconColor` | `string` | — | Icon color when the color field is empty | | `markerPriority` | `'image' \| 'icon'` | `'image'` | Which one to show when a record has both | | `enableViewportFilter` | `boolean` | `false` | Limits `markers` / `mappedItems` to the viewport | | `viewportFilterMode` | `'auto' \| 'manual'` | `'auto'` | `manual` waits for `searchThisArea()` | | `enableMarkerDrag` | `boolean` | `false` | Makes markers draggable (long-press, then drag) | | `onMarkerMove` | `(marker, newPosition) => void \| Promise` | PATCH location | Replaces the default PATCH | | `markerTemplate` | `string` | — | `{{field_slug}}` template for the callout text | | `getMarker` | `(item, context) => DocyrusMapMarker \| null` | — | Replaces the built-in marker builder | | `defaultCenter` | `DocyrusMapPoint` | `{ lat: 39.9334, lng: 32.8597 }` | Center when there are no markers | | `defaultZoom` | `number` | `6` | Zoom when there are no markers | | `focusedZoom` | `number` | `14` | Zoom for a single marker | | `fitBoundsPadding` | `number` | `48` | Padding in px for the one-time fit to all markers | | `autoSelectSingleMarker` | `boolean` | `true` | Selects the marker automatically when there is only one | | `googleMapsApiKey` | `string \| null` | — | Enables Google reverse geocoding. There is no env fallback on native | | `onMapClick` | `(position: DocyrusMapPoint) => void` | — | Called on a tap on the map, then again with `address` once it resolves | | `basemaps` | `Array \| false` | — | Basemaps in the picker. Omit to hide the picker | | `defaultBasemap` | `string` | first entry | Initial basemap | | `enableGroupMenu` / `enableRowHeightMenu` / `enableDisplayMenu` | `boolean` | `false` | Grid toolbar menus | | `staleTime` | `number` | `30000` | React Query stale time in ms | | `onReload` | `() => void` | — | Called after a toolbar reload | | `persistState` | `boolean \| { storage?: 'session' \| 'local'; key?: string }` | — | Saves the grid's view parameters and the basemap (`…:map`) for each saved view | ### Result (`UseDocyrusMapViewResult`) | Key | Type | Description | |-----|------|-------------| | `markers` / `allMarkers` | `DocyrusMapMarker[]` | Markers after / before the viewport filter | | `items` / `mappedItems` / `unmappedItems` | `TData[]` | All records / records with a location / records without one | | `counts` | `{ total, mapped, unmapped }` | Record counts | | `viewportBounds` / `setViewportBounds` | `DocyrusMapBounds \| null` / setter | Last settled bounds. Wire the setter to `onViewportChange` | | `isViewportStale` | `boolean` | Manual mode: the map has moved since the last search | | `searchThisArea` / `clearViewportFilter` | `() => void` | Applies / clears the viewport filter | | `enableMarkerDrag` / `handleMarkerMove` | `boolean` / `(marker, pos) => Promise` | Drag-to-move | | `availableBasemaps` / `activeBasemap` / `setActiveBasemap` | — | Basemap state | | `locationField` / `titleField` / `descriptionFields` | `DataSourceField` | Resolved fields | | `hasLocationField` | `boolean` | Whether a location field was found | | `requestedColumns` / `resolvedListParams` | — | Query payload | | `center` / `bounds` / `defaultZoom` / `focusedZoom` / `fitBoundsPadding` | — | Camera inputs for `DocyrusMapView` | | `selectedMarkerId` / `selectedMarker` / `setSelectedMarkerId` / `selectMarker` / `clearSelectedMarker` | — | Selection | | `googleMapsApiKey` | `string \| null` | The resolved key | | `onMapClick` | `(position) => void \| undefined` | Passed through from options | | `isLoading` / `error` / `reload` | — | Loading, error and reload | | `table` / `toolbar` / `views` / `fields` / `dataSource` / `activeViewId` / `setActiveViewId` | — | Passed through from `useDocyrusDataGrid` | ### DocyrusMapView props | Prop | Type | Default | Description | |------|------|---------|-------------| | `markers` | `DocyrusMapMarker[]` | — | Markers to render (required) | | `center` | `DocyrusMapPoint` | — | Initial center (required) | | `bounds` | `DocyrusMapBounds \| null` | — | The map fits these bounds once, the first time they are available | | `defaultZoom` | `number` | `6` | Initial zoom without bounds | | `focusedZoom` | `number` | `14` | Zoom used for a single point | | `fitBoundsPadding` | `number` | `48` | Fit padding in px | | `selectedMarkerId` | `string \| null` | — | Highlighted marker | | `onSelectMarker` | `(id \| null) => void` | — | Called on a marker press, and with `null` when the map is tapped | | `onMapClick` | `(position) => void` | — | Called on a tap on empty map, then again with `address` | | `renderPopup` | `(marker) => ReactNode` | — | Custom callout content | | `onEditMarker` | `(marker) => void` | — | Called when the callout is pressed | | `onViewportChange` | `(bounds) => void` | — | Called when the map settles | | `draggableMarkers` | `boolean` | — | Makes markers draggable | | `onMarkerMove` | `(marker, position) => void \| Promise` | — | Called when a marker is dropped | | `allMarkersCount` | `number` | — | Keeps the map mounted when the viewport has no markers | | `basemaps` / `activeBasemap` / `onBasemapChange` | — | — | Layers control; shown when there are 2 or more basemaps | | `renderMarkerIcon` | `(marker, isSelected) => ReactNode` | — | Custom marker view | | `emptyState` | `ReactNode` | `t('ui.mapView.emptyMarkers')` | Shown when there is no data | | `className` | `string` | `h-[420px]` | Container classes. Give it a height | | `provider` | `'default' \| 'google'` | `'default'` | Map provider (native-only) | | `isViewportStale` / `onSearchThisArea` | `boolean` / `() => void` | — | "Search this area" pill (native-only) | | `enableClustering` | `boolean` | `true` | Clusters nearby markers (native-only) | | `showZoomControl` / `showLocateControl` | `boolean` | `true` | Map controls (native-only) | | `googleMapsApiKey` | `string \| null` | — | Reverse-geocoding key for `onMapClick` (native-only) | `DocyrusGoogleMapView` also accepts `apiKey`, `defaultMapType` (`'roadmap' | 'satellite' | 'hybrid' | 'terrain'`) and `getMarkerIcon(marker, isSelected) => string | { url, size } | null`. ### Helpers - `reverseGeocode(lat, lng, apiKey?)` returns `Promise`. It tries the optional `expo-location` peer first, then the Google Geocoding REST API when a key is given, then OpenStreetMap Nominatim. It never throws. - `getExpoLocationModule()` returns the optional `expo-location` module, or `null`. - `DEFAULT_DOCYRUS_BASEMAPS` holds the built-in `street` / `satellite` / `hybrid` / `terrain` definitions. ## Native deltas - There is ONE `DocyrusMapView` on react-native-maps instead of the web Leaflet and Google Maps views. Both web names are exported as aliases. - `DocyrusBasemapDefinition` adds `mapType` (`'standard' | 'satellite' | 'hybrid' | 'terrain'`). The Leaflet tile fields are optional and ignored. - `DocyrusMapMarker` adds `icon` (the raw Docyrus icon id), which is rendered with `DocyrusIcon`. - The map fits to the markers only once, like web, so refetches and drags never move the user's viewport. The viewport is not persisted. - `expo-location` is an **optional peer**. Without it, reverse geocoding uses Google REST (with a key) or Nominatim. ## Translation Keys | Key | Fallback | |-----|----------| | `ui.mapView.emptyMarkers` | No mapped records yet. | | `ui.mapView.searchThisArea` | Search this area | | `ui.mapView.tapToEdit` | Tap to edit | ## Type Exports | Type | Description | |------|-------------| | `UseDocyrusMapViewOptions` / `UseDocyrusMapViewResult` | Hook options and result | | `DocyrusMapMarker` / `DocyrusMapMarkerBuilderContext` / `DocyrusMapMarkerIcon` | Marker types | | `DocyrusMapPoint` / `DocyrusMapBounds` / `DocyrusLocationValue` / `DocyrusNormalizedLocation` | Geometry types | | `DocyrusBasemapId` / `DocyrusBasemapDefinition` / `DocyrusGoogleMapType` / `DocyrusMapProvider` | Basemap types | | `DocyrusMapViewProps` / `DocyrusGoogleMapViewProps` | View props |