npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@geoapify/route-waypoint-selector

v1.0.0

Published

TypeScript waypoint selector UI component for route planning using Geoapify Autocomplete.

Readme

Route Waypoint Selector UI Library

@geoapify/route-waypoint-selector is a TypeScript UI library for building route waypoint pickers in web applications. Use it when you need to add, edit, reorder, and remove route stops in delivery planning, trip builders, ride booking, logistics dashboards, and map-based route editors. It uses Geoapify Address Autocomplete to help users search for and select addresses for each waypoint.

Each waypoint represents one stop in a route. The library gives you a simple UI for building, updating, and rearranging those stops.

  • Create waypoints for routing: start with one or more stops and build a route step by step.
  • Search addresses with autocomplete: type an address and choose from Geoapify suggestions.
  • Add and remove waypoints: insert new stops when needed and remove stops you no longer want.
  • Reorder waypoints with drag and drop: change the stop order by dragging items up or down.
  • Use callbacks for changes: listen for updates when the route changes and keep your app state in sync.

Install

Install via npm for framework or bundler-based apps. Use the CDN example if you want to try the library directly in a browser without a build step.

To get a Geoapify API key with a free tier, sign up at https://www.geoapify.com/.

npm

Installing @geoapify/route-waypoint-selector also installs @geoapify/geocoder-autocomplete as a dependency:

npm i @geoapify/route-waypoint-selector

In your app, import the CSS for both packages so the waypoint selector and geocoder are styled correctly:

import "@geoapify/geocoder-autocomplete/styles/minimal.css";
import "@geoapify/route-waypoint-selector/styles/core.css";
import "@geoapify/route-waypoint-selector/styles/theme-minimal.css";

CDN

Use versioned URLs with <link> tags for CSS and an import map for browser-safe ESM loading:

<link rel="stylesheet" href="https://unpkg.com/@geoapify/[email protected]/styles/minimal.css" />
<link rel="stylesheet" href="https://unpkg.com/@geoapify/route-waypoint-selector@1/styles/core.css" />
<link rel="stylesheet" href="https://unpkg.com/@geoapify/route-waypoint-selector@1/styles/theme-minimal.css" />

<script type="importmap">
  {
    "imports": {
      "@geoapify/geocoder-autocomplete": "https://unpkg.com/@geoapify/[email protected]/dist/index.js"
    }
  }
</script>

<script type="module">
  import { WaypointSelector } from "https://unpkg.com/@geoapify/route-waypoint-selector@1/dist/index.js";
</script>

This example has three parts:

  • The <link> tags load the CSS needed for the geocoder and the waypoint selector.
  • The import map tells the browser how to resolve @geoapify/geocoder-autocomplete, which the library uses internally.
  • The type="module" script loads WaypointSelector from unpkg and creates the selector instance in your page.

Basic usage

Use this setup when your app already has a bundler or build step. The JavaScript import gives you the selector class, and the CSS imports add the default widget and geocoder styles.

import { WaypointSelector } from "@geoapify/route-waypoint-selector";
import "@geoapify/geocoder-autocomplete/styles/minimal.css";
import "@geoapify/route-waypoint-selector/styles/core.css";
import "@geoapify/route-waypoint-selector/styles/theme-minimal.css";

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
    onChange: (waypoints, context) => {
      console.log("Waypoints changed", { waypoints, context });
    }
});

Notes:

  • WaypointSelector renders the route waypoint UI into the container you pass in.
  • #waypoint-selector should point to an element that already exists in your page.
  • YOUR_GEOAPIFY_API_KEY should be replaced with your actual Geoapify API key.
  • onChange lets your app react whenever the waypoint list changes.

Styling

The library styles are split into two parts:

  • styles/core.css contains shared layout and component base styles.
  • A theme file contains the visual skin (colors, borders, spacing details).

For now, there are 4 themes, and they correspond to the @geoapify/geocoder-autocomplete themes with the same names. Use matching theme pairs for consistent UI.

