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

@netiva-ai/elements

v0.0.3

Published

Embeddable, framework-agnostic React billing components for Netiva — a Stripe-Elements-style provider plus a themeable PlanSelector and a token-authenticated NetivaCheckout. Built on the shared @netiva-ai/billing primitives.

Readme

@netiva-ai/elements

Embeddable, themeable React billing components for Netiva — the Stripe-Elements-style way to host Netiva checkout/subscriptions directly in your app.

Built on the shared @netiva-ai/billing primitives (the same primitives power the billing parts of @netiva-ai/site-kit).

The components:

  • <PlanSelector> — a themeable grid of plan cards. Feed it the plans you already mirror in your own DB (or let it fetch the workspace's plans).
  • <NetivaCheckout> — a Stripe-Elements subscription checkout that authenticates from a short-lived token your server mints (no in-component login), runs Stripe's SCA-safe off-session SetupIntent flow, and fires onCheckoutComplete.
  • <CurrentPlan> — a "preview current plan" card showing the customer's live subscription (plan, status, renewal/trial/cancellation), read from the same clientToken.
  • <ManageBillingButton>one-click login into the Netiva members portal: your server generates a magic link, the button opens it, and the already-known customer lands in the full self-service portal with no email round-trip.

Works in any React app (Vite, CRA, Next.js, Remix) — no Tailwind, no framework lock-in, no global CSS, no styling runtime.

Install

npm install @netiva-ai/elements @stripe/stripe-js @stripe/react-stripe-js

Peers: react >= 18, react-dom >= 18, and the two @stripe/* packages (the bundle statically imports Stripe for the checkout). @netiva-ai/billing is a peer dependency — it is kept external so the provider and every hook share one React-context instance across your app (and with @netiva-ai/site-kit if you also use it). See Compatibility.

How auth works (the one thing to wire up)

Your app already owns the customer (you created it server-to-server via @netiva-ai/sdk). To let <NetivaCheckout> act as that customer without a second login, your server mints a short-lived, customer-scoped token and your client passes it as clientToken:

1. Your server  →  POST {apiUrl}/api/v1/billing-session-token   (API-key auth)
                   body: { customerId, organizationId }
                   resp: { token, expiresInMinutes }   // scoped to customer + org, ≤15 min
2. Your client  →  <NetivaCheckout clientToken={token} … />

The token is short-lived and never persisted — mint it on demand right before showing checkout (e.g. via @netiva-ai/sdk's netiva.billing.generateSessionToken). If it expires mid-flow, onTokenExpired fires so you can mint a fresh one. Your webhook stays authoritative for feature activation; onCheckoutComplete just closes the modal and refetches.

Quickstart

import '@netiva-ai/elements/styles.css'
import { useState } from 'react'
import { NetivaProvider, PlanSelector, NetivaCheckout } from '@netiva-ai/elements'
import type { PlanPrice } from '@netiva-ai/elements'

const config = {
  apiUrl: 'https://api.netiva.ai',
  workspaceId: 'YOUR_WORKSPACE_ID',
  // No Stripe key here — the provider loads the workspace's publishable key
  // from Netiva automatically.
}

export default function Billing({ plans }) {
  const [selected, setSelected] = useState<PlanPrice | null>(null)
  const [clientToken, setClientToken] = useState<string | null>(null)

  async function onSelectPlan(price: PlanPrice) {
    setSelected(price)
    // Preferred: use the official @netiva-ai/sdk on your server
    // const { token } = await netiva.billing.generateSessionToken({ customerId: 'cus_123' })
    // Fallback / custom server: call the endpoint directly with your nsk_ API key
    const { token } = await fetch('/api/mint-netiva-token', { method: 'POST' }).then((r) => r.json())
    setClientToken(token)
  }

  return (
    <NetivaProvider config={config} appearance={{ variables: { colorPrimary: '#4f46e5' } }}>
      <PlanSelector plans={plans} onSelectPlan={onSelectPlan} />
      {selected && clientToken && (
        <NetivaCheckout
          clientToken={clientToken}
          priceId={selected.id}
          onCheckoutComplete={() => { setSelected(null); /* close modal + refetch org */ }}
          onError={(e) => console.error(e)}
          onTokenExpired={() => setClientToken(null)}
        />
      )}
    </NetivaProvider>
  )
}

<NetivaProvider> mounts once, near your root, and composes the whole site-kit stack (config, customer auth, Stripe <Elements>, plans). Import the stylesheet once — all styles are self-contained and scoped under the provider; nothing leaks into (or out of) your app's CSS.

Current plan + one-click portal

Pass the same clientToken to the provider (not just <NetivaCheckout>) and the kit can read the customer's live subscription — <CurrentPlan> previews it, and the plan grid auto-marks their current plan. For everything checkout can't do (payment methods, invoices, cancel/upgrade), <ManageBillingButton> one-click-logs the customer into the members portal: your server generates a magic link, the button opens it, no email round-trip.

