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

@pixygon/seo

v1.0.0

Published

Estate-standard SEO + AIEO for Pixygon web apps: full-page prerender (real content for crawlers & AI answer engines), the canonical Organization/sameAs identity graph, and a production-safe <PixygonSEO> head.

Readme

@pixygon/seo

The estate-standard SEO + AIEO toolkit for Pixygon web apps. One source of truth so this is never reinvented — or re-bugged — per project.

It solves the three things that were independently broken across six Pixygon SPAs:

  1. Empty-shell problem — a client-rendered SPA serves <div id="root"> with no content to crawlers and AI answer engines (GPTBot, ClaudeBot, PerplexityBot, CCBot, Googlebot's no-JS pass). The full-page prerender renders each route in headless Chromium at build time and writes real dist/<route>.html.
  2. Duplicate meta — react-helmet(-async) appends its per-route tags on top of the ones hard-coded in index.html, so every prerendered route shipped 2× og:image/canonical/title. The prerender dedupes the head (keep-last).
  3. Fragmented identity — every site emits the same Organization + sameAs graph so Google reads the estate as one entity.

New projects

Already baked into the website template (Dyson/templates/website) — a scaffolded project gets all of this for free. Nothing to do.

Adding it to an existing Vite SPA (≈10 minutes)

  1. Install:

    npm i @pixygon/seo
    npm i -D [email protected]
  2. Dockerfile — add a prerender stage between build and nginx. ⚠ It must be the Debian jammy Playwright image; Alpine silently degrades the prerender to head-only (empty body):

    FROM mcr.microsoft.com/playwright:v1.60.0-jammy AS prerender
    WORKDIR /app
    COPY --from=build /app ./
    RUN node node_modules/@pixygon/seo/prerender.mjs || true
    
    FROM nginx:alpine
    COPY --from=prerender /app/dist /usr/share/nginx/html

    Keep the image tag in lock-step with the playwright version.

  3. nginx.conf — serve the prerendered files before the SPA fallback (without $uri.html the prerender does nothing):

    try_files $uri $uri.html $uri/ /index.html;
  4. Routes — add pixygon-seo.config.json at the repo root (it also auto-discovers from dist/sitemap.xml):

    { "routes": ["/", "/pricing", "/about", "/faq"] }

    List every PUBLIC content route; leave out auth/app/checkout pages.

  5. Head tags — use <PixygonSEO> (or the helpers). Canonicals come from the production domain you pass — never window.location.origin:

    import { PixygonSEO } from '@pixygon/seo/react';
    
    <PixygonSEO
      domain="https://yoursite.pixygon.io"
      siteName="YourSite"
      title="Pricing"
      description="…"
      image="https://yoursite.pixygon.io/og-image.png"
      path="/pricing"
    />

    The Organization + sameAs identity graph is emitted automatically. Pass siteSchema={false} on inner routes if you already emit it once per page.

  6. Verify the deploy (do not skip — the Alpine trap is silent):

    curl -s -A "Googlebot/2.1" https://yoursite.pixygon.io/pricing | grep -c 'id="root"></div>'
    # 0 = real content served ✓   1 = still an empty shell ✗

API

| Export | From | Use | |---|---|---| | prerender(opts?) | @pixygon/seo/prerender.mjs | the build-time renderer (also a pixygon-prerender bin) | | PIXYGON_SAME_AS | @pixygon/seo | the canonical 6-social identity array | | pixygonOrganization() | @pixygon/seo | the Organization JSON-LD node | | pixygonSiteSchema(opts) | @pixygon/seo | full @graph: Organization + App + WebSite | | dedupeSeoHead(html) | @pixygon/seo | string-level head dedupe (for custom prerenders) | | <PixygonSEO> | @pixygon/seo/react | the per-route head (needs react-helmet-async) |

The node-safe core (.) has no React dependency, so build scripts and the prerender import it freely; the React component lives at @pixygon/seo/react.

Publishing

npm run build && npm publish --access public