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

neutron-react

v0.1.2

Published

React components for Neutron Bitcoin Lightning payments — drop-in checkout in one line

Readme

Neutron React

npm version License: MIT

Drop-in React components for Bitcoin Lightning payments. Add a payment checkout to your app in one line.

Install

npm install neutron-react

Prerequisites

Sign up at portal.neutron.me to get your API key and secret. You'll need neutron-sdk on your backend.

Quick Start

import { NeutronPay } from "neutron-react";

function Checkout() {
  return (
    <NeutronPay
      amountSats={10000}
      createUrl="/api/neutron/create-invoice"
      statusUrl="/api/neutron/status"
      onPaid={(data) => console.log("Paid!", data.txnId)}
    />
  );
}

That's it. The component handles:

  • ⚡ QR code generation
  • 📋 Copy-to-clipboard
  • 📱 "Open in Wallet" deep link
  • ⏰ Countdown timer
  • 🔄 Auto-polling for payment status
  • ✅ Success / expired / error states
  • 🌙 Light and dark themes

Backend Setup

The component talks to YOUR backend (never exposes API keys in the browser).

You need two endpoints:

1. Create Invoice (POST /api/neutron/create-invoice)

// Next.js API route example
import { Neutron } from "neutron-sdk";

const neutron = new Neutron({
  apiKey: process.env.NEUTRON_API_KEY!,
  apiSecret: process.env.NEUTRON_API_SECRET!,
});

export async function POST(req: Request) {
  const { amountSats, memo, orderId } = await req.json();

  const invoice = await neutron.lightning.createInvoice({
    amountSats,
    memo,
    extRefId: orderId,
  });

  return Response.json({
    invoice: invoice.invoice,
    txnId: invoice.txnId,
    amountSats: invoice.amountSats,
    expiresAt: Date.now() + 3600000, // 1 hour
    qrPageUrl: invoice.qrPageUrl,
  });
}

2. Check Status (GET /api/neutron/status?txnId=xxx)

export async function GET(req: Request) {
  const txnId = new URL(req.url).searchParams.get("txnId")!;
  const txn = await neutron.transactions.get(txnId);

  return Response.json({ status: txn.txnState });
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | amountSats | number | required | Amount in satoshis | | createUrl | string | required | Your backend endpoint to create invoices | | statusUrl | string | required | Your backend endpoint to check payment status | | memo | string | — | Invoice description | | orderId | string | — | Your reference ID (sent to createUrl) | | onPaid | (data) => void | — | Called when payment is confirmed | | onExpired | (data) => void | — | Called when invoice expires | | onError | (error) => void | — | Called on any error | | theme | "light" \| "dark" | "light" | Color theme | | qrSize | number | 256 | QR code size in pixels | | showAmount | boolean | true | Show amount display | | showCopyButton | boolean | true | Show copy invoice button | | showWalletButton | boolean | true | Show "Open Wallet" button | | pollIntervalMs | number | 3000 | Status polling interval | | displayCurrency | string | — | Fiat currency label (e.g. "USD") | | exchangeRate | number | — | Sats per fiat unit for display | | style | CSSProperties | — | Custom container styles | | className | string | — | Custom CSS class | | header | ReactNode | — | Custom header content | | footer | ReactNode | — | Custom footer content | | createParams | object | — | Extra params sent to createUrl |

useNeutronPayment Hook

For full control, use the hook directly:

import { useNeutronPayment } from "neutron-react";

function CustomCheckout() {
  const { status, invoice, qrDataUrl, error, create, reset } = useNeutronPayment({
    createInvoice: async () => {
      const res = await fetch("/api/create-invoice", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ amountSats: 10000 }),
      });
      return res.json();
    },
    checkStatus: async (txnId) => {
      const res = await fetch(`/api/status?txnId=${txnId}`);
      const data = await res.json();
      return data.status;
    },
    onPaid: (data) => console.log("Paid!", data),
    onExpired: (data) => console.log("Expired"),
  });

  return (
    <div>
      <p>Status: {status}</p>
      {qrDataUrl && <img src={qrDataUrl} alt="QR" />}
      {invoice && <p>{invoice.invoice}</p>}
      {status === "expired" && <button onClick={create}>Retry</button>}
    </div>
  );
}

Hook Return Values

| Value | Type | Description | |-------|------|-------------| | status | PaymentStatus | "idle" \| "creating" \| "waiting" \| "paid" \| "expired" \| "error" | | invoice | InvoiceData \| null | Invoice data after creation | | qrDataUrl | string \| null | QR code as a data URL | | error | Error \| null | Error if any | | create | () => void | Create or recreate an invoice | | reset | () => void | Reset to idle state |

useCountdown Hook

Standalone countdown timer:

import { useCountdown } from "neutron-react";

const { formatted, isExpired, timeLeft } = useCountdown(expiresAt);
// formatted: "4:32"
// isExpired: false
// timeLeft: 272000 (ms)

Dark Theme

<NeutronPay
  amountSats={10000}
  createUrl="/api/create-invoice"
  statusUrl="/api/status"
  theme="dark"
/>

Custom Styling

<NeutronPay
  amountSats={10000}
  createUrl="/api/create-invoice"
  statusUrl="/api/status"
  style={{ borderRadius: "20px", maxWidth: "400px" }}
  className="my-payment-widget"
  header={<h2>Pay with Bitcoin</h2>}
  footer={<p>Questions? Contact support</p>}
/>

Complete Next.js Example

your-app/
├── app/
│   ├── api/neutron/
│   │   ├── create-invoice/route.ts    # POST: creates invoice
│   │   └── status/route.ts            # GET: checks payment
│   └── checkout/page.tsx              # Frontend checkout page
├── package.json
npm install neutron-sdk neutron-react

Frontend (app/checkout/page.tsx):

"use client";
import { NeutronPay } from "neutron-react";
import { useRouter } from "next/navigation";

export default function Checkout() {
  const router = useRouter();

  return (
    <div style={{ display: "flex", justifyContent: "center", padding: "40px" }}>
      <NeutronPay
        amountSats={50000}
        memo="Premium subscription"
        orderId="order-123"
        createUrl="/api/neutron/create-invoice"
        statusUrl="/api/neutron/status"
        onPaid={() => router.push("/success")}
        theme="light"
      />
    </div>
  );
}

Links

License

MIT