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

@moneybar.online/moneybar

v12.1.3

Published

The navbar of monetization. Fix the 3 money-blocking stages: forced sign-ins, silent drop-offs, and broken payment flows. Turn browsers into buyers.

Readme

MoneyBar

The navbar that turns free users into paying customers. Authentication, usage limits, payment checkout, and subscription management — all in one component. No backend required.


Quick Start

Step 1: Create your config file

Create moneybar-config.js in your project:

import { MoneyBar } from 'https://cdn.jsdelivr.net/npm/@moneybar.online/[email protected]/dist/index.bundle.js';

const moneyBar = new MoneyBar({

  // ── Required ────────────────────────────────────────────────
  security_key: 'sk_...',       // Get from MoneyBar dashboard
  actionLabel: 'PDF Exports',   // Shown in navbar: "PDF Exports: 2/3"
  freeAttemptLimit: 3,          // Free attempts before paywall

  // ── Payment (required for paid plans) ───────────────────────
  payment: [{
    provider: 'dodo',
    productId: 'pdt_xxx',       // From Dodo Payments dashboard
    mode: 'live',               // 'test' or 'live'
    name: 'Pro',                // Badge label shown in navbar
    description: 'Unlimited exports, no watermarks'  // Shown in paywall modal (optional)
  }],

  // ── Feedback form (optional) ─────────────────────────────────
  feedback: {
    form: {
      title: 'Quick Feedback',
      description: 'What stopped you from upgrading?',
      option1: 'Too expensive',
      option2: 'Not enough features',
      option3: 'Need more time to evaluate'
    },
    email: [{
      provider: 'resend',
      apiKey: 'your-resend-api-key',
      fromEmail: '[email protected]'
    }]
  },

  // ── Theme (optional) ─────────────────────────────────────────
  theme: {
    name: 'emerald',            // DaisyUI theme name
    primaryColor: '#059669'     // Hex color for buttons and accents
  },

  // ── Navbar title & links (optional) ──────────────────────────
  titleBar: {
    title: 'My App',
    links: [
      { text: 'Features', url: '#features', target: '_self' },
      { text: 'Pricing',  url: '#pricing',  target: '_self' }
    ]
  },

  // ── ⚠️ DO NOT MODIFY ─────────────────────────────────────────
  // MoneyBar's backend — handles auth, payments, and usage tracking.
  // This is pre-configured. Changing these values will break MoneyBar.
  supabase: {
    url: 'https://qhlzdkwcrrjjcqvupyaa.supabase.co',
    anonKey: 'eyJhbGci...'
  }
});

// Make it accessible from your other scripts
window.moneyBar = moneyBar;

Step 2: Add to your HTML

<!DOCTYPE html>
<html>
<head>
  <title>My App</title>
</head>
<body>

  <button id="export-btn">Export PDF</button>

  <!-- Load your MoneyBar config -->
  <script type="module" src="moneybar-config.js"></script>

  <!-- Attach MoneyBar to your action buttons -->
  <script type="module">
    // Wait for MoneyBar to initialize
    window.addEventListener('moneybar:ready', () => {

      window.moneyBar.attachToButton('#export-btn', async (userContext) => {
        if (userContext.isPremium) {
          await exportPDF();                          // unlimited
        } else if (userContext.remaining > 0) {
          await exportPDF();                          // uses 1 free attempt
        }
        // If remaining === 0, MoneyBar shows the paywall automatically
      });

    });
  </script>

</body>
</html>

Step 3: Configure Dodo webhooks

MoneyBar needs Dodo to notify it when payments complete. In your Dodo dashboard:

  • Webhooks → Add endpoint
  • URL: https://qhlzdkwcrrjjcqvupyaa.supabase.co/functions/v1/verify-payment
  • Signing secret: provided to you with your security key — paste it as the webhook signing secret in Dodo
  • Subscribe to: payment.succeeded, subscription.active, subscription.renewed, subscription.updated, subscription.cancelled

Without this step, users who complete checkout will not get premium access. The signing secret is used to verify that webhook calls genuinely come from Dodo. Do not share it publicly.


NPM / Build Tool Projects

npm install @moneybar.online/moneybar
import { MoneyBar } from '@moneybar.online/moneybar';

const moneyBar = new MoneyBar({ ...config });

UserContext Reference

Every attachToButton callback receives a userContext object:

