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

lightningconnect

v1.0.1

Published

Zero-friction Bitcoin wallet connection for React. Four connectors, automatic payment detection, one drop-in component.

Downloads

384

Readme

LightningConnect

Zero-friction Bitcoin wallet connection for any web app. Four connectors across two categories, one component, automatic payment detection.

LightningConnect is a drop-in React widget + hook that solves the wallet connection AND payment detection problem for Bitcoin web apps. It is Blink-native — with two dedicated Blink connectors no other library ships — and universally compatible with every other wallet via generic Lightning Address and Nostr Wallet Connect. Your app makes invoices and gets a callback when they're paid. No polling code to write.

v1.0.0 is here. Payment detection is now built in. Connect, invoice, and get paid — zero polling code required.

npm install lightningconnect

Why

Most Bitcoin payment libraries leave the hard parts to you: polling for payment, cleaning up intervals, handling expiry. LightningConnect ships four browser-side connectors AND built-in payment detection so every user — from casual to power — can pay without you writing a single setInterval.

  • Blink-native — two dedicated Blink connectors (Address + API Key)
  • Universally compatible — generic Lightning Address + NWC for every other wallet
  • Auto payment detectiononPayment callback fires once, automatically
  • Auto cleanup — watchers stop on PAID/EXPIRED and on unmount
  • Light & dark mode — automatic theme switching with persisted preference
  • Encrypted local storage — device-bound via Web Crypto
  • <30kb gzipped — only React as a peer dep

Quick start

import { LightningConnect, useWalletConnect } from "lightningconnect";

export default function App() {
  const { connect, isConnected, makeInvoice } = useWalletConnect({
    onPayment: (invoice) => alert(`Paid: ${invoice.amount} sats`),
    onExpiry: (invoice) => console.log("Expired", invoice.paymentHash),
  });

  return (
    <>
      <LightningConnect theme={{ primary: "#F7931A" }} />
      {isConnected ? (
        <button onClick={() => makeInvoice(1000, "BTC", "Coffee")}>
          Get paid 1000 sats
        </button>
      ) : (
        <button onClick={connect}>Connect wallet</button>
      )}
    </>
  );
}

That's it. No setInterval, no useEffect, no cleanup. LightningConnect starts watching as soon as makeInvoice() resolves and fires onPayment exactly once when it settles.

Before & after

Before — manual polling

const invoice = await makeInvoice(1000, "BTC", "Coffee");
const interval = setInterval(async () => {
  const status = await lookupInvoice(invoice.paymentHash, invoice);
  if (status === "PAID") {
    clearInterval(interval);
    handlePayment(invoice);
  } else if (status === "EXPIRED") {
    clearInterval(interval);
    handleExpiry(invoice);
  }
}, 5000);
// …remember to clearInterval on unmount, on navigation, on error…

After — built-in onPayment

const { makeInvoice } = useWalletConnect({
  onPayment: handlePayment,
  onExpiry: handleExpiry,
});
await makeInvoice(1000, "BTC", "Coffee");
// done. LightningConnect handles polling, dedupe, expiry, and cleanup.

BlinkInvoice-style components (the reference implementation) used to wrap a setInterval lookup loop. The recommended migration is to drop the loop entirely and use onPayment on the parent hook.

The hook

const {
  connect,         // () => void — opens the connect modal
  disconnect,      // () => void — clears the stored connection
  isConnected,     // boolean
  connectionType,  // 'blink-address' | 'nwc' | 'blink-api' | null
  makeInvoice,     // (amount, 'USD' | 'BTC', memo) => Promise<Invoice>
  lookupInvoice,   // (paymentHash, invoice?) => Promise<'PAID' | 'PENDING' | 'EXPIRED'>
  cancelWatch,     // (invoice) => void — stop watching a specific invoice
  walletInfo,      // { name, address, currency } | null
} = useWalletConnect({
  onPayment,        // (invoice) => void — fires exactly once when PAID
  onExpiry,         // (invoice) => void — fires when EXPIRED
  onError,          // (error, invoice) => void — lookup errors (non-fatal)
  pollInterval,     // number — defaults to 5000ms
});

