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

@chaosity/location-client-react

v0.1.19

Published

React bindings for Chaosity Location Service client

Readme

@chaosity/location-client-react

React bindings for @chaosity/location-client with automatic token refresh.

Installation

npm install @chaosity/location-client-react @chaosity/location-client

Quick Start

1. Create a Server Action to fetch config

// app/actions/location.ts
'use server'

import { getClientConfig } from '@chaosity/location-client/server'

export async function getLocationConfig() {
  // Auto-reads LOCATION_API_URL, LOCATION_CLIENT_ID, LOCATION_CLIENT_SECRET
  return await getClientConfig()
}

2. Wrap your app with the provider

// app/layout.tsx
'use client'

import { LocationClientProvider } from '@chaosity/location-client-react'
import { getLocationConfig } from './actions/location'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <LocationClientProvider getConfig={getLocationConfig}>
      {children}
    </LocationClientProvider>
  )
}

3. Use the client in any component

import { useLocationClient } from '@chaosity/location-client-react'
import { SuggestCommand } from '@chaosity/location-client'

function SearchComponent() {
  const { client, loading, error } = useLocationClient()

  const searchPlaces = async (query: string) => {
    if (!client) return
    const response = await client.send(
      new SuggestCommand({ QueryText: query, MaxResults: 5 }),
    )
    return response.ResultItems
  }

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error}</div>

  return <div>...</div>
}

Map Utilities

useMapLanguage

React hook that keeps map label language in sync. Automatically reapplies after map.setStyle() calls (e.g. when switching color schemes or terrain).

import { useMapLanguage } from '@chaosity/location-client-react'

function MapComponent() {
  const [mapInstance, setMapInstance] = useState<maplibregl.Map | null>(null)
  const [language, setLanguage] = useState('en')

  // Keeps labels in sync — zero API calls for language changes
  useMapLanguage(mapInstance, language)

  useEffect(() => {
    const map = new maplibregl.Map({
      /* ... */
    })
    map.once('load', () => setMapInstance(map))
    return () => map.remove()
  }, [])

  return (
    <>
      <select value={language} onChange={(e) => setLanguage(e.target.value)}>
        <option value="en">English</option>
        <option value="fr">Français</option>
        <option value="de">Deutsch</option>
        <option value="ja">日本語</option>
      </select>
      <div ref={mapContainer} />
    </>
  )
}

API Reference

LocationClientProvider

Provides the location client and automatic token refresh to all child components.

<LocationClientProvider getConfig={getLocationConfig} refreshBuffer={60}>
  {children}
</LocationClientProvider>

Props:

  • getConfig — Async function that returns { apiUrl: string, token: string, expiresAt?: number }. Called on init and whenever the token needs refreshing.
  • refreshBuffer (optional, default: 60) — Seconds before token expiry to proactively refresh. Prevents mid-request expiration.
  • children — Child components.

useLocationClient

Hook to access the location client in any component.

const { client, getToken, loading, error } = useLocationClient()

Returns:

  • client (GeoPlacesClient | null) — The location client instance. Automatically refreshes the token before each send() call if needed.
  • getToken (() => string | undefined) — Returns the current token. Useful for direct API calls (e.g., map style fetch).
  • loading (boolean) — Whether the client is initializing.
  • error (string | null) — Error message if initialization or token refresh failed.

Throws: Error if used outside LocationClientProvider.

Token Refresh

The provider automatically handles token lifecycle:

  1. Fetches an initial token via getConfig on mount
  2. Before each client.send() call, checks if the token is expired or within the refreshBuffer window
  3. If expired, calls getConfig again to get a fresh token
  4. Concurrent refresh requests are deduplicated — multiple send() calls wait for the same refresh

No manual token management needed. The client always uses a valid token.

Complete Example with MapLibre

'use client'

import { useEffect, useRef, useState } from 'react'
import {
  useLocationClient,
  useMapLanguage,
} from '@chaosity/location-client-react'
import {
  GeoPlaces,
  fetchMapStyle,
  createTransformRequest,
} from '@chaosity/location-client'
import maplibregl from 'maplibre-gl'
import MaplibreGeocoder from '@maplibre/maplibre-gl-geocoder'

const API_URL = process.env.NEXT_PUBLIC_LOCATION_API_URL!

export default function MapComponent() {
  const mapContainer = useRef<HTMLDivElement>(null)
  const map = useRef<maplibregl.Map | null>(null)
  const [mapInstance, setMapInstance] = useState<maplibregl.Map | null>(null)
  const [language, setLanguage] = useState('en')
  const { client, getToken, loading, error } = useLocationClient()

  // Keeps map labels in sync with language — reapplies after every setStyle() call
  useMapLanguage(mapInstance, language)

  useEffect(() => {
    if (!mapContainer.current || map.current || loading || !client) return
    ;(async () => {
      // Fetch style with terrain, 3D buildings, and language baked into the descriptor
      const style = await fetchMapStyle(API_URL, 'Standard', getToken, {
        colorScheme: 'Light',
        terrain: 'Terrain3D',
        buildings: 'Buildings3D',
        language,
      })

      const instance = new maplibregl.Map({
        container: mapContainer.current!,
        style,
        center: [-123.12, 49.28],
        zoom: 10,
        maxPitch: 85,
        transformRequest: createTransformRequest(API_URL, getToken),
      })

      instance.addControl(
        new maplibregl.NavigationControl({ visualizePitch: true }),
        'top-right',
      )
      instance.addControl(
        new maplibregl.TerrainControl({ source: 'amazon' }),
        'top-right',
      )

      const geoPlaces = new GeoPlaces(client, instance)
      const geocoder = new MaplibreGeocoder(geoPlaces, {
        maplibregl,
        showResultsWhileTyping: true,
        limit: 30,
      })
      instance.addControl(geocoder, 'top-left')

      map.current = instance
      setMapInstance(instance)
    })()

    return () => {
      if (map.current) {
        map.current.remove()
        map.current = null
        setMapInstance(null)
      }
    }
  }, [client, getToken, loading])

  if (error) return <div>Error: {error}</div>
  if (loading) return <div>Loading map...</div>

  return <div ref={mapContainer} style={{ width: '100%', height: '600px' }} />
}

useMapLanguage

Hook that keeps map labels in the specified language. Registers a persistent style.load listener so language is automatically reapplied after map.setStyle() calls.

useMapLanguage(map: MapLike | null, language: string): void

Parameters:

  • map — MapLibre Map instance, or null while the map is initializing.
  • language — ISO 639-1 language code (e.g. 'en', 'fr', 'de', 'ja', 'zh', 'ar').

Available Commands

All AWS Location Service commands are available through the client:

import {
  SuggestCommand,
  GeocodeCommand,
  ReverseGeocodeCommand,
  GetPlaceCommand,
  SearchTextCommand,
  SearchNearbyCommand,
} from '@chaosity/location-client'

function MyComponent() {
  const { client } = useLocationClient()

  const search = async () => {
    const response = await client!.send(
      new SuggestCommand({ QueryText: 'Vancouver', MaxResults: 5 }),
    )
    return response.ResultItems
  }
}

Logging

Enable debug logging with the DEBUG environment variable:

DEBUG=location-client-react:* npm run dev

TypeScript Support

Full TypeScript support with types from AWS SDK:

import type { SuggestCommandOutput } from '@aws-sdk/client-geo-places'

const { client } = useLocationClient()
const response: SuggestCommandOutput = await client!.send(
  new SuggestCommand({ QueryText: 'Vancouver' }),
)

License

MIT