| Route Waypoint Selector | Geocoder Autocomplete | | --- | --- | | styles/theme-minimal.css | styles/minimal.css | | styles/theme-minimal-dark.css | styles/minimal-dark.css | | styles/theme-round-borders.css | styles/round-borders.css | | styles/theme-round-borders-dark.css | styles/round-borders-dark.css |

API Reference

The API is small and focused: create the selector, read or update the waypoint list, and listen for changes.

| Method | Description | | --- | --- | | new WaypointSelector(container: HTMLElement \| string, apiKey: string, options?: WaypointSelectorOptions) | Creates and mounts the waypoint selector. See WaypointSelectorOptions. | | getWaypoints(): Waypoint[] | Returns the current list of waypoints. See Waypoint. | | setWaypoints(waypoints: Waypoint[]): void | Allows you to set the waypoint list from outside the component. See Waypoint. | | addWaypoint(waypoint: Waypoint, index?: number): void | Adds a new waypoint at the end or at a chosen position. See Waypoint. | | removeWaypoint(waypointIndexOrId: number \| string): void | Removes a waypoint by index or id. See Waypoint. | | setWaypoint(waypointIndexOrId: number \| string, label: string, lat?: number, lon?: number): void | Updates one waypoint by index or id. See Waypoint. | | isWaypointResolved(waypoint: Waypoint): boolean | Checks whether a waypoint has label, latitude, and longitude. See Waypoint. | | destroy(): void | Removes the UI and cleans up events. |

Methods

new WaypointSelector(container: HTMLElement \| string, apiKey: string, options?: WaypointSelectorOptions)

Creates and mounts the selector in the chosen container.

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
  onChange: (waypoints, context) => {
    console.log("Waypoints changed", waypoints, context);
  }
});

getWaypoints(): Waypoint[]

Returns the current waypoint list.

const waypoints = selector.getWaypoints();
console.log(waypoints);

setWaypoints(waypoints: Waypoint[]): void

Replaces the current waypoint list with a new one.

selector.setWaypoints([
  {
    id: "wp-1",
    label: "Times Square, New York, NY, USA",
    lat: 40.758,
    lon: -73.9855
  },
  {
    id: "wp-2",
    label: "Central Park, New York, NY, USA",
    lat: 40.785091,
    lon: -73.968285
  }
]);

addWaypoint(waypoint: Waypoint, index?: number): void

Adds a new waypoint at the end or at a specific position.

selector.addWaypoint({
  id: "wp-3",
  label: "Brooklyn Bridge, New York, NY, USA",
  lat: 40.706086,
  lon: -73.996864
});

removeWaypoint(waypointIndexOrId: number | string): void

Removes a waypoint by index or by id.

selector.removeWaypoint("wp-3");
selector.removeWaypoint(0);

setWaypoint(waypointIndexOrId: number | string, label: string, lat?: number, lon?: number): void

Updates a single waypoint’s label and coordinates.

selector.setWaypoint("wp-1", "Times Square, Manhattan, NY, USA", 40.758, -73.9855);

reorderWaypoints(fromIndex: number, toIndex: number): void

Moves a waypoint to a different position in the list.

selector.reorderWaypoints(0, 1);

isWaypointResolved(waypoint: Waypoint): boolean

Checks whether a waypoint already has label, latitude, and longitude.

const resolved = selector.getWaypoints().map((waypoint) => ({
  waypoint,
  resolved: selector.isWaypointResolved(waypoint)
}));
console.log(resolved);

destroy(): void

Removes the selector from the page and cleans up event listeners.

selector.destroy();

Waypoint

Waypoints are the data objects returned by the library and passed into callbacks. Each waypoint can contain:

| Field | Description | | --- | --- | | id | A custom waypoint identifier used by your app to connect related data. The library does not require it, but it helps you keep things like map markers in sync when waypoints are reordered. | | label | The selected address or place name. | | lat | Latitude. | | lon | Longitude. |

Use the returned waypoint data to store route stops in your app state, send them to your backend, or build routing logic.

Localization / customization

