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

@post-embed/elements

v0.6.2

Published

Snapshot post elements for the web

Readme

@post-embed/elements

Web components for rendering saved social posts.

import '@post-embed/elements/x/theme.css'

import { registerXPost } from '@post-embed/elements/x'
import type { XPost } from '@post-embed/types'

export function showPost(container: HTMLElement, post: XPost) {
  registerXPost()
  const element = document.createElement('post-embed-x-post')
  element.data = post
  container.append(element)
  return element
}

XPost is a small snapshot with only what the card renders: the author, the body split into text and link segments, media, an optional quote and reply target, the edit state. Assign a new XPost object to element.data to update the post, or null to clear it. The theme import is optional.

@post-embed/exporter produces XPost values: fromSyndication from the syndication API data X's own embeds use, observeXTweets from the GraphQL responses of the x.com page.

Loading a snapshot by URL

Set element.url and element.resolver to let the element load its own snapshot. The element calls resolver(url) whenever data is null and both are set, and renders whatever it returns (a snapshot, a promise of one, or undefined when nothing was found). Results from a superseded url are dropped. While a promise is pending the fallback card carries a data-pending attribute. A saved data always wins over the fetched snapshot.

import { fromSyndication } from '@post-embed/exporter/x/syndication'

const element = document.createElement('post-embed-x-post')
element.resolver = async (url) => {
  const id = new URL(url).pathname.split('/').at(-1)
  const response = await fetch(`/api/tweet/${id}`)
  if (!response.ok) return
  const result = fromSyndication(await response.json())
  if (result.issues) throw new Error('Invalid post data')
  return result.value
}
element.url = 'https://x.com/jack/status/20'

The syndication API X's own embeds read from does not allow cross-origin requests, so resolver for X posts usually goes through your server.

YouTube video

import '@post-embed/elements/youtube/theme.css'

import { registerYouTubeVideo } from '@post-embed/elements/youtube'
import type { YouTubeVideo } from '@post-embed/types'

export function showVideo(container: HTMLElement, url: string) {
  registerYouTubeVideo()
  const element = document.createElement('post-embed-youtube-video')
  element.resolver = async (url) => {
    const response = await fetch(
      `https://www.youtube.com/oembed?url=${encodeURIComponent(url)}&format=json`,
    )
    if (!response.ok) return
    return { url, ...(await response.json()) } as YouTubeVideo
  }
  element.url = url
  container.append(element)
  return element
}

The snapshot is the YouTube oEmbed response plus the video url; YouTube's oEmbed endpoint allows cross-origin requests, so resolver can call it directly, or assign a saved snapshot to element.data instead. The element renders a card with the poster, title, channel, and a "Watch on YouTube" link, and never creates an iframe unless you set playback="inline" (as an attribute or element.playback = 'inline'). In inline mode the poster becomes a play button, and clicking it swaps in the youtube-nocookie.com player. The poster loads from the saved thumbnail_url; point it at your own copy if the page must not contact Google's image CDN.

See the playground for examples.