Leaflet Location Picker (Click to Set Coordinates) — Free Snippet

Leaflet Location Picker (Click to Set Coordinates) · Forms · Plain HTML, CSS & JS · Live preview

What's included

Features

Four synced input paths
Click, drag, type, or search — one function updates all state.
Validated manual entry
Typed coordinates are range-checked before moving anything.
Local place-search demo
Same UX pattern a real geocoding API integration would use.
Outside-click dismissal
Suggestions close like a real autocomplete, not just on select.
Bidirectional pin and form
Moving either one always updates the other.
Free OpenStreetMap tiles
No API key or paid map provider required.

About this UI Snippet

Leaflet Location Picker — Four Inputs, One Coordinate, No Way to Disagree

Screenshot of the Leaflet Location Picker (Click to Set Coordinates) snippet rendered live

A location picker that only supports clicking the map is missing half its users — some already know the exact coordinates, some know an address, and some just want to drag a pin to fine-tune a rough guess. This snippet supports all four paths (map click, pin drag, typed coordinates, and place search) by routing every one of them through a single setLocation(lat, lng, pan) function, so no path can produce a state the others don't recognize.

One function, four callers

Clicking the map, finishing a drag, selecting a search suggestion, and typing valid coordinates all eventually call setLocation — it's the only place that updates the latitude/longitude inputs, moves or creates the marker, and enables the confirm button. That's what guarantees the form fields and the map pin can never show two different locations at once, regardless of which interaction the user chose.

Manual coordinate entry is validated before it moves anything

Typing into the latitude or longitude field doesn't move the pin on every keystroke — it validates on change (when the field loses focus or Enter is pressed), checking both that the values parse as numbers and that they fall within real coordinate ranges (-90 to 90, -180 to 180) before calling setLocation. An invalid or incomplete entry simply doesn't move anything, rather than crashing or panning to NaN, NaN.

The place search is a local filter, not a live geocoding call

To keep the demo self-contained and free of network dependencies inside the sandboxed preview, search matches against a small fixed list of named places rather than a real geocoding API — the interaction pattern (type, see suggestions, click one, pin moves and map flies there) is exactly what a real fetch-based geocoder integration would look like, with the local array standing in for the API response.

Suggestions close on outside click, not just on selection

A document-level click listener closes the suggestions dropdown whenever a click lands outside the search field's container — the kind of small interaction detail (matching what every real autocomplete does) that's easy to skip and immediately feels unfinished without it.

Reusing it

Swap the local PLACES array for a real geocoding API call (Nominatim, Mapbox, Google) inside the same input handler, keep the rest of the four-way sync exactly as is, and this becomes a production-ready address picker for checkout flows, pickup/dropoff selection, or any form that needs a precise geographic point.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to design the state-synchronization architecture yourself. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why routing every input path (click, drag, type, search) through one shared setLocation function is what guarantees the map and form can never disagree, and how the manual coordinate validation prevents an invalid typed value from ever moving the pin. The same assistant can help optimize it — ask whether the local PLACES array search should be debounced if it were replaced with a real network-based geocoding call, to avoid firing a request on every keystroke. It's also useful for extending the effect: ask it to wire the search to a real geocoding API like Nominatim, add reverse geocoding so clicking the map also fills in a readable address (not just coordinates), or add a "use my current location" button using the Geolocation API. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a location picker form with a synced interactive map using Leaflet.js (load Leaflet's CSS and JS from a CDN, no other library), in plain HTML, CSS, and JavaScript.