You can customize the visible text in the UI and the behavior of address suggestions.

  • labels lets you change button text, headings, and accessibility labels.
  • geocoderOptions is forwarded to @geoapify/geocoder-autocomplete so you can customize the suggestion widget.

Example:

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
  labels: {
    route_waypoint_selector: "Planificador de rutas",
    choose_destination: "Elige una parada",
    add_destination: "Añadir parada",
    remove_destination: "Eliminar parada",
    switch_waypoints: "Intercambiar paradas",
    drag_to_reorder: "Arrastra para reordenar"
  },
  geocoderOptions: {
    lang: "es"
  }
});

Callbacks

Callbacks are provided through WaypointSelectorOptions. Use them to react to changes in the waypoint list and keep your app state in sync.

| Callback | When it runs | Parameters | | --- | --- | --- | | onWaypointChange | When a waypoint is set or cleared. | (waypoint, context) | | onAdd | When a waypoint is added. | (waypoint, index) | | onRemove | When a waypoint is removed. | (waypoint, index) | | onReorder | When the waypoint order changes. | (waypoints, context) | | onChange | On any list change, including init, add, remove, reorder, set, or waypoint updates. | (waypoints, context) | | onError | When the component reports an internal error. | (error) |

Example:

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
  onWaypointChange: (waypoint, context) => {
    console.log("Waypoint changed", waypoint, context);
  },
  onAdd: (waypoint, index) => {
    console.log("Waypoint added", waypoint, index);
  },
  onRemove: (waypoint, index) => {
    console.log("Waypoint removed", waypoint, index);
  },
  onReorder: (waypoints, context) => {
    console.log("Waypoints reordered", waypoints, context);
  },
  onChange: (waypoints, context) => {
    console.log("Waypoints changed", waypoints, context);
  },
  onError: (error) => {
    console.error("Waypoint selector error", error);
  }
});

WaypointSelectorOptions

WaypointSelectorOptions is the overview of the main configuration settings you can pass to WaypointSelector. It includes the initial route data, UI labels, geocoder settings, and event callbacks.

import { WaypointSelector } from "@geoapify/route-waypoint-selector";

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
  initialWaypoints: [
    // Optional initial list rendered on mount
    {
      label: "Times Square, New York, NY, USA",
      lat: 40.758,
      lon: -73.9855
    }
  ],
  labels: {
    // Optional custom UI texts
    route_waypoint_selector: "Route waypoint selector",
    choose_destination: "Choose destination",
    add_destination: "Add destination",
    remove_destination: "Remove destination",
    switch_waypoints: "Switch waypoints",
    drag_to_reorder: "Drag waypoint to reorder"
  },
  geocoderOptions: {
    // Forwarded to @geoapify/geocoder-autocomplete
    // Example: force English results in suggestions
    lang: "en"
  },
  onWaypointChange: (waypoint, context) => {
    // Fired when a waypoint is set or cleared
    console.log("onWaypointChange", { waypoint, context });
  },
  onAdd: (waypoint, index) => {
    // Fired when a waypoint is added
    console.log("onAdd", { waypoint, index });
  },
  onRemove: (waypoint, index) => {
    // Fired when a waypoint is removed
    console.log("onRemove", { waypoint, index });
  },
  onReorder: (waypoints, context) => {
    // Fired when waypoint order changes
    console.log("onReorder", { waypoints, context });
  },
  onChange: (waypoints, context) => {
    // Fired on any list change (init/add/remove/reorder/waypoint/set)
    console.log("onChange", { waypoints, context });
  },
  onError: (error) => {
    // Fired when the component reports an internal error
    console.error("onError", error);
  }
});

FAQ

How do I connect selected waypoints to a routing API request?

Read waypoints from the selector, keep only resolved points, and send them to a routing API when it fits your UX (for example on onChange or on a "Build route" button click).