Guarantees

  • onPayment fires exactly once per invoice, even if polling overlaps the PAID transition.
  • Watchers stop automatically on PAID, EXPIRED, or when invoice.expiresAt is reached.
  • Component unmount cancels all active watchers — no leaked timers.
  • cancelWatch(invoice) lets you stop watching manually (e.g. user navigated away).

Per-connector behaviour

| Connector | Polling primitive | PAID signal | EXPIRED signal | | --- | --- | --- | --- | | Blink Lightning Address | LNURL verify URL | settled: true | expiresAt past | | Lightning Address (any) | LNURL verify URL | settled: true | expiresAt past | | NWC | lookup_invoice request | settled_at, preimage, state === paid | state === expired | | Blink API Key | lnInvoicePaymentStatus query | status === PAID | status === EXPIRED |

The four connectors

LightningConnect groups its connectors into two categories — Blink (native, premium) and Other Wallets (universal compatibility).

Blink

Blink Lightning Address

The user types satoshi (or [email protected]). The widget validates the address via https://blink.sv/.well-known/lnurlp/{username}, then calls the LNURL-pay callback to mint invoices. No API key, no dashboard. Recommended for most users.

Blink API Key (advanced)

Full control for power users. Paste a Blink API key and LightningConnect talks directly to https://api.blink.sv/graphql, unlocking BTC + USD invoice creation, real-time payment status, transaction history, balance, and the full account surface.

Setup:

  1. Open dashboard.blink.sv
  2. Navigate to API Keys
  3. Create a key with READ + RECEIVE scopes
  4. Paste it into the widget

Your API key is encrypted with AES-GCM and stored on-device — it never leaves the browser.

Other Wallets

₿ Lightning Address

Any standard Lightning Address ([email protected], [email protected], [email protected], …). Resolved via standard LNURL-pay (/.well-known/lnurlp/{user}), so it works with Wallet of Satoshi, Alby, Coinos, Strike and every other Lightning Address provider.

Nostr Wallet Connect (Beta)

Pair by pasting an nostr+walletconnect://... string. The widget talks to the wallet over a Nostr relay using NIP-47: make_invoice to mint, lookup_invoice for status. Works with Alby Hub, Zeus, Phoenix, Mutiny and any NIP-47 compatible wallet.

Standalone payment watcher

If you need payment detection outside the hook, use watchPayment directly:

import { watchPayment } from "lightningconnect";

const cancel = watchPayment({
  invoice,
  lookup: () => lookupInvoice(paymentHash),
  pollInterval: 5000,
  onPayment: (inv) => console.log("Paid!", inv),
  onExpiry: (inv) => console.log("Expired", inv),
  onError: (err) => console.error(err),
});

// Stop manually
cancel();

Theming

Dark mode (default)

<LightningConnect
  theme={{
    primary: "#F7931A",
    background: "#0A0A0A",
    foreground: "#F5F5F5",
    border: "#262626",
    radius: "14px",
    muted: "#A1A1AA",
  }}
/>

Light mode

Pass lightTheme to define the light appearance, and defaultMode to set the starting mode:

<LightningConnect
  theme={{ primary: "#F7931A" }}            // dark mode overrides
  lightTheme={{ primary: "#F7931A" }}       // light mode overrides
  defaultMode="light"                       // "light" | "dark"
/>

The widget renders a sun/moon toggle in the modal header. The user's choice is persisted to localStorage under the key lightningconnect:mode. If no lightTheme is provided, sensible light defaults are used automatically.

All theme keys are optional in both theme and lightTheme.

Storage & portability

Connections are encrypted with AES-GCM using a key derived from a device fingerprint via PBKDF2 and stored in localStorage.

import { exportConnection, importConnection } from "lightningconnect";

const token = await exportConnection();
await importConnection(token);

Build output

  • ESM + CJS bundles
  • Full TypeScript types
  • <30kb gzipped (excluding peer React)

License

MIT.