Requirements:
- Show a form with latitude and longitude text inputs, an address/place search input, and a Confirm button (disabled until a location has been set), next to an interactive map.
- Support setting the location four different ways, all of which must produce exactly the same resulting state (updated latitude/longitude fields, a marker on the map at that position, and the confirm button enabled): clicking anywhere on the map, dragging an existing marker to a new position, typing valid latitude and longitude values directly into the text fields, and selecting a place from an address search.
- Implement the address search against a small local array of named places with coordinates (standing in for a real geocoding API), showing a dropdown of matching suggestions as the user types that filters by partial, case-insensitive text match, and dismissing the dropdown when the user clicks outside the search field.
- Validate manually typed coordinates before applying them — both values must parse as real numbers and fall within valid geographic ranges (latitude -90 to 90, longitude -180 to 180) — and silently do nothing if the typed values are invalid, rather than moving the pin to an incorrect position.
- Make dragging the marker update the latitude/longitude text fields continuously throughout the drag gesture (not only when the drag ends), so the form never shows stale values while the marker is being moved.
- Route every one of these four interaction paths through a single shared function that is the only code responsible for updating the marker, the form fields, and the confirm button's enabled state.
- Use free OpenStreetMap tile layers so the demo requires no API key.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="lp-wrap">
  <div class="lp-form">
    <h1>Set Pickup Location</h1>
    <label class="lp-field">Latitude
      <input type="text" id="lpLat" placeholder="Click the map">
    </label>
    <label class="lp-field">Longitude
      <input type="text" id="lpLng" placeholder="Click the map">
    </label>
    <label class="lp-field">Address (search)
      <input type="text" id="lpSearch" placeholder="Type an address or place...">
      <div class="lp-suggestions" id="lpSuggestions"></div>
    </label>
    <p class="lp-hint" id="lpHint">Click anywhere on the map, drag the pin, or search an address above.</p>
    <button class="lp-confirm" id="lpConfirm" type="button" disabled>Confirm Location</button>
  </div>
  <div class="lp-map" id="lpMap"></div>
</div>

Step by step

How to Use

  1. 1
    Add the Leaflet CDNLoad leaflet.css and leaflet.js before the snippet's JS runs.
  2. 2
    Paste HTML, CSS, and JSA form and map render side by side, both empty.
  3. 3
    Click anywhere on the mapA pin drops and the coordinate fields fill in.
  4. 4
    Drag the pinThe coordinate fields update live as you move it.
  5. 5
    Type coordinates directlyTab or click away to move the pin to that exact point.
  6. 6
    Search a place namePick a suggestion to fly the map there and drop the pin.

Real-world uses

Common Use Cases

Checkout and delivery address forms
Let a customer fine-tune their exact drop-off point.
Ride-share pickup location pickers
Map-and-form entry that always agree.
Property listing coordinate entry
Admin tools for placing a listing precisely.
Event or venue location setup
Organizer-facing forms needing exact coordinates.
Geofence and service-area configuration
Pair with the radius selector elsewhere in this collection.
Learning Leaflet forms
A clear reference for two-way map-and-input synchronization.

Got questions?

Frequently Asked Questions

Every interaction path — clicking the map, dragging the pin, selecting a search result, or typing valid coordinates — ultimately calls the same setLocation function, which is the only code that updates the latitude/longitude input values, moves or creates the marker, and enables the confirm button. Because there is exactly one function responsible for that state, no interaction path can leave the form and the map pin showing different locations.

The manual-entry handler runs on the field's change event and checks that both values parse as real numbers and fall within valid coordinate ranges (latitude between -90 and 90, longitude between -180 and 180) before calling setLocation. If the check fails — an empty field, non-numeric text, or an out-of-range value — nothing happens: the pin stays where it was rather than jumping to an invalid or NaN position.

No — to keep this snippet self-contained and free of network calls inside a sandboxed preview, it searches against a small fixed local array of named places. The interaction it demonstrates (type, see filtered suggestions, click one, the map flies there and the pin drops) is exactly the pattern you would wire up to a real geocoding API's search endpoint, with the local array simply standing in for that API's response.

The marker's drag event (which fires continuously throughout the gesture) reads the marker's current position on every frame of the drag and writes it straight into the latitude and longitude fields. Using dragend instead would leave the form fields stale and out of sync with the pin's actual position for the entire duration of the drag, which reads as a bug even if it self-corrects at the end.

Replace the local PLACES.filter() logic inside the search input's handler with a fetch call to a real geocoding API (Nominatim, Mapbox Geocoding, Google Places), map its response results into the same { label, lat, lng } shape the suggestions list already expects, and everything downstream — rendering suggestions, selecting one, flying the map, dropping the pin — needs no other changes.