With Geoapify Routing API, see Routing API docs.

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
  onChange: async (waypoints) => {
    // Build a route only when all waypoints are resolved (have label, lat, and lon)
    const allResolved = waypoints.length >= 2 && waypoints.every((waypoint) => selector.isWaypointResolved(waypoint));
    if (!allResolved) return;

    const waypointsParam = waypoints
      .map((point) => `${point.lon},${point.lat}`)
      .join("|");

    const url = `https://api.geoapify.com/v1/routing?waypoints=${encodeURIComponent(waypointsParam)}&mode=drive&apiKey=YOUR_GEOAPIFY_API_KEY`;
    const response = await fetch(url);
    const routeData = await response.json(); // GeoJSON FeatureCollection with route geometry and properties

    console.log("Routing response", routeData);
  }
});

Is this library free to use?

Yes. @geoapify/route-waypoint-selector itself is open-source and free to use.

Geoapify API calls (for example Address Autocomplete) may be billed when your usage is over the free tier. At the time of writing, the free tier includes up to 3,000 requests/day.

How can I get a Geoapify API key?

Geoapify is a commercial service, but it has a generous free tier that can also be used for commercial projects. Sign up at https://www.geoapify.com/, then create an API key in your dashboard and pass it as apiKey to new WaypointSelector(...). No credit card is required to get started.

Can I use a third-party routing API?

Yes. You can use any routing backend. The selector gives you waypoint data (label, lat, lon, optional id), and you can map that data to any routing provider request format.

How do I add waypoints to Leaflet?

Use onChange and maintain a Map<string, L.Marker> keyed by waypoint.id.

import L from "leaflet";

const leafletMap = L.map("map");
const markersById = new Map<string, L.Marker>();

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
  onChange: (waypoints) => {
    const nextIds = new Set<string>();

    waypoints.forEach((waypoint, index) => {
      if (!waypoint.id || !selector.isWaypointResolved(waypoint)) return;

      nextIds.add(waypoint.id);

      const latLng: L.LatLngExpression = [waypoint.lat!, waypoint.lon!];
      const markerTitle = `${index + 1}. ${waypoint.label ?? "Waypoint"}`;
      const existing = markersById.get(waypoint.id);

      if (existing) {
        existing.setLatLng(latLng).bindTooltip(markerTitle);
      } else {
        const marker = L.marker(latLng).addTo(leafletMap).bindTooltip(markerTitle);
        markersById.set(waypoint.id, marker);
      }
    });

    for (const [id, marker] of markersById) {
      if (!nextIds.has(id)) {
        marker.remove();
        markersById.delete(id);
      }
    }
  }
});

How do I add waypoints to MapLibre?

Same idea: keep Map<string, maplibregl.Marker> keyed by waypoint.id and sync it on onChange.

import maplibregl from "maplibre-gl";

const map = new maplibregl.Map({
  container: "map",
  style: `https://maps.geoapify.com/v1/styles/osm-bright/style.json?apiKey=YOUR_GEOAPIFY_API_KEY`,
  center: [0, 0],
  zoom: 2
});

const markersById = new Map<string, maplibregl.Marker>();

const selector = new WaypointSelector("#waypoint-selector", "YOUR_GEOAPIFY_API_KEY", {
  onChange: (waypoints) => {
    const nextIds = new Set<string>();

    waypoints.forEach((waypoint, index) => {
      if (!waypoint.id || !selector.isWaypointResolved(waypoint)) return;

      nextIds.add(waypoint.id);

      const lngLat: [number, number] = [waypoint.lon!, waypoint.lat!];
      const existing = markersById.get(waypoint.id);

      if (existing) {
        existing.setLngLat(lngLat);
      } else {
        const marker = new maplibregl.Marker()
          .setLngLat(lngLat)
          .setPopup(new maplibregl.Popup().setText(waypoint.label ?? `Waypoint ${index + 1}`))
          .addTo(map);

        markersById.set(waypoint.id, marker);
      }
    });

    for (const [id, marker] of markersById) {
      if (!nextIds.has(id)) {
        marker.remove();
        markersById.delete(id);
      }
    }
  }
});

License

MIT

Resources

Attribution

This library uses icons from Font Awesome 7 Free licensed under the Creative Commons Attribution 4.0 International License.