window.moneyBar.attachToButton('#btn', (userContext) => {

  // ── Access ───────────────────────────────────────────────────
  userContext.isPremium           // boolean — has active paid access
  userContext.isAuthenticated     // boolean — is signed in
  userContext.isLapsed            // boolean — was premium, subscription ended

  // ── Usage limits ─────────────────────────────────────────────
  userContext.currentCount        // number  — times used so far
  userContext.remaining           // number  — free attempts left
  userContext.limit               // number  — total free limit

  // ── User info ─────────────────────────────────────────────────
  userContext.email               // string  — user's email (if signed in)
  userContext.name                // string  — user's name (if available)

  // ── Subscription (only when isPremium: true) ──────────────────
  userContext.subscriptionType          // 'day' | 'month' | 'year' | 'one-time'
  userContext.subscriptionId            // string — Dodo subscription ID
  userContext.subscriptionExpiresAt     // ISO string — access expires at
  userContext.subscriptionDaysRemaining // number — days until expiry
  userContext.isSubscriptionActive      // boolean — false if cancel is scheduled
  userContext.cancelAtNextBillingDate   // boolean — cancellation scheduled
  userContext.nextBillingDate           // ISO string — next renewal date
  userContext.customerId                // string — Dodo customer ID

});

State examples

Free user (not signed in):

{ isPremium: false, isAuthenticated: false, isLapsed: false, remaining: 3, limit: 3 }

Free user (signed in, hasn't paid):

{ isPremium: false, isAuthenticated: true, isLapsed: false, email: '[email protected]', remaining: 1, limit: 3 }

Active subscriber:

{
  isPremium: true,
  subscriptionType: 'month',
  subscriptionId: 'sub_xxx',
  subscriptionExpiresAt: '2026-07-10T07:29:45Z',
  subscriptionDaysRemaining: 30,
  isSubscriptionActive: true,
  cancelAtNextBillingDate: false,
  nextBillingDate: '2026-07-10T07:29:45Z',
  customerId: 'cus_xxx'
}

Subscriber who scheduled a cancellation:

{
  isPremium: true,           // still has access until billing date
  isSubscriptionActive: false,
  cancelAtNextBillingDate: true,
  subscriptionExpiresAt: '2026-07-10T07:29:45Z'
}

Lapsed subscriber (subscription ended):

{ isPremium: false, isLapsed: true, email: '[email protected]', remaining: 0 }

Common Patterns

Premium vs free quality

window.moneyBar.attachToButton('#export-btn', async (userContext) => {
  if (userContext.isPremium) {
    await exportHighQuality();
  } else if (userContext.remaining > 0) {
    await exportStandard();
  }
  // paywall shown automatically when remaining === 0
});

Prevent count increment on error

window.moneyBar.attachToButton('#convert-btn', async (userContext) => {
  try {
    await convertImage();
    // count increments only on success
  } catch (err) {
    showError(err.message);
    throw err;   // re-throw → count does NOT increment
  }
});

Multiple buttons, same logic

function handleExport(userContext) {
  if (userContext.isPremium) runExport();
}

window.moneyBar.attachToButton('#btn-png',  handleExport);
window.moneyBar.attachToButton('#btn-pdf',  handleExport);
window.moneyBar.attachToButton('#btn-csv',  handleExport);

Different buttons, different actions

window.moneyBar.attachToButton('#export-btn',  ctx => { if (ctx.isPremium) exportPDF(); });
window.moneyBar.attachToButton('#convert-btn', ctx => { if (ctx.isPremium) convert(); });

attachToButtons — bulk attach with selector map

window.moneyBar.attachToButtons({
  '#export-btn':  (ctx) => { if (ctx.isPremium) exportPDF(); },
  '#convert-btn': (ctx) => { if (ctx.isPremium) convert(); }
});

What MoneyBar Handles Automatically

| Feature | Detail | |---|---| | Authentication | Google sign-in (more providers coming) | | Usage tracking | Counts per user per app, resets per plan | | Payment checkout | Dodo Payments checkout flow | | Subscription management | Renewal, cancellation, resubscription | | Customer portal | Self-service subscription management via "Manage Payments" | | Help & support | Built-in ? icon → contextual help modal + support email form | | Caching | Premium status cached 24h; usage count cached 60s | | Multi-app | Same user across multiple apps, separate limits per app |


Configuration Reference

| Key | Required | Description | |---|---|---| | security_key | Yes | App identifier — get from MoneyBar dashboard | | actionLabel | Yes | What users are doing, shown in navbar | | freeAttemptLimit | Yes | Free attempts before paywall (0 = always paywalled) | | payment | For paid plans | Dodo product config (provider, productId, mode, name, description) | | feedback.form | No | Quick Feedback modal options | | feedback.email | No | Resend config for feedback + support emails | | theme | No | DaisyUI theme name + primary hex color | | titleBar | No | Navbar title and nav links | | supabase | Yes | Pre-configured — do not modify |


Getting a Security Key

MoneyBar is currently in private beta.


Support


License

MIT