import { NetivaProvider, CurrentPlan, ManageBillingButton } from '@netiva-ai/elements'

// Your server: POST {apiUrl}/api/v1/magic-link/generate (API-key auth) — or the
// SDK's `netiva.magicLink.generate({ customerId, redirectTo })` — returns { link }.
const getPortalUrl = async () =>
  (await fetch('/api/portal-link', { method: 'POST' }).then((r) => r.json())).url

<NetivaProvider config={config} clientToken={sessionToken}>
  <CurrentPlan>
    <ManageBillingButton getPortalUrl={getPortalUrl}>Manage billing</ManageBillingButton>
  </CurrentPlan>
</NetivaProvider>

<CurrentPlan> renders nothing until a session exists, so mount it only where you've supplied a clientToken. <ManageBillingButton> holds no API key — it just calls your getPortalUrl, exactly like the checkout token is minted server-side.

Theming — full per-element control via CSS variables

A single color prop won't cut it, so every element is restyleable. Set global tokens (palette + scale) and/or per-element tokens (button shape, input chrome, card borders, error states, …). Each per-element token falls back to a global one, so you override only what you want:

<NetivaProvider
  config={config}
  appearance={{
    theme: 'light', // 'light' | 'dark' | 'flat'
    variables: {
      // global
      colorPrimary: '#4f46e5',
      fontFamily: 'Inter, system-ui, sans-serif',
      // per-element
      planCardRadius: '16px',
      planCardBorder: '1px solid #e5e7eb',
      buttonRadius: '9999px',
      buttonPadding: '12px 22px',
      inputRadius: '10px',
      inputBorder: '1px solid #d1d5db',
      priceFontSize: '2.25rem',
      errorColor: '#b91c1c',
    },
    // escape hatch: set ANY --netiva-* property without a named key (here the
    // interval toggle / muted surface, which has no dedicated token)
    cssVars: { '--netiva-color-muted-bg': '#eef2ff' },
  }}
>

Named tokens (all optional) include the global palette/scale (colorPrimary, colorText, colorBorder, borderRadius, fontFamily, fontSizeBase, spacingUnit, …) plus per-element groups: button* (Background, Color, HoverBackground, Radius, Border, Padding, FontSize, FontWeight), input* (Background, Border, Radius, Color, FocusRing), planCard* (Background, Border, Radius, Padding, Shadow, FeaturedBorderColor), price*, feature*, error*, label*. Anything else: use cssVars. Each maps 1:1 to a --netiva-* custom property scoped to the provider; every component also takes a className.

The Stripe <CardElement> lives in a Stripe iframe and can't read CSS, so the kit bridges your font/text/placeholder/danger tokens into Stripe's own style option automatically — the card field matches the rest of your form.

The theme is explicit (it ignores OS prefers-color-scheme). For your own dark mode pass appearance={{ theme: isDark ? 'dark' : 'light' }}.

Components

  • NetivaProviderconfig, appearance?, clientToken?. Mount once.
  • PlanSelectorplans? (controlled; omit to auto-fetch), onSelectPlan(price, plan), defaultInterval?, loadingSlot?/errorSlot?/emptySlot?, className?.
  • NetivaCheckoutclientToken (required), priceId, onCheckoutComplete?, onError?, onTokenExpired?, className?.
  • PlanCard — the individual card PlanSelector renders; usable standalone.
  • CurrentPlantitle?, children? (action row, e.g. a <ManageBillingButton>), loadingSlot?/emptySlot?, className?. Reads the session's live subscription; renders nothing until a customer is present.
  • ManageBillingButtongetPortalUrl (required), target? ('_self' default | '_blank'), variant?, onError?, children? (label), className?.

The kit also re-exports the most useful billing hooks/types from @netiva-ai/billing so you can import everything from one place: usePlans, useCurrentSubscription, useCustomerAuth, useSubscribe, formatPrice, and the Plan / PlanPrice / ActiveSubscription types.

Compatibility

@netiva-ai/elements depends on @netiva-ai/billing (the shared primitives for config, customer auth, plans, and Stripe). Both packages keep it external so there is exactly one copy of the React contexts in your bundle.

If you also use @netiva-ai/site-kit (the full-site kit for AI-generated Next.js apps), install a compatible version of @netiva-ai/billing — both elements and site-kit will then share the same provider instances.

Environment variables (Next.js)

| Variable | Where it's used | Notes | |--------------------------------|--------------------------|-------| | NETIVA_API_KEY | Server only | nsk_ key | | NETIVA_CUSTOMER_ID | Server only | The demo customer | | NETIVA_WORKSPACE_ID | Server + client | Required | | NETIVA_API_URL | Server + client | Defaults to production | | NEXT_PUBLIC_NETIVA_* | Client | Safe values exposed to browser |

License

MIT