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

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
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.
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.
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.
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.
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.tsNavigation 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 ae7bf7aStaging 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.tsTruthful 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.tsValidation 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 7289cebVideo 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.tsInternal 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 840bca9Pages 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.tsxOversized 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.
