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

@dacostafilipe/react-geoportail

v0.1.3

Published

React SDK for the Geoportail Luxembourg v3 API

Downloads

343

Readme

react-geoportail

Disclaimer: This is an unofficial, community-built React package. It is not affiliated with, endorsed by, or supported by the Geoportail Luxembourg team or the Administration du Cadastre et de la Topographie (ACT). For the official API, see apiv3.geoportail.lu.

An unofficial React SDK for the Geoportail Luxembourg v3 API. Provides a map component and hooks for geocoding, all typed with TypeScript.

Features

  • <GeoportailMap> — render a Geoportail map with optional pin/marker support
  • useReverseGeocode — look up a Luxembourg address from lat/lon coordinates
  • useGeocode — search for coordinates from an address string
  • Coordinate conversion utilities (EPSG:2169 ↔ WGS84) included
  • Zero runtime npm dependencies — uses the official apiv3loader.js script

Requirements

  • React 17 or later
  • A modern browser (the Geoportail API requires it)
  • For production deployments: register your domain with ACT at [email protected]. The API works without restriction on localhost.

Installation

npm install @dacostafilipe/react-geoportail

The Geoportail API script (apiv3.geoportail.lu/apiv3loader.js) is injected automatically — no manual <script> tag needed.

Quick start

import { GeoportailMap } from '@dacostafilipe/react-geoportail';

export default function App() {
  return (
    <GeoportailMap
      center={{ lat: 49.6116, lon: 6.1319 }}
      zoom={13}
      style={{ height: 500 }}
    />
  );
}

Components

<GeoportailMap>

Renders a Geoportail map inside a div. The map fills its container — set a height via style or className.

<GeoportailMap
  center={{ lat: 49.6116, lon: 6.1319 }}
  zoom={13}
  bgLayer="basemap_2015_global"
  markerMode="click"
  onMarkerPlace={(coords) => console.log(coords.lat, coords.lon)}
  style={{ height: 480 }}
/>

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | center | LatLon | Luxembourg City | Initial map center | | zoom | number | 12 | Initial zoom level (1–20) | | bgLayer | string | 'basemap_2015_global' | Background layer identifier | | markerMode | 'none' \| 'fixed' \| 'click' | 'none' | Controls pin behaviour (see below) | | markerPosition | LatLon | — | Pin position for 'fixed' mode, or initial pin for 'click' mode | | onMarkerPlace | (coords: LatLon) => void | — | Called when the user places a pin (markerMode='click') | | layers | number[] | — | Additional Geoportail layer IDs to overlay | | className | string | — | CSS class for the container div | | style | React.CSSProperties | — | Inline styles for the container div |

Marker modes

| Mode | Behaviour | |------|-----------| | 'none' | No marker shown | | 'fixed' | A static pin is shown at markerPosition | | 'click' | User clicks the map to place/move the pin; fires onMarkerPlace with the clicked WGS84 coordinates |

Imperative ref

Pass a ref typed as GeoportailMapHandle to control the map programmatically:

import { useRef } from 'react';
import { GeoportailMap, GeoportailMapHandle } from '@dacostafilipe/react-geoportail';

const mapRef = useRef<GeoportailMapHandle>(null);

<GeoportailMap ref={mapRef} ... />

// Move the map
mapRef.current?.setCenter({ lat: 49.5, lon: 6.1 });
mapRef.current?.setZoom(15);

// Access the raw lux.Map instance
const luxMap = mapRef.current?.getLuxMap();

Hooks

useReverseGeocode

Convert a WGS84 lat/lon position to a Luxembourg address. Uses the Geoportail REST reverse geocode endpoint — no API key required.

import { useReverseGeocode } from '@dacostafilipe/react-geoportail';

function LocationInfo({ lat, lon }: { lat: number; lon: number }) {
  const { state, lookup } = useReverseGeocode();

  useEffect(() => {
    lookup({ lat, lon });
  }, [lat, lon]);

  if (state.status === 'loading') return <p>Looking up address…</p>;
  if (state.status === 'error')   return <p>Error: {state.error.message}</p>;
  if (state.status === 'success') return <p>{state.address.label}</p>;
  return null;
}

Return value

const { state, lookup, reset } = useReverseGeocode();

| | Type | Description | |-|------|-------------| | state.status | 'idle' \| 'loading' \| 'success' \| 'error' | Current state | | state.address | Address \| null | Result when status === 'success' | | state.error | Error \| null | Error when status === 'error' | | lookup(pos) | (pos: LatLon) => void | Trigger a lookup; cancels any in-flight request | | reset() | () => void | Return to 'idle' and cancel any in-flight request |

Address shape

interface Address {
  label: string;    // Full formatted address
  distance: number; // Distance from queried point (metres)
  easting: number;  // EPSG:2169 easting
  northing: number; // EPSG:2169 northing
}

useGeocode

Search for a Luxembourg address and get back coordinates. Supports a free-text query string or structured fields.

import { useGeocode } from '@dacostafilipe/react-geoportail';

function AddressSearch() {
  const { state, search } = useGeocode();

  return (
    <>
      <button onClick={() => search({ queryString: 'Place d\'Armes, Luxembourg' })}>
        Search
      </button>

      {state.status === 'success' && state.results.map((r, i) => (
        <p key={i}>
          {r.street} {r.num}, {r.zip} {r.locality}
          — {r.latLon.lat.toFixed(5)}, {r.latLon.lon.toFixed(5)}
        </p>
      ))}
    </>
  );
}

Return value

const { state, search, reset } = useGeocode();

| | Type | Description | |-|------|-------------| | state.status | 'idle' \| 'loading' \| 'success' \| 'error' | Current state | | state.results | GeocodeResultItem[] \| null | Results when status === 'success' | | state.error | Error \| null | Error when status === 'error' | | search(query) | (query: GeocodeQuery) => void | Trigger a search; cancels any in-flight request | | reset() | () => void | Return to 'idle' |

GeocodeQuery options

// Free-text (recommended for most cases)
search({ queryString: '1 rue du Fort Thüngen, Luxembourg' });

// Structured fields
search({ num: '1', street: 'rue du Fort Thüngen', zip: '1499', locality: 'Luxembourg' });

GeocodeResultItem shape

interface GeocodeResultItem {
  latLon: LatLon;   // WGS84 { lat, lon }
  easting: number;  // EPSG:2169
  northing: number; // EPSG:2169
  accuracy: number;
  street?: string;
  num?: string;
  zip?: string;
  locality?: string;
}

Coordinate utilities

The SDK exposes the internal coordinate conversion functions if you need them directly.

import { latLonToLuref, lurefToLatLon } from '@dacostafilipe/react-geoportail';

// WGS84 → EPSG:2169 (Luxembourg TM)
const { easting, northing } = latLonToLuref(49.6116, 6.1319);

// EPSG:2169 → WGS84
const { lat, lon } = lurefToLatLon(76651, 75358);

Accuracy is within ~1 m across Luxembourg's territory, using a Molodensky transformation (WGS84 → ED50) followed by a Transverse Mercator projection with the official LUREF parameters.


Development

# Install dependencies
npm install

# Start the demo app (Vite dev server)
npm run dev

# Type-check
npm run type-check

# Build the library (outputs to dist/)
npm run build

The demo app (src/demo/main.tsx) shows all three features together: map with click-to-pin, reverse geocoding on pin placement, and an address search bar.


API reference

License

MIT