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

headlo-react

v1.0.14

Published

Headlo React SDK

Downloads

450

Readme

Headlo React SDK

React hooks for Headlo. Requires headlo.

Install

npm install headlo headlo-react

Authentication

Headlo supports user-scoped queries — records owned by the currently logged-in visitor. There are two ways to provide a JWT token.

Option A — HeadloAuthProvider (recommended)

Wrap your app (or the subtree that needs auth) with HeadloAuthProvider. Every useCollection and useRecord call inside it will automatically pick up the token without you passing getToken to each hook.

import { HeadloAuthProvider } from 'headlo-react'
import { useAuth } from '@clerk/clerk-react' // or any auth provider

function App() {
  const { getToken } = useAuth()

  return (
    <HeadloAuthProvider getToken={() => getToken()}>
      <YourApp />
    </HeadloAuthProvider>
  )
}

getToken must return Promise<string | null>. The provider memoizes it so hooks don't re-run on every render.

Option B — per-hook getToken

Pass getToken directly to each hook. Useful when different parts of your app use different auth contexts.

const { records } = useCollection('posts', { limit: 10 }, ANON_KEY, undefined, () => myAuth.getToken())

If both are present, the explicit prop takes precedence over the context.


useCollection

Fetches a collection and manages pagination state. Returns navigation functions so you never touch the cursor directly.

import { useCollection } from 'headlo-react'

function Blog() {
  const [pageSize, setPageSize] = useState(10)

  const {
    records,
    count,
    loading,
    error,
    goNext,
    goPrev,
    currentPage,
    totalPages,
  } = useCollection('posts', { limit: pageSize }, ANON_KEY)

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

  return (
    <>
      {records.map(r => (
        <div key={r.collection_record_id}>{r.title}</div>
      ))}

      <button onClick={goPrev} disabled={currentPage === 1}>Prev</button>
      <span>Page {currentPage} of {totalPages}</span>
      <button onClick={goNext} disabled={currentPage === totalPages}>Next</button>
    </>
  )
}

When wrapped in HeadloAuthProvider, the getToken param can be omitted — the hook reads it from context automatically.

Props

| Param | Type | Description | |---|---|---| | collectionId | string | Collection ID | | opts | ListOptions | limit, sort, dir, filter, record_ids | | anonKey | string | Your site anon key | | getToken | () => Promise<string \| null> | Optional — overrides HeadloAuthProvider if both are present |

Returns

| Key | Type | Description | |---|---|---| | records | Record<string, unknown>[] | Flat records for the current page | | count | number | Total matching records | | loading | boolean | | | error | string \| null | | | goNext | () => void | Advance to next page | | goPrev | () => void | Go back to previous page | | currentPage | number | Current page number (1-based) | | totalPages | number | Total pages based on count and limit |

Changing limit or any other option automatically resets to page 1.


useList

For when you want to use the createClient fluent builder but still get managed pagination. Pass .pagination() from the builder and a { limit } option.

import { useList } from 'headlo-react'
import { createClient } from 'headlo'

const client = createClient(ANON_KEY)

function Blog() {
  const [pageSize, setPageSize] = useState(10)

  const { records, count, loading, error, goNext, goPrev, currentPage, totalPages } = useList(
    client.collection('posts').sort('created_at', 'desc').pagination(),
    { limit: pageSize },
  )
}

.pagination() returns a function (cursor, limit) => Promise<...> that useList calls internally — limit and cursor are always controlled by the hook. Filter and sort are pre-configured on the builder before calling .pagination().

You can also pass any async function that matches the signature:

useList(
  async (cursor, limit) => myApi.getPosts({ cursor, limit }),
  { limit: pageSize },
)

useRecord

Fetch a single record by ID. Also reads from HeadloAuthProvider context when no getToken is passed explicitly.

import { useRecord } from 'headlo-react'

const { record, loading, error } = useRecord('posts', recordId, ANON_KEY)

Routing

Pages in Headlo are the routing mechanism. Each page has a page_id that maps to a URL path (/about, /products, /checkout/success). The dashboard resolves which page to render based on the URL.

Every page component receives three routing props automatically:

| Prop | Type | Description | |---|---|---| | query | Record<string, string> | Parsed query string params | | path | string | Extra path segments after the page's own path | | params | string[] | path split by / into an array |

Static routes

A page with page_id = /about renders at /about. Exact match.

Dynamic routes — extra path segments

A page with page_id = /products catches any URL that starts with /products/:

/products/detail/abc-123  →  path = "detail/abc-123", params = ["detail", "abc-123"]
/products/checkout/done   →  path = "checkout/done",  params = ["checkout", "done"]
function Page({ components, params }) {
  const section = params[0]  // "detail"
  const id      = params[1]  // "abc-123"
}

If both /products and /products/detail exist as pages, the most specific match wins.

Query string

/checkout/success?session_id=cs_test_xxx
function Page({ components, query }) {
  const sessionId = query.session_id  // "cs_test_xxx"
}

Useful for Stripe's success_url — pass ?session_id={CHECKOUT_SESSION_ID} and Stripe fills it in automatically.

Stripe checkout example

function Page({ components, query, params }) {
  const status    = params[0]         // "success" or "canceled"
  const sessionId = query.session_id  // Stripe session ID (success only)

  if (status === 'success') return <div>Order confirmed! Session: {sessionId}</div>
  if (status === 'canceled') return <div>Order canceled.</div>
}

With success URL: https://yoursite.com/checkout/success?session_id={CHECKOUT_SESSION_ID} And cancel URL: https://yoursite.com/checkout/canceled


useComponents

Load and compile the component registry for a site. Pass the client directly — the hook configures the component sandbox automatically.

import { createClient } from 'headlo'
import { useComponents } from 'headlo-react'

const client = createClient(ANON_KEY)

function App() {
  const { entries, loading, error } = useComponents(client)

  const card = entries.find(e => e.component_id === 'card_grid')
  if (!card) return null

  return <card.Component records={records} options={card.templateOptions} />
}

License

Elastic License 2.0 — © Headlo Team

Source available. Free for internal use. You may not offer this software as a competing hosted or managed service. See LICENSE for full terms.

Built by Headlo.