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

@creative-locator/headless

v2.0.2

Published

Framework-agnostic platform adapters for Creative Locator. Drop-in implementations of the @creative-locator/core ApiClient / I18n / Storage / ConfigSource interfaces for headless consumers (Astro, Next.js, Eleventy, vanilla HTML) that talk to any WordPres

Readme

@creative-locator/headless

Drop-in platform adapters for Creative Locator consumers running outside WordPress.

Provides concrete implementations of the four @creative-locator/core adapter interfaces so that any host environment — Astro, Next.js, Eleventy, vanilla HTML, Remix, SvelteKit, or anything else that can render Leaflet in a browser — can render a Creative Locator against a Creative Locator REST backend over HTTP.

What's included

| Adapter | Interface | Purpose | |---|---|---| | HttpApiAdapter | ApiClient | Generic fetch-based client against {baseUrl}/dealers, /search, /geocode, /taxonomies, /analytics/*. Optional custom headers and fetch override. | | IdentityI18nAdapter | I18n | Single-locale default. Returns source strings unchanged; implements sprintf for positional placeholders. | | BrowserStorageAdapter | Storage | window.localStorage wrapper with graceful degradation for private browsing and SSR. | | InMemoryStorageAdapter | Storage | Map-backed Storage for SSR and tests. | | StaticConfigAdapter | ConfigSource | Takes a LocatorConfig at construction time. |

Install

pnpm add @creative-locator/core @creative-locator/headless leaflet leaflet.markercluster

Quick start

import { createLocator, type PlatformAdapters, type LocatorConfig } from '@creative-locator/core';
import {
	HttpApiAdapter,
	IdentityI18nAdapter,
	BrowserStorageAdapter,
	StaticConfigAdapter,
} from '@creative-locator/headless';
import '@creative-locator/core/styles/style.scss';

const locatorConfig: LocatorConfig = {
	// ... your locator configuration. See @creative-locator/core's LocatorConfig type.
};

const adapters: PlatformAdapters = {
	api: new HttpApiAdapter({
		baseUrl: 'https://example.com/wp-json/creative-locator/v1',
	}),
	i18n: new IdentityI18nAdapter(),
	storage: new BrowserStorageAdapter(),
	config: new StaticConfigAdapter(locatorConfig),
};

const container = document.querySelector<HTMLElement>('#my-locator')!;
createLocator(container, { adapters });

Framework recipes

Astro

Most common pattern: render the locator as an island in an .astro page.

---
// src/pages/locator.astro
import '@creative-locator/core/styles/style.scss';
---

<div id="my-locator"></div>

<script>
	import { createLocator } from '@creative-locator/core';
	import {
		HttpApiAdapter,
		IdentityI18nAdapter,
		BrowserStorageAdapter,
		StaticConfigAdapter,
	} from '@creative-locator/headless';

	createLocator(document.querySelector('#my-locator')!, {
		adapters: {
			api: new HttpApiAdapter({
				baseUrl: import.meta.env.PUBLIC_LOCATOR_API_URL,
			}),
			i18n: new IdentityI18nAdapter(),
			storage: new BrowserStorageAdapter(),
			config: new StaticConfigAdapter({
				/* LocatorConfig — see @creative-locator/core types */
			}),
		},
	});
</script>

The <script> block runs in the browser, so all adapters work as expected. Set PUBLIC_LOCATOR_API_URL in .env to your WordPress site's REST base.

Next.js (App Router, client component)

The locator must mount in a Client Component because Leaflet touches window.

// app/components/Locator.tsx
'use client';
import { useEffect, useRef } from 'react';
import { createLocator } from '@creative-locator/core';
import {
	HttpApiAdapter,
	IdentityI18nAdapter,
	BrowserStorageAdapter,
	StaticConfigAdapter,
} from '@creative-locator/headless';
import '@creative-locator/core/styles/style.scss';

export function Locator() {
	const ref = useRef<HTMLDivElement>(null);

	useEffect(() => {
		if (!ref.current) return;
		const handle = createLocator(ref.current, {
			adapters: {
				api: new HttpApiAdapter({ baseUrl: process.env.NEXT_PUBLIC_LOCATOR_API_URL! }),
				i18n: new IdentityI18nAdapter(),
				storage: new BrowserStorageAdapter(),
				config: new StaticConfigAdapter({
					/* LocatorConfig */
				}),
			},
		});
		return () => handle?.destroy?.();
	}, []);

	return <div ref={ref} />;
}

Then in your page:

// app/locator/page.tsx
import dynamic from 'next/dynamic';
const Locator = dynamic(() => import('@/components/Locator').then((m) => m.Locator), {
	ssr: false,
});
export default function LocatorPage() {
	return <Locator />;
}

SvelteKit (+page.svelte with onMount)

Same pattern as Next.js — defer to onMount so SSR doesn't try to touch window.

<!-- src/routes/locator/+page.svelte -->
<script lang="ts">
	import { onMount } from 'svelte';
	import { createLocator } from '@creative-locator/core';
	import {
		HttpApiAdapter,
		IdentityI18nAdapter,
		BrowserStorageAdapter,
		StaticConfigAdapter,
	} from '@creative-locator/headless';
	import '@creative-locator/core/styles/style.scss';

	let container: HTMLDivElement;

	onMount(() => {
		const handle = createLocator(container, {
			adapters: {
				api: new HttpApiAdapter({
					baseUrl: import.meta.env.PUBLIC_LOCATOR_API_URL,
				}),
				i18n: new IdentityI18nAdapter(),
				storage: new BrowserStorageAdapter(),
				config: new StaticConfigAdapter({
					/* LocatorConfig */
				}),
			},
		});
		return () => handle?.destroy?.();
	});
</script>

<div bind:this={container}></div>

Vanilla HTML

Smallest possible setup — no framework, just an ESM <script type="module">:

<!doctype html>
<link rel="stylesheet" href="/node_modules/@creative-locator/core/dist/style.css" />
<div id="my-locator"></div>
<script type="module">
	import { createLocator } from 'https://esm.sh/@creative-locator/core';
	import {
		HttpApiAdapter,
		IdentityI18nAdapter,
		BrowserStorageAdapter,
		StaticConfigAdapter,
	} from 'https://esm.sh/@creative-locator/headless';

	createLocator(document.querySelector('#my-locator'), {
		adapters: {
			api: new HttpApiAdapter({ baseUrl: 'https://your-site.com/wp-json/creative-locator/v1' }),
			i18n: new IdentityI18nAdapter(),
			storage: new BrowserStorageAdapter(),
			config: new StaticConfigAdapter({
				/* LocatorConfig */
			}),
		},
	});
</script>

SSR / hydration notes

  • All adapters are client-only by design (Leaflet requires window/document).
  • BrowserStorageAdapter gracefully no-ops when window.localStorage is unavailable (private browsing, SSR contexts) — safe to instantiate at module top-level without an if (typeof window …) guard.
  • For SSR-rendered pages: defer createLocator() to a client-only mount (Astro <script>, Next.js useEffect in a Client Component, SvelteKit onMount, etc.).

TypeScript

All adapters ship with full .d.ts definitions. The HttpApiAdapter constructor takes a typed HttpApiAdapterOptions:

interface HttpApiAdapterOptions {
	baseUrl: string;
	headers?: Record<string, string>;
	fetch?: typeof fetch;
}

Override fetch for testing or for environments where the global fetch needs middleware (auth tokens, request logging, retry).

License

GPL-2.0-or-later.