# React Location Picker: Pin ziehen, Radius, Suche

> Bau einen React Location Picker auf einer echten Karte: Pin zum Ziehen, Radius mit Slider, Adresssuche, Sheet am Smartphone und statischer Fallback.

- Autor: [Serban Rusu](https://wingo-ui.com/blog/authors/serban), Founder of Wingo UI
- Veröffentlicht: 19. Okt. 2026
- Kategorie: [Komponenten-Guides](https://wingo-ui.com/de/blog/category/components)
- Lesezeit: 9 Min.
- Kanonische URL: https://wingo-ui.com/de/blog/react-location-picker
- Englische Fassung: https://wingo-ui.com/blog/react-location-picker

## Kurz gesagt

Ein React Location Picker braucht einen Pin, den du per Klick setzt, ziehst und mit der Tastatur verschiebst, eine Suche, die die Karte zu einem Ort fliegen lässt, und optional einen Radius für Liefergebiete oder Umkreisfilter. Außerdem muss er die Karten-Library nur im Browser laden, das Scrollen am Smartphone nicht blockieren und etwas Sinnvolles zeigen, wenn die Kacheln nie ankommen. Die Location Map von Wingo UI erledigt das auf Leaflet und OpenStreetMap mit kontrolliertem value und radius, dazu StaticMap als statische Ansicht für E-Mails und lange Listen.

Du brauchst einen React Location Picker: ein Inseratsformular, das fragt, wo die Wohnung liegt, einen Checkout, der fragt, wo das Paket abgelegt werden soll, eine Suchseite, die nach Entfernung filtert. Ein Textfeld für die Adresse reicht, bis das Gebäude keine Hausnummer hat, der Kurier den Hintereingang braucht oder der Filter „im Umkreis von 10 Meilen“ lautet. Dann brauchst du eine echte Karte mit Pin. Dieser Guide zeigt, was ein Picker können muss und auf welche Props es bei der [Location Map](https://wingo-ui.com/components/location-map) von Wingo UI ankommt, und liefert komplette Beispiele für Pin, Radius und Adresssuche.

## Was muss ein React Location Picker können?

Sieben Dinge. Wer nach „pick location on map react“ sucht, weil Nutzer einen Standort auf der Karte auswählen sollen, findet meist einen Click-Handler mit `useMapEvents` aus react-leaflet, der einen Marker setzt. Der deckt nur den ersten Punkt ab. Das ist die Liste, an der wir unseren eigenen React Map Picker messen:

1. **Drei Wege, den Pin zu setzen.** Auf die Karte klicken oder tippen, den Pin ziehen und ihn mit den Pfeiltasten bewegen, sobald er den Fokus hat.
2. **Suche.** Ort oder Adresse eintippen, ein Ergebnis wählen, und die Karte fliegt dorthin.
3. **Ein Radius** für Liefergebiete und Umkreisfilter, mit einem Slider, der den Kreis live anpasst.
4. **Mein Standort**, mit einer ehrlichen Meldung, wenn der Browser ablehnt.
5. **Seiten-Scrollen, das weiter funktioniert.** Eine Karte, die das Mausrad oder ein Wischen mit einem Finger abfängt, hält dich mitten auf der Seite fest.
6. **SSR-sicheres Laden.** Leaflet greift beim Import auf `window` zu, und das lässt ein Server-Rendering in Next.js abstürzen.
7. **Ein Fallback**, wenn Kacheln oder der Karten-Chunk nie ankommen: offline, blockiert oder einfach noch nicht ins Bild gescrollt.

Die Punkte 5 und 7 merkst du erst in Produktion. Eine Karte mitten in einem Formular ist am Smartphone eine 300px hohe Fläche, auf der ein Wischen die Karte verschiebt statt die Seite, und eine Karte, die nicht lädt, zeigt einen grauen Kasten ohne jeden Hinweis, wo der Pin war.

## Auf welchen React-Karten-Libraries kannst du einen Location Picker bauen?

Leaflet mit OpenStreetMap braucht keinen API-Key, Google Maps und Mapbox brauchen einen Key oder Token und ein Konto. Die Angaben stammen aus der öffentlichen Doku des jeweiligen Projekts, Stand Oktober 2026.

| Option | Was es ist | Key nötig | Verschiebbarer Marker |
|---|---|---|---|
| [react-leaflet](https://react-leaflet.js.org/) | React-Komponenten für Leaflet (Doku zu v5), [Hippocratic License 2.1](https://github.com/PaulLeCam/react-leaflet/blob/master/LICENSE.md) | Für Leaflet selbst nicht; hängt vom Kachel-Anbieter ab | Leaflets Marker-Option `draggable` und das Event `dragend` |
| [@vis.gl/react-google-maps](https://visgl.github.io/react-google-maps/) | React-Komponenten und Hooks für die Google Maps JavaScript API | Ja, laut Google [braucht die Plattform einen API-Key](https://developers.google.com/maps/documentation/javascript/get-api-key) für Authentifizierung und Abrechnung | Ja, über die Maps JavaScript API |
| [Mapbox GL JS](https://docs.mapbox.com/mapbox-gl-js/example/drag-a-marker/) | Library für Vektorkarten, in React über Wrapper genutzt | Ja, ein Mapbox Access Token | `draggable: true` und ein Listener auf `dragend` |
| [Wingo UI Location Map](https://wingo-ui.com/components/location-map) | Pro-Komponente als Quellcode, auf Leaflet 1.9 und standardmäßig mit OpenStreetMap-Kacheln | Standardmäßig nicht; `tileUrl` zeigt auf jeden Raster-Anbieter | Eingebaut, dazu Klick zum Setzen, Bewegen per Tastatur und ein Radius |

Google ist die bessere Wahl, wenn du die Ortsdaten von Google und weltweite Adress-Autovervollständigung bis auf Straßenebene brauchst und mit der Abrechnung leben kannst. Mapbox passt, wenn du Vektor-Styles willst. Ein React Leaflet Location Picker ist der leichteste Weg zu einem Pin auf einer Karte ohne Konto. Genau darauf baut die Location Map auf: Du bekommst die Kacheln von Leaflet, ohne Klick, Ziehen, Suche und das Verhalten am Smartphone selbst zu schreiben.

## Welche Props braucht die Location Map?

Einen Wert (`value`) und ein `onValueChange`. Der Wert ist ein `GeoPoint` (`{ lat, lng }`) oder `null`, solange noch kein Pin gesetzt ist. Die Karte ruft `onValueChange` auf, wenn ein Ziehen endet, ein Klick den Pin setzt, ein Suchergebnis oder der Standort-Button ihn verschiebt oder die Eingabe per Tastatur kurz ruht.

```bash
npx wingo-ui@latest add location-map
```

Die CLI kopiert `components/ui/location-map.tsx` samt den Komponenten und Helfern, die die Datei importiert, und installiert `leaflet`, `motion` und `lucide-react`.

> Live-Demo (Location Map): Klick auf die Karte, um den Pin zu setzen, zieh ihn auf ein anderes Gebäude und beweg dann den Radius-Slider: Kreis und Ortsanzahl passen sich an. Probier sie unter [Location Map](https://wingo-ui.com/components/location-map) aus und installier sie mit `npx wingo-ui@latest add location-map`.

Die eingebaute Suche läuft offline und kennt rumänische Orte, Gebiete und Kreise aus [ro-locations](https://wingo-ui.com/components/ro-locations), deshalb zeigt die Demo oben Cluj-Napoca. Für eine App in den USA übergibst du ein Zentrum und einen Geocoder, der dein Land kennt:

```tsx
"use client";

import { useState } from "react";
import {
  LocationMap,
  nominatimGeocoder,
  nominatimReverseGeocoder,
  type GeoPoint,
} from "@/components/ui/location-map";

// beide helfer teilen sich eine warteschlange: höchstens eine nominatim-anfrage pro sekunde
const geocoder = nominatimGeocoder({ email: "dev@example.com", countryCodes: "us" });
const reverseGeocoder = nominatimReverseGeocoder({ email: "dev@example.com" });

const NEW_YORK: GeoPoint = { lat: 40.7128, lng: -74.006 };

export function DropOffLocation() {
  const [point, setPoint] = useState<GeoPoint | null>(null);

  return (
    <LocationMap
      value={point}
      onValueChange={setPoint}
      center={NEW_YORK}
      defaultZoom={12}
      geocoder={geocoder}
      reverseGeocoder={reverseGeocoder}
      showTownsInRadius={false}
      locale="en-US"
      labels={{ search: "Adresse in New York suchen" }}
    />
  );
}
```

`nominatimGeocoder` setzt `countryCodes` standardmäßig auf `"ro"`, also gib es an (oder übergib `""` für die ganze Welt). Der Reverse Geocoder benennt den Pin 500ms, nachdem er zum Stehen gekommen ist, mit Straße und Ort. Ohne ihn zeigt die Ortszeile ersatzweise den nächstgelegenen rumänischen Ort, und außerhalb Rumäniens nur die Koordinaten.

Die anderen Props, die du brauchen wirst: `defaultZoom` (13 zeigt ein Viertel, 16 eine Straße), `draggable` und `clickToPlace` (beide standardmäßig an), `readOnly` für eine Filialseite, die nur zeigt, wo der Laden ist, `searchable` und `locate`, um das Suchfeld oder den Standort-Button auszublenden, und `height` (standardmäßig 320px).

## Wie füge ich der Karte einen Radius hinzu?

Übergib einen Radius in Kilometern. Ein Radius für Liefergebiete oder „Inserate in der Nähe“ ist einfach ein weiterer kontrollierter Wert: `radius` und `onRadiusChange`, dazu `radiusRange` und `radiusStep` für den Slider unter der Karte. `null` oder `0` blendet Kreis und Slider aus.

```tsx
"use client";

import { useState } from "react";
import { LocationMap, type GeoPoint } from "@/components/ui/location-map";
import { Button } from "@/components/ui/button";

export function DeliveryZone() {
  const [point, setPoint] = useState<GeoPoint | null>({ lat: 40.7128, lng: -74.006 });
  const [radius, setRadius] = useState<number | null>(5);

  async function save() {
    if (!point || !radius) return;
    await fetch("/api/zones", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ lat: point.lat, lng: point.lng, radiusKm: radius }),
    });
  }

  return (
    <div className="flex flex-col gap-4">
      <LocationMap
        value={point}
        onValueChange={setPoint}
        radius={radius}
        onRadiusChange={setRadius}
        radiusRange={[1, 25]}
        radiusStep={1}
        defaultZoom={11}
        showTownsInRadius={false}
        locale="en-US"
      />
      <Button tone="brand" onClick={save} disabled={!point || !radius}>
        Zone speichern
      </Button>
    </div>
  );
}
```

`onRadiusChange` feuert bei jedem Schritt des Sliders, und die Ansicht passt sich dem Kreis an, sobald du loslässt. Zeichnest du die Zone selbst mit reinem Leaflet, denk daran, dass `L.Circle` Meter erwartet, keine Kilometer. `showTownsInRadius` ergänzt eine Anzeige wie „14 towns in range“ aus der rumänischen Ortsliste, außerhalb Rumäniens schaltest du es also ab. Der [Slider](https://wingo-ui.com/components/slider) unter der Karte ist derselbe, den du in Formularen nutzt.

## Wie suche ich nach einer Adresse?

Übergib einen `geocoder`: eine Funktion, die die Suchanfrage und `{ signal, locale }` bekommt und `GeocodeResult[]` zurückgibt (`id`, `label`, optional `description`, `point`, optional `bounds`). Die Karte entprellt das Tippen, bricht die vorherige Anfrage über das Signal ab und passt die Ansicht an `bounds` an, wenn ein Ergebnis sie mitliefert. Die Ergebnisliste ist der [Search Input](https://wingo-ui.com/components/search-input) mit Pfeiltasten und Enter.

`nominatimGeocoder` reicht für einen Prototyp. Die [Nominatim Usage Policy](https://operations.osmfoundation.org/policies/nominatim/) von OpenStreetMap erlaubt höchstens eine Anfrage pro Sekunde und schließt Autovervollständigung im Client aus. Der Helfer drosselt die Anfragen per Warteschlange auf eine pro Sekunde, aber ein Suchfeld, das beim Tippen abfragt, ist trotzdem genau der Fall, vor dem die Policy warnt. Für die Produktion lässt du den Geocoder auf deine eigene Route zeigen. Die kann jeden Anbieter aufrufen, Antworten cachen und den Key auf dem Server halten:

```ts
import type { GeocodeResult, Geocoder } from "@/components/ui/location-map";

export const placesGeocoder: Geocoder = async (query, { signal, locale }) => {
  const params = new URLSearchParams({ q: query, lang: locale });
  const response = await fetch(`/api/places?${params}`, { signal });
  if (!response.ok) throw new Error(`places ${response.status}`);
  return (await response.json()) as GeocodeResult[];
};
```

Wirft der Geocoder einen Fehler, zeigt die Liste „No places found“ statt einer Fehlermeldung. Wenn du zusätzlich eine Adresse als Text erfasst, ergänzen sich Pin und Formularfelder: Der [Address Input](https://wingo-ui.com/components/address-input) enthält Straße und Hausnummer, die Karte den genauen Punkt. Der [Guide zum rumänischen Adressformat](https://wingo-ui.com/de/blog/romanian-address-format-react-form) zeigt dieses Formular Teil für Teil, und der [Guide zur React Combobox](https://wingo-ui.com/de/blog/react-combobox) erklärt die Tastaturregeln, denen die Ergebnisliste folgt.

## Wie verhält sich der Picker am Smartphone?

Die eingebettete Karte nimmt der Seite das Scrollen nicht weg. Mit `interactive="auto"` (Standard) ist die Karte mit der Maus direkt an Ort und Stelle bedienbar. Auf Touch-Geräten öffnet ein Tippen ein Sheet in voller Höhe, mit der Suche oben, einer Karte, die du per Pinch zoomen, ziehen und antippen kannst, und unten dem Button „Done“. Schließt du das Sheet auf andere Weise, wird die Änderung verworfen. So verschiebt ein versehentliches Wischen den Pin nie. Eingebettet zeigt ein Smartphone höchstens 220px Karte, denn die volle Karte ist nur ein Tippen entfernt.

`interactive="always"` macht die Karte überall direkt bedienbar, und `interactive="expand"` öffnet immer das Sheet (am Desktop einen Dialog), was zu einer kompakten Formularzeile passt. Das Sheet ist der [Drawer](https://wingo-ui.com/components/drawer); der [Guide zum React Bottom Sheet](https://wingo-ui.com/de/blog/react-bottom-sheet-for-the-web) erklärt, warum Sheets unter 768px besser sind als Popover. Am Desktop zoomt das Mausrad mit `scrollWheelZoom="focus"` erst, nachdem du in die Karte geklickt hast, und hört auf, sobald der Zeiger sie verlässt. So bleibt das Scrollen an der Karte vorbei nie an ihr hängen.

## Ist der Location Picker barrierefrei?

Ja, jede Bewegung geht auch per Tastatur. Tab erreicht die Suche, die Karte (Pfeiltasten verschieben, Plus und Minus zoomen), den Pin (Pfeiltasten bewegen ihn um 10px, Shift plus Pfeiltaste um 50px, übernommen wird kurz nach der letzten Taste), die Karten-Buttons und den Slider. Für Screenreader beschreiben der Ortsname und die Koordinaten darunter die Karte, und eine Live Region sagt jede Bewegung als „Pin moved: ...“ an. Jeder Text, Ansagen eingeschlossen, steckt in `labels` und überschreibt die Werte aus `DEFAULT_LOCATION_MAP_LABELS`, so übersetzt du alles, ohne die Datei zu forken. Reduced Motion schaltet die Fall-Animation des Pins und die animierten Flüge der Karte ab.

## Was passiert, wenn die Karte nicht lädt?

Eine gezeichnete statische Ansicht springt ein: eine Skizze des Viertels mit Pin, Radius-Ring und einem Chip mit Ort und Koordinaten. Du siehst sie, bevor die Karte lädt (mit `lazy` lädt Leaflet erst, wenn die Karte bis auf 200px an den Viewport herankommt), wenn der Browser offline ist, wenn mehr als sechs Kacheln nacheinander fehlschlagen und wenn der Karten-Chunk nicht lädt. Leaflet wird in einem Effect importiert, nie auf Modulebene, deshalb rendert die Komponente auf dem Server ohne `next/dynamic`.

`staticOnly` lässt die Karte ganz weg. Für eine E-Mail, eine Druckansicht, ein Thumbnail oder eine Liste mit fünfzig Filialen nimmst du `StaticMap` direkt: Sie braucht keine Karten-Library und kein Netzwerk.

> Live-Demo (Location Map): Derselbe Picker mit staticOnly: die gezeichnete Ansicht mit Pin, Radius-Ring, Ort und Koordinaten, ohne dass eine Kachel angefragt wird. Probier sie unter [Location Map](https://wingo-ui.com/components/location-map) aus und installier sie mit `npx wingo-ui@latest add location-map`.

```tsx
import { StaticMap } from "@/components/ui/location-map";

export function OrderMap() {
  return (
    <StaticMap
      value={{ lat: 40.7128, lng: -74.006 }}
      place="350 Broadway, New York"
      height={180}
      tone="brand"
      locale="en-US"
    />
  );
}
```

## Welche typischen Fehler gibt es bei einem Location Picker?

Vier Fehler tauchen auf, sobald ein Picker die Demo verlässt.

- **Mit den öffentlichen OSM-Kacheln live gehen.** Die [Tile Usage Policy](https://operations.osmfoundation.org/policies/tiles/) hat kein SLA und kann starke Nutzung sperren. Setz `tileUrl` auf die Raster-Kacheln deines Anbieters und `attribution` auf den Hinweis, den er verlangt.
- **Rumänische Standardwerte beibehalten.** Ohne Props zentriert die Karte auf Cluj-Napoca, sucht offline nach rumänischen Orten, zählt rumänische Orte im Radius, und `nominatimGeocoder` beschränkt die Ergebnisse auf `"ro"`. Eine App für die USA setzt `center`, `geocoder` mit `countryCodes`, `reverseGeocoder` und `showTownsInRadius={false}`, wie in den Beispielen oben.
- **Bei jedem Slider-Schritt speichern.** `onRadiusChange` feuert pro Schritt; speichere beim Absenden oder entprell den Aufruf.
- **Dark Mode ohne Plan.** `darkTiles="filter"` invertiert die hellen Kacheln im Dark Mode ohne zweiten Anbieter; `"url"` wechselt auf `darkTileUrl`, und `"none"` behält in beiden Themes die hellen Kacheln.

## Wie baue ich die Komponente in mein Projekt ein?

Führ `npx wingo-ui@latest add location-map` aus. Die Location Map ist eine Pro-Komponente ([Preise](https://wingo-ui.com/de/pricing)); Search Input und Drawer, auf denen sie aufbaut, sind kostenlos. Weitere Deep Dives zu einzelnen Komponenten findest du in der [Kategorie Komponenten-Guides](https://wingo-ui.com/de/blog/category/components), und der [Guide zu React-Formularkomponenten](https://wingo-ui.com/de/blog/react-form-components-guide) wendet dieselben Regeln für Touch, Tastatur und Screenreader auf jedes Formularelement an.

## Komponenten in diesem Artikel

- [Location Map](https://wingo-ui.com/components/location-map) (Pro): A React location map to pick or show a place: a draggable pin, an optional radius with a towns count, offline search and a static fallback. Installation: `npx wingo-ui@latest add location-map`

## Häufige Fragen

### Wie bekomme ich in React Breiten- und Längengrad, wenn jemand auf die Karte klickt?

In react-leaflet liest du den Klick mit dem Hook useMapEvents und speicherst e.latlng im State. Die Location Map von Wingo UI nimmt dir das ab: Ein Klick setzt den Pin und onValueChange bekommt { lat, lng }.

### Warum wirft Leaflet in Next.js „window is not defined“?

Leaflet liest window schon beim Import des Moduls, deshalb stürzt ein Import in Code ab, der auf dem Server läuft. Lade es nur im Browser, entweder mit next/dynamic und ssr: false aus einer Client-Komponente oder mit einem dynamischen Import in einem Effect, so wie die Location Map es macht.

### In welcher Einheit gibt man den Radius eines Leaflet-Kreises an?

L.Circle von Leaflet erwartet den Radius in Metern. Der radius-Prop der Location Map ist in Kilometern und rechnet für dich um.

### Kann ich OpenStreetMap-Kacheln und Nominatim in Produktion nutzen?

Nur bei geringer Last. Die Tile Policy von OSM gibt kein SLA und kann starken Traffic sperren, und die Nominatim-Policy erlaubt höchstens eine Anfrage pro Sekunde und keine Autovervollständigung im Client. Apps in Produktion sollten tileUrl und den Geocoder deshalb auf einen eigenen Anbieter zeigen lassen.

### Ist die Location Map von Wingo UI kostenlos?

Nein, die Location Map ist eine Pro-Komponente. Search Input und Drawer, auf denen sie aufbaut, sind kostenlos.

---

Quelle: https://wingo-ui.com/de/blog/react-location-picker
