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 🙏

© 2025 – Pkg Stats / Ryan Hefner

tailbits-js

v0.0.4

Published

A lightweight, framework-agnostic SDK for Tailbits experiences. Components are implemented as Web Components using Lit, with real-time updates over a resilient reconnecting WebSocket (via `reconnecting-websocket`) and background data fetching powered by T

Readme

tailbits-js

A lightweight, framework-agnostic SDK for Tailbits experiences. Components are implemented as Web Components using Lit, with real-time updates over a resilient reconnecting WebSocket (via reconnecting-websocket) and background data fetching powered by TanStack Query. React wrappers are generated for convenient use in React and a CDN build is available for drop‑in usage.

Features

  • Web Components for framework agnosticism
  • Background refetch + interval polling via @tanstack/query-core
  • Real-time invalidation via reconnecting-websocket
  • Typed API client generated from OpenAPI via orval, using ky as fetcher
  • React wrappers generated from the Custom Elements Manifest
  • CDN and ESM builds

Getting Started (CDN)

Use the CDN as the primary integration path. Import the loader, and call init(...) once.

<script type="module">
  import { init } from 'https://unpkg.com/tailbits-js';

  init({
    projectId: '{{ your-tailbits-project-id }}',
    environmentId: '{{ your-env-id }}',
  });
</script>

Notes

  • You can pin the CDN to a version: https://unpkg.com/[email protected].
  • Use preload: ['tb-journey'] in init(...) if you want to eagerly load known tags. The default behavior is to load features lazily, as they're detected on the page.

Config Options

Call init(options) once per page load. Subsequent calls throw.

| Option | Type | Default | Required | Description | |-----------------|------------------------------|--------------------------------------|----------|--------------------------------------------------------------------------------------------------------------------| | projectId | string | — | Yes | Your Tailbits project ID. | | environmentId | string | — | Yes | The environment identifier for the page. | | preload | string[] | [] | No | Feature tags to eagerly load (e.g., ['tb-journey']). Otherwise features load lazily when first seen on the page. | | onError | (error: unknown) => void | undefined | No | Global error handler for SDK-level errors. Components also emit tb-error events. |

Error Handling

Handle errors globally via onError, and at the component level via the tb-error event. In most cases, it's enough to use one of these hooks.

<tb-journey journey-id="..." step="install_cli"></tb-journey>

<script type="module">
  import { init } from 'https://unpkg.com/tailbits-js';

  init({
    projectId: '...',
    environmentId: '...',
    onError(error) {
      // Global SDK errors (e.g., network/socket)
      console.error('[tailbits onError]', error);
    },
  });

  const el = document.querySelector('tb-journey');
  el?.addEventListener('tb-error', (e) => {
    // Component-level errors include a message and original error
    console.error('[tb-error]', e.detail);
  });
</script>

Components

tb-journey

Render tb-journey anywhere on your page, and the loader will dynamically fetch the required assets to wire it up.

<tb-journey journey-id="{{ your-journey-id }}" step="{{ your-journey-step }}">
  <h3>Install the CLI</h3>
  <!-- Optional slotted content -->
</tb-journey>

The above is all it takes to wire up a Tailbits Journey. Add more elements with different steps to wire up the complete journey. Note that every tb-journey element must have a journey-id and step assigned.

Events

It’s possible to attach event listeners to tb-journey elements using the native addEventListener API.

const el = document.querySelector('tb-journey');
el.addEventListener('tb-step-update', (event) => {
  console.log('step status:', event.detail.status);
});

el.addEventListener('tb-error', (event) => {
  console.error('component error:', event.detail);
})

React (npm)

If using the CDN route is not an option for you, install the package and the peer dependencies, initialize via the loader export, and render the generated React wrappers.

npm i tailbits-js react react-dom
import { init } from 'tailbits-js/loader';
import Journey from 'tailbits-js/react/tb-journey';

init({ 
  projectId: '...', 
  environmentId: '...',
});

export function App() {
  return (
    <Journey
      journeyId="..."
      step="install_cli"
      onStepUpdate={(e) => console.log('step status:', e.detail.status)}
    >
      <h3>Install the CLI</h3>
    </Journey>
  );
}

Architecture

Components (Lit)

  • Web Components live in src/features/* and extend a small base in src/internal/core/base-element.ts.
  • Elements emit typed custom events (e.g., tb-step-update, tb-error) declared in src/events/*.

Loader + Registry

  • Entry point src/loader.ts sets global config and registers a lazy element loader using import.meta.glob.
  • The registry in src/internal/core/registry.ts observes the DOM and dynamically imports elements on first sighting; preload lets you eagerly load known tags.

Data Layer (TanStack Query Core)

  • Central client in src/internal/api/query.ts configures sensible defaults:
    • staleTime: 60_000, gcTime: 5 * 60_000, retry: 3
    • refetchOnWindowFocus: true for background freshness
    • Per-query refetchInterval (e.g., 30s for journey progress)
  • Components call this.query(...) to subscribe to query results and lifecycle; errors are surfaced via a tb-error event.

Real-time (WebSocket)

  • src/internal/core/socket.ts uses reconnecting-websocket to create a resilient connection.
  • Incoming messages trigger targeted invalidations (e.g., step_update invalidates ['journey', id]), seamlessly syncing server pushes with cached data.

API SDK (HTTP)

  • orval.config.ts generates src/internal/api/client.ts from the Tailbits OpenAPI spec.
  • HTTP is performed by ky via a small wrapper in src/internal/api/fetcher.ts (with centralized error forwarding via src/internal/core/errors.ts).

React Wrappers

  • Thin React adapters for each element are generated with @lit/react and live under src/react/* and dist/react/*.
  • Codegen is driven by the Custom Elements Manifest using a Vite plugin: scripts/vite-plugin-cem-react.ts.
  • Event props map to element events (e.g., onStepUpdatetb-step-update).

Agnostic by Design

  • The primary deliverable is a set of standard Web Components (Lit) that run without any framework.
  • React wrappers are optional and add zero business logic—just ergonomic bindings.

Background Fetching & Intervals

  • Queries are configured for background freshness (refetchOnWindowFocus: true).
  • Interval polling is used where appropriate (e.g., getJourneyQueryOptions sets refetchInterval: 30_000).
  • WebSocket messages act as server‑driven invalidation signals for immediate consistency.

Builds

Library (ESM)

  • npm run build:lib produces ESM modules in dist with type declarations.
  • Exports map:
    • tailbits-jsdist/cdn/loader.js (CDN/loader)
    • tailbits-js/loaderdist/loader.js
    • tailbits-js/react/*dist/react/*.js
    • tailbits-js/features/*dist/features/*.js
    • tailbits-js/events/*dist/events/*.js

CDN

  • npm run build:cdn creates a CDN‑friendly bundle in dist/cdn for <script type="module"> usage.

React Codegen

  • React wrappers are generated during Vite builds by scripts/vite-plugin-cem-react.ts and written to src/react/* (sources) and dist/react/* (output).

Events

  • tb-step-update{ journeyId, stepId, status: 'incomplete' | 'complete' | 'pending' }
  • tb-error{ message, error }

React wrappers expose event props mirroring these (e.g., onStepUpdate). Types are exported from tailbits-js/events/* and tailbits-js/react/*.

Demos

  • Vanilla demo: yarn dev:vanilla, sources exist in demo/vanilla/index.html
  • React demo: yarn dev:react, sources exist in demo/react/main.tsx
  • Mock server for demos is powered by msw (see mocks/*).