Technical SEO for Next.js App Router
SEO fixes that arrive as pull requests.
Metafix crawls your live site, finds technical SEO issues, maps each one to the file that causes it, and opens a pull request with the fix. You review the diff. Nothing reaches your default branch without you.
Pull requests are in private beta.
audit: live · pull requests: private beta
seo: fix 8 issues in 3 files #42
import type { Metadata } from "next";import { Plans } from "@/components/plans"; export const metadata: Metadata = {};export const metadata: Metadata = { title: "Pricing", description: "Plans for teams of any size.", alternates: { canonical: "/pricing" }, openGraph: { type: "website", url: "/pricing", title: "Pricing", description: "Plans for teams of any size.", images: ["/og/pricing.png"], },}; export default function PricingPage() { return <Plans />;}- app/layout.tsx+2 -0
- components/hero.tsx+2 -2
Flagged, not guessed (2)
- app/blog/[slug]/page.tsx — metadata comes from getPostMeta(), can't parse, left untouched
- /old-pricing — 404, no redirect target known
From crawl to verified fix
Illustrative output. Steps 2 to 5 are in private beta.
Crawl and audit
Reads your sitemap and every page it lists, respects robots.txt, and runs deterministic rules. No model decides what is broken.
87 pages · 56 issues
Map to code
Resolves each URL to the App Router file that renders it, through route groups, dynamic segments and layouts.
/pricing → app/pricing/page.tsx
Fix
A model writes only the text: descriptions, alt text, JSON-LD values. It is schema-validated, and the code change is made on the AST.
+ description: "Plans for teams of any size."
Open the pull request
A new branch and a pull request listing every change. Never your default branch.
metafix/seo-fixes-2026-10-04 → #42
Verify
After you merge and deploy, Metafix re-crawls and marks each issue resolved, then re-runs on a schedule to catch regressions.
re-crawl: 8 of 8 resolved
Six kinds of fix. One reviewable diff.
Missing or duplicated titles and descriptions get a written value in the page's metadata export.
title-missing · title-duplicate · meta-description-missing · meta-description-duplicate
export const metadata: Metadata = {};export const metadata: Metadata = { title: "Pricing", description: "Plans for teams of any size.",};When we don't fix itMetadata that comes from a CMS or a helper we can't parse is flagged in the PR description, never guessed.
Ground rules
The commitments the pull-request pipeline is built around.
- 01branch + PR only
Never pushes to your default branch.
Branch and pull request only. Always.
- 02pure functions
The audit is deterministic.
No model decides what is broken. Every rule is a pure function with fixture tests.
- 03zod
The model only writes text.
Meta descriptions, alt text and JSON-LD values, schema-validated before they can reach a PR.
- 04flagged items
Unsupported patterns are flagged, not guessed.
Metadata from a CMS or a helper we can't parse is listed in the PR description and left untouched.
- 05ts-morph
Code is edited through the AST.
No regex, no string replacement. Minimal diffs that keep your formatting.
Run your first audit.
Paste a production URL. You'll get a report in about a minute.
Want the pull requests when they open up?