# location-picker: full documentation > Form-ready map location picker: pan a map under a fixed pin to pick one latitude/longitude. Web component + class, keyless OpenStreetMap by default, Google and MapLibre optional. Generated by scripts/build-llms-full.mjs from the README and docs/ of https://github.com/cyphercodes/location-picker. Short version: https://cyphercodes.github.io/location-picker/llms.txt --- Source: https://github.com/cyphercodes/location-picker/blob/master/README.md # location-picker **Let people pick an exact spot on a map, and get `lat,lng` in your form.** Form-ready map location picker: pan a map under a fixed pin to pick one latitude/longitude. Web component + class, keyless OpenStreetMap by default, Google and MapLibre optional.  **[Live demo](https://cyphercodes.github.io/location-picker/)** · [No-code embed](https://cyphercodes.github.io/location-picker/embed/) · [Pick link](https://cyphercodes.github.io/location-picker/pick/) · [API reference](https://github.com/cyphercodes/location-picker/blob/master/docs/api.md) · [For AI agents](#for-ai-agents) · MIT One script tag, no API key, no build step. Put the element inside your form: ```html
``` People pan the map under the pin, search for a place, paste coordinates or tap the locate button (the browser asks for permission only then). On Save, the form posts `location=48.85837,2.294481`, latitude first, like any other field. In JavaScript, read the element's `value` or listen for `change`: `e.detail` has `value`, `lat`, `lng`, `source` and `address`. - **With npm:** `npm i location-picker`, then `import 'location-picker'`. Recipes for React, Vue, Angular, Svelte and more: [Frameworks](#frameworks). - **With a coding agent:** `npx skills add cyphercodes/location-picker` adds the location-picker Agent Skill (it also ships in the package, at `node_modules/location-picker/skills/location-picker/SKILL.md`). [llms.txt](https://cyphercodes.github.io/location-picker/llms.txt) is the short version of these docs, and [Common mistakes](#common-mistakes) lists the traps. - **Without code:** the [embed generator](https://cyphercodes.github.io/location-picker/embed/) writes the snippet for WordPress, Webflow, Squarespace, Shopify or a plain HTML form ([per-platform guide](https://github.com/cyphercodes/location-picker/blob/master/docs/embed.md)). - **To ask a person:** send them the [pick link](https://cyphercodes.github.io/location-picker/pick/). They confirm a point on a map with nothing to install, and you get it back as text they copy to you, an `https://` redirect after their click, or a `postMessage` to your origin ([how it works](#ask-a-person-for-a-location-pick-link)). Made for chat bots, AI agents and support tickets. Try [`/pick/?q=Eiffel Tower`](https://cyphercodes.github.io/location-picker/pick/?q=Eiffel%20Tower). ## Why location-picker Use it when a form needs **one precise point**: a store, branch or sensor location in an admin screen, a delivery or pickup pin at checkout, an event venue, the place of a report, a home or work location in a profile. - **A real form field.** `required` validates, form reset works and back/forward navigation restores it. Submit one `"lat,lng"` entry (`name`), separate entries (`lat-name`, `lng-name`), or fill inputs you already have (`lat-input`, `lng-input`). See [Forms](#forms). - **No API key to start.** OpenStreetMap through Leaflet by default; Google Maps or MapLibre when you choose them. The default OpenStreetMap tiles and Photon address search are free public services with no SLA: for production traffic, use your own or a commercial tile server and geocoder ([usage policies](#providers-and-usage-policies)). - **Pan, search, paste or locate.** Arrow keys and Enter work too, and the locate button asks for permission only when clicked. Typed or pasted coordinates jump straight there with no network call: `48.8584, 2.2945`, DMS, `geo:` URIs and Google, OpenStreetMap or Apple Maps links. - **Phone-first when you want it.** [Field mode](#phone-first-field-mode) shows a compact control that opens the map full screen; the value changes only on Confirm. - **Your stack.** Plain HTML forms and server templates (Laravel, Django, Rails), React, Vue, Angular, Svelte, Solid and Preact; jQuery, Livewire, Alpine and htmx through bound inputs. TypeScript types and JSX typings are included. - **No runtime npm dependencies.** Leaflet, MapLibre and the Google Maps types are optional peers. When the page has no Leaflet, it is loaded from jsDelivr ([Install](#install)). - **Clear errors, generated test results.** Every runtime problem has a stable `code` and a one-line fix ([error codes](https://github.com/cyphercodes/location-picker/blob/master/docs/errors.md)). Browser and device results come from the test matrix: [COMPATIBILITY.md](https://github.com/cyphercodes/location-picker/blob/master/COMPATIBILITY.md). **When not to use it:** drawing shapes, routes or areas; several markers; a full mapping application (use Leaflet, MapLibre or the Google Maps JS API directly); text-only address autocomplete with no map; offline maps; React Native or other native mobile apps. ## Install | How | Use | | ----------------------- | ---------------------------------------------------------------------------------------- | | Script tag (no bundler) | `` | | npm | `npm i location-picker`, then `import 'location-picker'` | | npm, class only | `import { LocationPicker } from 'location-picker/core'` (defines no element) | | Upgrading 1.x code | `import locationPicker from 'location-picker/v1'` ([migration guide](https://github.com/cyphercodes/location-picker/blob/master/docs/migration.md)) | The script tag defines `Picked:
``` ## How the value works - The picker starts **unset** (`value === ""`) unless you give it a `value`. While unset, the map shows `center` (or a world view) and the pin is dimmed. - The user **commits** a point by panning the map, using the arrow keys, pressing Enter on the map, clicking the confirm button (shown while unset when `center` is set), picking a search result, or locating. `setLocation()` commits too. - Zooming never changes the value: zoom is anchored at the pin. - Every commit fires `input` (final value), then `change`, with `detail.source`: `user`, `api`, `geolocation` or `search`. Live `input` events fire while the user drags. - Writing `el.value = '48.8584,2.2945'` is silent (no events), like a native ``. - The value string is always latitude first, dot decimal, no spaces, rounded to `precision` decimals (6 by default): `"48.85837,2.294481"`. ## Forms The element is form-associated: it validates with `required`, resets with the form and restores on back/forward navigation. Choose what it submits: | Attributes | Submitted entries | | ------------------------------------------------ | ------------------------------------------------------------- | | `name="loc"` | `loc=48.85837,2.294481` | | `lat-name="lat" lng-name="lng"` | `lat=48.85837`, `lng=2.294481` | | `+ address-name="addr"` | `addr=` (`""` while unknown) | | `lat-input="lat" lng-input="lng"` (bound inputs) | your existing inputs submit themselves; the picker fills them | The recommended server-rendered pattern puts the inputs inside the element. Without JavaScript they are visible and typed by hand; with it they are hidden, filled by the map, and still submitted. Their server values seed the picker, and a form reset brings them back. ```blade ``` ```php // StoreController@update $data = $request->validate([ 'lat' => ['required', 'numeric', 'between:-90,90'], 'lng' => ['required', 'numeric', 'between:-180,180'], 'address' => ['nullable', 'string', 'max:255'], ]); $store->update($data); ``` The same in a Django template: ```django {% load l10n %}{value || 'Pan the map to choose'}
> ); } //