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
Maintainers
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
navigationplugin 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-graphConfiguration
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 buildA 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
routeMaptoo, as long as they expose a slug-like field — or use aprefixwith an emptyslugFieldvalue convention (slugFieldpointing to any string attribute).
