Strapi SEO Link Graph logo

Strapi SEO Link Graph

By Paul Lefizelier

Link Graph turns your Strapi content into a live, interactive map — so your editors can *see* the structure of the site instead of guessing it.

Latest version

v0.1.0

released August 4, 2026

npm install strapi-plugin-link-graph

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

EdgeDetectionStyle
Editorial linkMarkdown [text](/path), to="/path" / href="/path" attributes in rich text, and any attribute listed in linkFieldssolid
Block relationA relation from an entry to another routable type (front ends usually render these as links)dashed
NavigationItems 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:

ProviderDefault modelEnv fallback
Google Geminigemini-3.6-flashGEMINI_API_KEY
OpenAIgpt-5.1-miniOPENAI_API_KEY
Anthropic Claudeclaude-haiku-4-5ANTHROPIC_API_KEY
Mistralmistral-small-latestMISTRAL_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

Submit your content

Share your work with the community and get it listed in the Strapi ecosystem for everyone to discover and use.

Submit
Submit your content