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

@mediansh/widget

v0.11.0

Published

The Median widgets: a support panel an AI agent answers first, the same panel as a modal, a contact form that starts the conversation, and a feedback panel a model files for you. One component each, or one mount call.

Readme

@mediansh/widget

The Median support widget. A launcher in the corner of your site opens into a support panel. An AI agent answers first, your team picks up in the Median inbox, and replies stream back to the visitor. There is no connection or message list to manage.

The same panel as a modal, a contact form, and a feedback panel ship alongside it. Same key, same inbox.

Install

npm install @mediansh/widget

Import the stylesheet once, wherever your global CSS lives:

import "@mediansh/widget/styles.css";

React

import { MedianSupport } from "@mediansh/widget";

<MedianSupport apiKey="median_pk_..." />;

Mount it once, anywhere in your tree. The median_pk_ id is derived from your server's MEDIAN_KEY; the docs show the one line helper.

On an app with auth, serve medianIdentity from a route and pass that instead. The key comes down with the signed in user, so the conversation follows the person rather than the browser:

<MedianSupport identity="/api/median/identity" />;

The markdown renderer and the screenshot code load on demand, so do not wrap this in dynamic(). That only delays the launcher.

Without React

import { mountMedianSupport } from "@mediansh/widget";

const support = mountMedianSupport({ apiKey: "median_pk_..." });

Same widget, same options, on any page with a bundler. The instance returns update for new options and unmount to take it off the page. React installs alongside as a peer dependency; your app does not use it.

Control it from anywhere

import { medianSupport } from "@mediansh/widget";

medianSupport.open();
medianSupport.attach("Crash trace", error.stack);
medianSupport.reportError(error); // from your error screen
medianSupport.reset(); // on sign out

React components can use useMedianSupport() for the same control with a live isOpen.

Modal

The widget, opened in the middle of the screen over a scrim rather than out of the corner. The button stays; every prop and every control is the same.

import { MedianSupportModal } from "@mediansh/widget";

<MedianSupportModal apiKey="median_pk_..." />;

mountMedianSupportModal is the version for pages without React. Mount one of the two, not both.

Contact form

A form where you put it, for the page where a chat panel is the wrong shape. An email and a message are always asked; add the rest:

import { MedianContactForm } from "@mediansh/widget";

<MedianContactForm
  apiKey="median_pk_..."
  fields={[
    { name: "name", label: "Name", required: true },
    { name: "topic", type: "select", options: ["Billing", "A bug"] },
  ]}
/>;

Sending starts a conversation in your inbox, exactly as the widget does, and the reply goes to the email given. mountMedianContactForm({ target, ... }) renders it into an element of yours on a page without React.

Or keep your own form and only borrow the send:

const contact = useMedianContact({ apiKey: "median_pk_..." });

await contact.send({ email, message, fields: { Topic: topic } });

sendMedianContact(options, message) is the same call as a plain function.

Feedback

A second panel, for notes rather than questions. It wraps a trigger of your own and opens under it:

import { MedianFeedback } from "@mediansh/widget";

<MedianFeedback apiKey="median_pk_...">
  <button>Share feedback</button>
</MedianFeedback>;

One box and one button. A model reads the note and files it as a bug or a suggestion, or drops it as spam. Nobody picks a category or writes a title, and the page it was written on rides along.

mountMedianFeedback is the version for pages without React, and medianFeedback / useMedianFeedback() open it from anywhere else.

Theming

Every piece reads shadcn-style CSS variables from your page and falls back to a plain light theme without them. They inherit your font.

Docs

Props, identity, diagnostics, the modal, the contact form, feedback, and agent tools: docs.median.sh, or the source at github.com/mediansh/median.

Credits

Icons are from Solar by 480 Design, licensed under CC BY 4.0.