All skills
harlan-zw avatar

/nuxt-link-checker

Find and fix broken or SEO unfriendly links in a Nuxt app with the nuxt-link-checker module. Use when a task mentions broken links, 404 links, link checking on build, failOnError, excludeLinks, excludePages, skipInspections, link checker reports, live inspections, the linkChecker config key, or the link-checker/valid-route ESLint rule. Gives the build scan scope, verified config, and the traps that make the checker pass silently or flag valid links.

GitHub Updated yesterday

Use this Skill: https://skilld.dev/gh/harlan-zw/nuxt-link-checker/nuxt-link-checker

Raw

nuxt-link-checker

Tested against the nuxt-link-checker release after 5.3.0 on Nuxt 4.5 (peer range Nuxt 3.9 to 5). The module inspects every <a href> in rendered HTML with 15 rules, at build time and in dev. It also ships two ESLint rules. Docs: https://nuxtseo.com/docs/link-checker

Setup

Config key: linkChecker. The module installs nuxt-site-config for you. @nuxtjs/seo already includes this module. Do not add it twice.

Set site.url to the production origin. The checker resolves internal links against it (see Traps). The trailing-slash rule reads site.trailingSlash, not a linkChecker option.

Automatic behaviour

  • Build scan. After prerendering, the module inspects the links on each prerendered HTML page. It prints a tree per page, then a summary.
  • Only prerendered pages are scanned. A plain nuxt build with no prerendered routes runs no inspections and logs Nuxt Link Checker scanned no pages. Use nuxt generate, or add routes to nitro.prerender.routes.
  • The whole page is scanned, including <Teleport to="body"> links. An <a> without href gets a no-missing-href warning unless it has role="button".
  • Default exclusions. excludeLinks starts with /^\/_/ and /^\/llms(-[\w-]+)?\.txt$/. Your entries are added to these; they do not replace them. With a non root app.baseURL, the base URL is also excluded.
  • External links are not fetched. fetchRemoteUrls is false, so every absolute external link counts as a 200.
  • Links to files in public/ pass without a request.
  • Dev. With Nuxt DevTools on, the module adds a Link Checker DevTools tab and /__link-checker__/* server routes. On page squiggles need showLiveInspections: true.
  • ESLint. With @nuxt/eslint, the module registers link-checker/valid-route (error) and link-checker/valid-sitemap-link (warn) for .vue, .ts, and .md files.

Fail CI on broken links

export default defineNuxtConfig({
  modules: ['nuxt-link-checker'],
  site: { url: 'https://example.com' },
  nitro: { prerender: { routes: ['/'], crawlLinks: true } },
  linkChecker: {
    failOnError: true,
    report: { json: true, markdown: true },
  },
})

Only errors fail the build. Errors come from no-error-response, missing-hash, and no-javascript. Every other rule is a warning. The count is the number of links with an error, not the number of errors.

With crawlLinks: true, Nitro crawls the broken link too and fails the build on its 404 before failOnError matters. To keep the build green and still get the report, set nitro.prerender.failOnError: false.

Reports

report.html, report.markdown, and report.json write link-checker-report.<ext> to .output/. When any report is on, the console shows only the summary. Read the report file for the link list.

  • report.storage: a path relative to the root, or unstorage options.
  • report.publish: true writes to .output/public/__link-checker__/. It adds X-Robots-Tag: noindex and a robots.txt disallow. The report is still public.

The JSON report is an array of { route, reports }. Each report has link, textContent, error[], warning[], and fix. fix is the suggested link, such as /about for a typo /abot.

Exclude links and pages

export default defineNuxtConfig({
  linkChecker: {
    excludeLinks: ['/admin/**', '/api/*', /\.(pdf|zip)$/],
    excludePages: ['/embed/**'],
    skipInspections: ['link-text', 'no-underscores'],
  },
})
  • excludeLinks matches the link. excludePages matches the page that contains the link and skips all its links.
  • String patterns are radix3 routes, not globs. * is one whole segment and ** is any depth. /_nuxt/file_*.pdf never matches. Use a RegExp for part of a segment.
  • skipInspections takes rule ids. The ids are: absolute-site-urls, link-text, missing-hash, no-baseless, no-double-slashes, no-duplicate-query-params, no-error-response, no-javascript, no-missing-href, no-non-ascii-chars, no-underscores, no-uppercase-chars, no-whitespace, redirects, trailing-slash. An unknown id does nothing and gives no warning.

ESLint without @nuxt/eslint

// eslint.config.mjs
import linkCheckerPlugin from 'nuxt-link-checker/eslint'
import vueParser from 'vue-eslint-parser'

export default [
  {
    files: ['**/*.vue', '**/*.ts'],
    languageOptions: { parser: vueParser },
    plugins: { 'link-checker': linkCheckerPlugin },
    rules: {
      'link-checker/valid-route': 'error',
      'link-checker/valid-sitemap-link': 'warn',
    },
  },
  // Markdown: same plugin and rules, plus processor: 'link-checker/markdown'
]

The rules read .nuxt/link-checker/routes.json. Run nuxt prepare or nuxt dev before eslint in CI. valid-sitemap-link checks nothing until nuxt dev merges the @nuxtjs/sitemap URLs into that file. Pass { rootDir } or { routesFile } as the rule option when ESLint runs from another directory. Only literal links that start with / are checked. Skip one link with rel="nofollow" or an eslint-disable-next-line comment.

Traps

  • No routes.json, no ESLint errors. Without nuxt prepare, both ESLint rules pass every link. ESLint prints one [nuxt-link-checker] ... not found warning, and still exits 0.
  • Links to pages that are not prerendered are checked against the live site. The module sends a HEAD request to site.url + path. A new SSR only page that is not deployed yet reports a 404. A page that exists only in production passes. Add such pages to excludeLinks, or prerender them.
  • Read the failure message, not only the rule id. An HTTP error reports the status the server sent. A server that rejects HEAD with 405 or 501 gets a GET retry. A DNS error or a refused connection reports Could not reach the link (ENOTFOUND) with the cause code. A timeout reports 408.
  • Rules chain on the fix. When one rule proposes a fix, later rules test the fixed link. /about/ that 404s reports only the 404, not the trailing slash.
  • Different results per mode. Build scans read prerender status codes. Dev scans fetch the dev server. ESLint reads route patterns only and ignores excludeLinks.

Version limits

These v1 options are gone. The module logs a warning that names the replacement, then ignores them:

  • exclude: use excludeLinks.
  • failOn404: use failOnError.
  • siteUrl and trailingSlash: set site.url and site.trailingSlash.

The ESLint rules and nuxt-link-checker/eslint exist from v5.0.0.

Config

  • enabled (true), runOnBuild (true), failOnError (false), debug (false).
  • fetchTimeout (10000 ms), fetchRemoteUrls (false). With fetchRemoteUrls: true the module checks that you are online first and turns itself off if not.
  • Full reference: https://nuxtseo.com/docs/link-checker/api/config

Debug

  • /__link-checker__/debug.json in dev shows the resolved config, including the serialized exclude patterns.
  • debug: true writes debug-link-responses.json next to the reports, with the status the module used for each link.

Source: SKILL.md on GitHub