Case study

End-to-End Technical SEO for a Next.js Company Website

How we audited, rebuilt and now monitor the technical SEO of 99visual.com — crawlability, canonicals, sitemaps, redirects, structured data and page speed — with every change traceable in version control.

Client
99 Visual Solutions (our own website)
Period
– · ongoing
Digital marketing and SEO services by 99 Visual Solutions

The starting point

Our own website moved to a Next.js (App Router) build in 2026, replacing an older site whose URLs were still in Google’s index. A rebuild like this is where search visibility is most often lost: old URLs return 404s, new pages ship without canonicals, and JavaScript-driven navigation hides links from crawlers.

We treated the site as we would a client engagement — audit first, fix in small traceable changes, check each fix against what Google actually receives, and keep monitoring in Google Search Console afterwards. This page documents that work. Because the client is us, every change below can be inspected in the site’s source history.

How we worked

  1. Audit

    Crawl the live site and the source together: what each URL returns, its robots directives and canonical, what the server-rendered HTML contains before JavaScript runs, and what Search Console reports for coverage and enhancements.

  2. Prioritise

    Rank issues by what blocks crawling or indexing first (404s, wrong canonicals, hidden links), then by what weakens how pages are understood (structured data, titles, internal links), then by speed.

  3. Implement

    Fix at the source of truth rather than page by page — one robots generator, one sitemap generator, one date registry, one set of schema helpers — so the same mistake can’t reappear on the next page.

  4. Validate

    Re-fetch the live URLs and check the delivered HTML and headers, validate structured data, then use Search Console URL Inspection and sitemap reports to confirm Google sees the change.

  5. Monitor

    Watch Search Console coverage and enhancement reports, and fix new issues as they surface. Monitoring is ongoing; several of the fixes below came directly from Search Console reports.

What we delivered

Each item lists where it can be verified.

Crawlability & indexability

Making sure Google can reach every page that should rank, and nothing that shouldn’t.

  • robots.txt generated from code

    Private areas (/api, /admin, /login) are disallowed for every crawler group, not just the default one. Rules that blocked tracking-parameter URLs were removed so Google can see their canonical tags, and /_next/ was unblocked so Google can load the CSS, JavaScript and images it needs to render pages.

    Evidence: app/robots.ts

  • Navigation links visible to crawlers

    The header’s Services menu only existed in the HTML while a mouse hovered over it, so the server-rendered page had no main-menu links to any service page. The panel is now always rendered and hidden with CSS while closed, with descriptive anchor text.

    Evidence: commit ae7bf7a

  • Staging copies kept out of the index

    Any request that isn’t for www.99visual.com — preview deployments, the hosting alias — is served an X-Robots-Tag: noindex header, so duplicate copies of the site can’t compete with the real one.

    Evidence: next.config.ts (headers)

  • Thin pages held back until they’re ready

    Insights topic pages are noindex,follow and absent from the sitemap until a topic has at least two published articles; empty topics return 404.

    Evidence: commit 4de8d1d

URLs, canonicals & redirects

One clean URL per page, and every legacy URL sent somewhere useful.

  • 15 permanent redirects for legacy URLs

    Old service, partner, blog, category and date-archive URLs from the previous site now 301 to their closest current page. Two of them were found because Search Console listed the old URL as returning 404 or not indexed.

    Evidence: next.config.ts (redirects)

  • Self-referencing canonicals everywhere

    Every page declares an absolute canonical on the production host. On articles, a stored canonical field could keep a renamed article’s old slug, so the page pointed its canonical at a 404 and the sitemap silently dropped it; articles now always canonicalise to their own URL.

    Evidence: commit 1bc8e89

XML sitemap

A sitemap that lists exactly the indexable pages, with honest dates.

  • Generated and refreshed automatically

    The sitemap is regenerated at most hourly and on every article save, so new articles appear without a redeploy. It covers core pages, service pages, Insights articles and topic pages, and video watch pages with video metadata.

    Evidence: app/sitemap.ts, lib/revalidate-insights.ts

  • Truthful lastmod dates

    Most pages previously claimed a last-modified date earlier than the site’s own code. Dates now come from one registry that the sitemap and each page’s structured data both read, so they can’t disagree, and article dates no longer change when a page is merely viewed.

    Evidence: lib/page-dates.ts, commit 7289ceb

Structured data

Schema that describes the business and each page accurately — and validates.

  • One connected schema graph per page

    Organization, LocalBusiness, WebSite, WebPage, BreadcrumbList, Service, FAQPage, Article/BlogPosting, CollectionPage, ItemList and VideoObject nodes, linked by stable @id references instead of duplicated objects.

    Evidence: lib/schema.ts

  • Validation fixes

    Invalid JSON-LD date formats are normalised or dropped (never defaulted to “now”), duplicate breadcrumb nodes were removed, and an FAQ graph that was built but never rendered on the Help Center is now output.

    Evidence: lib/schema-date.ts, commit 7289ceb

  • Video watch pages

    Search Console reported that videos weren’t on a watch page. Each clip now has its own page with VideoObject data and a video sitemap entry, with durations and thumbnails taken from the files themselves.

    Evidence: lib/videos.ts, commit 039484a

On-page SEO & internal linking

Titles, descriptions and links that tell Google what each page is for.

  • Titles and meta descriptions

    A sitewide title template with automatic removal of doubled brand suffixes, and meta descriptions trimmed at a sentence or word boundary to fit search results while the page keeps the full text.

    Evidence: lib/seo.ts

  • Internal links between related pages

    Footer links to every service page and to previously orphaned pages; articles link to their topic page and the related service; every article now links to other articles (previously three of five linked to none).

    Evidence: app/components/footer.tsx, commit 1bc8e89

Page speed

Changes aimed at Core Web Vitals, described as changes — not as measured scores.

  • Render-blocking font requests removed

    About 27 separate Google Fonts @import rules scattered through page styles were replaced with one preconnect and one stylesheet in the document head.

    Evidence: commit 840bca9

  • Pages no longer start invisible

    The page-transition wrapper shipped every page with opacity 0 until JavaScript finished, delaying the largest contentful paint. The fade now runs only on in-site navigation.

    Evidence: app/components/PageTransition.tsx

  • Oversized assets

    A 4904px, 11.3 MB photo shown at about 500px wide was resized to 1600px and 115 KB, and a stylesheet reference to a missing image (a 404 on every visit) was removed.

    Evidence: commit 96b81e4

What the evidence supports

  • Every page in the sitemap is built to declare index,follow and a self-referencing canonical on the production host, and robots.txt allows Google to crawl all of them — confirmed in the source and spot-checked on the live site.
  • robots.txt and the sitemap are generated from the same code that renders the pages, so they stay in step as the site changes.
  • Every change listed above is a reviewable commit in the site’s source history, with the reason written alongside it.

What we don’t claim

  • Ranking, traffic or lead changes: we haven’t published before-and-after data, so we make no claim about them here.
  • Indexing results: several service pages were still awaiting indexing when this was written. Getting pages crawlable and correctly signalled is within our control; when Google indexes them is not.
  • Core Web Vitals scores: the speed changes are implemented, but we haven’t published field data measuring their effect.

Stack & tools

  • Next.js App Router
  • TypeScript
  • Prisma / PostgreSQL
  • Vercel
  • Google Search Console
  • Schema.org JSON-LD

Want the same audit for your site?

See the full service, or tell us what you’re working with.