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

@convex-internal/web-analytics

v2.0.1

Published

Shared consent state, consent banner, and analytics setup for Convex websites.

Downloads

1,165

Readme

@convex-internal/web-analytics

Shared consent state, consent banner, and analytics setup for Convex websites.

Core

Mount a framework entry at the root (React or Svelte). Analytics start in production, except on localhost. Consent is synced to PostHog. The banner shows when the visitor has not chosen yet.

PostHog already copies standard campaign params (UTMs, gclid, rdt_cid, fbclid, and other common click IDs) from the landing URL onto events. This package also captures dub_id. After consent, PostHog creates an anonymous person profile. Campaign params still available at that point can be stored as $initial_*, supporting person-level attribution before login.

capture

Record an event from client code through the framework-neutral root entry. The optional third argument is PostHog's capture options.

import { capture } from "@convex-internal/web-analytics";

capture("www-login", undefined, {
  transport: "sendBeacon",
  send_instantly: true,
});

Consent banner

The banner shows when consent is "undecided". It records cookie_consent_shown when it appears and cookie_consent_decided with { decision: "accepted" | "declined" } just before the choice is saved.

The banner stays hidden while Global Privacy Control is on, because that resolves consent to "declined" rather than "undecided". GPC does not write the cookie; see Cookie behavior.

Theme with --wa-banner-* variables on .wa-banner:

.wa-banner {
  --wa-banner-background-color: var(--color-seashell);
  --wa-banner-font-family: var(--font-primary);
  --wa-banner-accept-background-color: var(--color-plum-p4);
}
  • Background, text, and border: --wa-banner-background-color, --wa-banner-text-color, --wa-banner-border-color, --wa-banner-border-radius, --wa-banner-box-shadow, --wa-banner-backdrop-filter
  • Type and layout: --wa-banner-font-family, --wa-banner-font-size, --wa-banner-padding, --wa-banner-max-width, --wa-banner-action-gap, --wa-banner-z-index
  • Link and focus: --wa-banner-link-color, --wa-banner-focus-color
  • Buttons: --wa-banner-button-border-radius, --wa-banner-button-padding, --wa-banner-button-min-height, --wa-banner-button-font-size, --wa-banner-button-font-weight
  • Accept button: --wa-banner-accept-background-color, --wa-banner-accept-text-color
  • Decline button: --wa-banner-decline-background-color, --wa-banner-decline-text-color

Buttons default to 80% opacity and go to 100% on hover or keyboard focus.

Cookie behavior

Once the cookie has been read, consent is resolved in this order:

  1. Global Privacy Control. If navigator.globalPrivacyControl === true, consent is "declined". GPC is not stored: the cookie is not written or changed. Turn GPC off and the stored cookie (if set) applies again.
  2. Cookie. true is "consented", false is "declined", and a missing or unrecognized value is "undecided".

The cookie is stored with path=/ and a max-age of one year. On convex.dev and *.convex.dev it is also set with domain=.convex.dev so the website, Docs, Stack, and other subdomains share one choice. The cookie is marked Secure everywhere except localhost.

Changes made through setConsent update immediately. Changes made elsewhere, such as in another tab or DevTools, are synchronized through cookie change events when available, and when the window regains focus or its visibility changes as a fallback.

React

Import from @convex-internal/web-analytics/react. WebAnalyticsProvider is a client component. useConsent throws if called outside that provider.

import { WebAnalyticsProvider } from "@convex-internal/web-analytics/react";

<WebAnalyticsProvider>{children}</WebAnalyticsProvider>;

Consented

Renders children only when consent is "consented". It renders nothing during SSR, while consent is still "unknown" or "undecided", if the visitor declined, or while Global Privacy Control is on.

import { Consented } from "@convex-internal/web-analytics/react";

<Consented>
  <YourAnalyticsScripts />
</Consented>;

useConsent

  • consent (ConsentStatus)
    • "unknown" until cookie has been read (SSR and first client render)
    • "undecided" after that if there is no recognized choice yet
    • "consented" if the cookie is true and GPC is not set
    • "declined" if the cookie is false or GPC is set
  • setConsent("consented" | "declined")
    • Saves the choice to the allowsCookies cookie and updates context
    • Do not write the cookie yourself

Use the hook when you need consent or setConsent directly. Enable tracking only when consent === "consented".

Svelte

The Svelte entry exports only WebAnalytics. Render it once in the root layout. It has no props and no snippet: it starts PostHog and shows the banner. There is no Consented wrapper or consent store. Those can be added later; nothing using the Svelte entry has needed them yet.

<script>
  import { WebAnalytics } from "@convex-internal/web-analytics/svelte";
</script>

<WebAnalytics />

Test in-development changes

  1. In the web-analytics repo, build and pack the package:
cd /path/to/web-analytics
npm run build
npm pack

This creates a tarball such as convex-internal-web-analytics-1.0.0.tgz.

  1. Install that tarball in the consuming app:
cd /path/to/consuming-app
npm install /path/to/web-analytics/convex-internal-web-analytics-1.0.0.tgz --force

--force ensures npm refreshes the package when the filename and version have not changed.

Publish a version to npm

  1. Confirm that you are signed in to the npm account with access to the @convex-internal organization:
npm whoami

Run npm login first if needed.

  1. Commit any README or code changes, then bump the version. npm version requires a clean Git worktree and creates a version commit and tag.
npm version major   # breaking, or 0.x → 1.0.0
npm version minor   # new backwards-compatible functionality
npm version patch   # bug fixes
  1. Run tests, inspect the package contents, then publish. .npmrc sets ignore-scripts=true, so prepack will not run — build first:
npm test
npm run build
npm pack --dry-run
npm publish

Consume a published version

Convex repos typically set min-release-age=7 in .npmrc, so a version published in the last 7 days will fail to install. Override the window for that one install:

npm install @convex-internal/web-analytics@<version> --min-release-age=0

Use the local tarball workflow whenever you need to test changes that have not been published yet.