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

strapi-plugin-link-graph

v0.1.0

Published

Internal linking graph for Strapi — interactive visualization, orphan detection, health score, and AI-powered link suggestions (Gemini).

Downloads

91

Readme

strapi-plugin-link-graph

Internal linking graph for Strapi v5 — an interactive map of the links between your content, orphan detection, a linking health score, and optional AI-powered link suggestions (Google Gemini).

Internal linking is one of the most underrated SEO levers: content that no other page links to ("orphans") is crawled less, ranks worse, and is invisible to visitors browsing your site. This plugin gives editors a live, visual answer to "how well is our content connected?" — right inside the admin panel.

Features

  • 🕸️ Interactive graph — a deterministic "constellation" layout (hubs in the center, clusters fanned around them, isolated content in a grid below). Drag nodes, pan, pinch/Ctrl+wheel zoom, double-click to zoom, per-type filters, search, neighborhood isolation, PNG export. No graph library — a single self-contained SVG component.
  • 🔗 Real link detection — schema-aware crawl of your entries: markdown links ([…](/path)), configurable link fields (url, to, href, …), links inside components and dynamic zones, and relations to routable types (rendered as links by most front ends) shown as dashed edges.
  • 🏝️ Orphan & dead-link lists — content with zero inbound links, and links pointing to unknown targets, each one click away from its edit view.
  • 💯 Health score — orphan rate, average inbound links, unresolved targets, all condensed into a 0–100 score.
  • 🧭 Navigation awareness — if the navigation plugin is installed, content reachable from a menu is flagged "in nav" (optional; the plugin works without it).
  • ✨ AI link suggestions (optional) — Gemini analyzes the real graph and proposes up to 15 new internal links (prioritizing orphans), with a natural anchor and a one-line reason. Suggestions are validated against the graph (existing links, self-links and unknown pages are rejected) and previewed as green ghost edges.
  • 🪄 One-click AI insertion (optional) — weaves a suggested link into the longest text field of the source entry, as a draft, respecting your brand voice. Nothing is published automatically.
  • 🎯 "View in link graph" document action in the Content Manager, deep-linking to the entry's node.

Installation

npm install strapi-plugin-link-graph
# or
yarn add strapi-plugin-link-graph
# or
pnpm add strapi-plugin-link-graph

Configuration

The plugin needs to know which content types have a public URL and how to build it. Declare them in config/plugins.ts (or .js):

export default ({ env }) => ({
  "link-graph": {
    enabled: true,
    config: {
      // REQUIRED — content types that have a public page.
      routeMap: {
        "api::article.article": {
          prefix: "/blog/",       // public path = prefix + slug
          slugField: "slug",      // attribute holding the slug
          titleField: "title",    // attribute shown as the node label
          label: "article",       // optional — type name in the legend
        },
        "api::page.page": {
          prefix: "/",
          slugField: "slug",
          titleField: "title",
        },
      },

      // OPTIONAL — attribute names treated as raw internal hrefs.
      // Default: ["url", "to", "href", "externalPath", "ctaUrl"]
      linkFields: ["url", "to", "href"],

      // OPTIONAL — locale analyzed by default. Default: "en"
      defaultLocale: "en",

      // OPTIONAL — AI defaults. Provider, model and API key can also be set
      // from the admin UI (⚙️ AI settings tab), which takes precedence.
      aiProvider: "google", // google | openai | anthropic | mistral
      aiModel: "gemini-3.6-flash",
      aiApiKey: env("GEMINI_API_KEY"),

      // OPTIONAL — injected into AI prompts.
      brandVoice: "Friendly, expert, concise. Never salesy.",
      language: "English", // language of anchors & reasons
    },
  },
});

Then rebuild the admin panel:

npm run build

A Link Graph entry appears in the admin menu.

How edges are detected

| Edge | Detection | Style | | --- | --- | --- | | Editorial link | Markdown [text](/path), to="/path" / href="/path" attributes in rich text, and any attribute listed in linkFields | solid | | Block relation | A relation from an entry to another routable type (front ends usually render these as links) | dashed | | Navigation | Items of the navigation plugin (if installed) | "in nav" flag |

Media paths (/uploads/…, files with extensions) are ignored. Inverse relation sides (mappedBy) are skipped so an edge is only counted from the owning side.

AI features

The AI features work with your choice of provider and model, configured in the ⚙️ AI settings tab of the plugin page (recommended — the key is stored server-side and never sent back to the browser), or via the config file / environment variables:

| Provider | Default model | Env fallback | | --- | --- | --- | | Google Gemini | gemini-3.6-flash | GEMINI_API_KEY | | OpenAI | gpt-5.1-mini | OPENAI_API_KEY | | Anthropic Claude | claude-haiku-4-5 | ANTHROPIC_API_KEY | | Mistral | mistral-small-latest | MISTRAL_API_KEY |

The model field is free text, so any current or future model name works. A Test connection button performs a minimal round-trip to validate the key and model. Precedence: admin settings → config file → environment variable.

  • Suggestions are generated from the actual graph inventory and strictly validated — the model cannot suggest a link that already exists, a self-link, or a page that doesn't exist.
  • Insertion updates the entry's draft only: the AI rewrites nothing but the sentence where the link lands, and the response is verified to contain the exact link before saving. You review and publish as usual.

All plugin routes are admin-only (admin::isAuthenticatedAdmin).

Requirements

  • Strapi v5 (uses the Document Service API)
  • Node 18+

Notes & limits

  • Analysis covers up to 1000 entries per content type.
  • The graph reads draft versions (what editors are working on).
  • Single types can be included in routeMap too, as long as they expose a slug-like field — or use a prefix with an empty slugField value convention (slugField pointing to any string attribute).

License

MIT