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

@konvert7/promoot

v0.7.0

Published

Render a Promoot sponsor slot inline as a React server component, with views counted by the visitor's own browser.

Readme

@konvert7/promoot

Render a Promoot sponsor slot inline, as part of your own markup, instead of in an iframe.

bun add @konvert7/promoot
import { PromootBlock } from "@konvert7/promoot";

<PromootBlock url="https://promootlabs.com/embed/YOUR_SLOT_ID" />;

The url is the embed URL from your Promoot dashboard — the same string the iframe snippet uses, so one value serves either method.

What it renders

Whatever the sponsor bought, without you choosing:

  • An image ad — their artwork, cropped to fill the slot.
  • A text ad — their favicon, a headline, a description and their domain, centred. Headline and description are both optional; a link on its own renders the mark and the bare domain. If no favicon could be captured, a monogram of the domain's first letter is shown instead — never both, so a transparent icon can't show the letter through it.

And when nobody has bought it yet, the slot sells itself. If the owner composed an empty state in their dashboard, it renders as a billboard: the lines they chose — their own words, the price, the view count, their domain — over a faint chart of the last 30 days of traffic, with a preview of the visitor's own ad fading in on hover. Owners who never composed one get the plain pitch instead: headline, audience, proof and terms.

The empty state needs no configuration here. It arrives resolved from the server, so the words are already the words, and this block only sets them.

Why inline

An iframe fails closed. If a content filter ever blocks the frame, your sponsor's ad disappears and they get nothing. An inline block renders inside your own document, so the worst a filter can do is drop the creative or the view beacon: the ad is still seen.

It also participates in your layout. No fixed-height box to defend, no frame to style around.

What it costs you

The creative lives in your DOM, so this is a weaker guarantee for sponsors than the iframe: nothing stops a site owner swapping or hiding it after approval. If you want that guarantee, keep the iframe. Both count views identically.

Props

| Prop | Type | Default | | |---|---|---|---| | url | string | — | Required. The dashboard embed URL. | | className | string | — | Applied to the container. | | style | CSSProperties | — | Applied to the container. | | fallback | ReactNode | null | Rendered when the slot is paused, empty, or unreachable. |

Requirements

A React Server Components framework — Next.js App Router or equivalent. The block fetches on the server and ships no client JavaScript; the view is counted by a plain <img> the visitor's browser loads, which is what keeps the count a first-hand observation rather than something your server claims.

The page must render dynamically. The block fetches with cache: "no-store", but if your route is statically generated the fetch runs once at build time and an expired ad keeps rendering — the sponsor gets free time and the next buyer never appears. In Next.js:

export const dynamic = "force-dynamic";
// or a short revalidate window
export const revalidate = 30;

Counting is unaffected by caching, because the beacon is a tag in your HTML that every visitor's browser fetches regardless of how that HTML was produced.

Styling

className and style land on the container, and the internal structure carries data-promoot-* attributes plus promoot-* class names you can target.

It inherits your page rather than painting over it. Unless the owner picked a typeface in their dashboard, the block sets font: inherit and renders in whatever face your page already uses, webfont included — something the iframe version can never do, because nothing crosses an iframe boundary. Corner radius and outline width come from the owner's settings too, and each falls back to the shipped value if your Promoot server is older than those controls.

The block never declares a colour scheme of its own. Its palette uses light-dark(), which resolves against the scheme it inherits from your page — so a light-only site keeps a light panel even for a visitor whose OS prefers dark, and a dark site gets a dark one. All of its CSS is scoped to the block; nothing is defined on :root. The only name it declares globally is the keyframes the empty state breathes with on touch devices, and that name carries the slot's own id, so two blocks on one page never collide.

The one thing you cannot restyle is the "Sponsored" label, which renders from inline styles. Disclosure of a paid placement is not the site owner's to remove.

License

MIT