Rebuilding this site with Claude as my copilot

Templates are starting points. If you're going to run a blog on someone else's code, you have to plan for the day you'll need to take it over.
This blog runs on a Next.js template which I started using in 2024. It worked perfectly well. I could write posts and click publish.
Then, like most templates, it started accumulating debt; the kind that doesn't show up until GitHub starts sending you security alerts and Dependabot opens dozens of pull requests you don't have time to read.
I sat down tonight to fix one or two of them. Several hours later, with Claude as my guide and copilot, I had replaced and upgraded most of the template's internals.
Here's a summary of what we achieved together, and why some of it might be useful to anyone else maintaining a tech stack they didn't create.
A template is a starting point
A starter template is a generous gift from someone who built something useful and gave it away. But the template's authors aren't your co-maintainers. They wrote the code that worked at the moment they shipped it.
Over time, the framework moves on. React moves on. Security advisories appear and don't get backported. The fixes aren't hard to implement for someone who knows what they're doing... but that someone wasn't me.
The template I've been using is Next.js Blog with Draft Mode. First released in April 2020, it's a static blog with Preview Mode, built to run using Next.js and Contentful. As the underlying tech stack continued to evolve and grow, the template was overdue for an update.
After tonight, the template uses Next.js 16 with App Router, async dynamic APIs, the current version of React, plus groovy optimizations like an RSS feed and a sitemap.
How the collaboration actually worked
I want to be precise about this, because "I used AI" can mean a lot of different things. I'm not a developer. I can read code well enough to know what it does, and I can edit a file when someone tells me which lines to change.
My approach was to set up a dedicated "project" within Claude specifically about updating this blog; that meant it could remember our chats and carry over contextual and historical information.
Even better, a GitHub integration meant that Claude could sync and audit the code in the repo directly. This is a step "lighter" than using Claude Code, and deliberately so. I wanted to be the conduit and observe the changes.
The fun part? Claude would group the suggested upgrades by severity and ease of implementation.
So the work split looked like this: I made the editorial decisions and committed changes to the repo; Claude diagnosed problems, wrote the code, predicted what would break, and walked me through the failures when its predictions were wrong (which happened a few times).
When I pasted error logs, Claude told me which file to edit and what to change. If an update led to a failed deployment, it would check the current state of the file rather than guessing.
That's the difference between a useful collaborator and an overconfident blowhard (artificial or otherwise) who's wasting your time.
The security audit was easier than the alert count suggested
GitHub showed 36 vulnerabilities. Two critical. Nineteen high. That looks bad until you sort them by where they actually live.
About twenty of those came from a single dependency that shouldn't have been in production at all — a CLI tool the template had listed as a runtime dependency for local development convenience. None of it ships to production. Removing it cleared most of the alerts in one commit.
Another seven were transitive build tooling that runs at build time, not at request time. They matter for supply chain hygiene but not for "someone can hack my blog." A clean reinstall with patched versions took care of those.
The remaining handful were real: an outdated framework version missing a year of security patches. Those are the bits that actually mattered and quickly resolved.
The data layer had the most headroom
Here's a funny one. The template was fetching posts from Contentful via two sequential GraphQL calls on every page load. No caching. Both calls returned the full post body even when the consuming component only needed a title and excerpt. None of this was broken — pages rendered, posts appeared — but every visit pulled more data than the page actually used
Three small changes (parallel fetches, a one-hour cache with tag-based revalidation, separate field sets for full posts versus preview cards) and the data layer started behaving like something I'd chosen deliberately rather than something I'd inherited. The site got faster. Contentful's API was being called upon much less often.
The thing is that Claude flagged this one without me asking. I had complained about Speed Insights scores, expecting to be pointed at images with large file sizes. Claude pointed at the data layer instead, and that turned out to be fundamentally more important.
Images with compression and sensible sizing
So, yes. Images. The original template requested oversized cover images on every device — phones, tablets, and desktops alike — because each <Image> lacked a sizes attribute telling the browser how the image would actually be displayed.
Without that hint, browsers fall back to assuming the image is rendered at full viewport width and pick the largest version available.
The fix was a combination of asking Contentful's image API for WebP instead of JPEG, adding a sizes attribute to each cover image so the browser knows the actual display context, and capping the largest available size in the template's configuration so even high-DPI tablets don't request images they can't perceive the difference of.
The net result is that readers get faster page loads while gobbling up less bandwidth. Nice.
Inline images in posts also didn't work (now they do)
Hark! An inline image! A thing like that.
This was the one I was most happy about. The template's hero image rendered fine. But if I tried to embed an image inside a post — a screenshot, a figure, a diagram — the renderer silently failed.
The Contentful side returned the image correctly; the React component that was supposed to render it used a property that had been deprecated some time ago. The result was empty space where my images should have been.
And now? Twenty lines of new code in the rich-text renderer and now I can embed images inside posts properly, while also using Contentful's asset description field as an automatic caption.
If only I'd investigated this sooner. For the longest time I thought it was a feature that simply hadn't been implemented, I didn't know it was broken to begin with. You don't discover gaps like that until you ask the question.
To Claude's credit, this one came up because I described what I was missing as a writer, not as a technical complaint — and it diagnosed the cause as a renderer bug rather than a limitation of the content model. That distinction would have taken me a long time to figure out alone.
What "done" looks like
By the end of the evening I had:
updated framework
current React
patched dependencies
self-hosted draft mode
a properly-cached data layer
responsive image pipeline
per-post SEO metadata with OpenGraph and Twitter Card support
sitemap, robots, RSS feed
structured data for search
branded favicon and apple-icon
single canonical domain
working inline images in posts
opening external links in a new tab, as per convention
and a Dependabot config that should keep the repo updated
The repo history tells the story honestly through commits. That's a different kind of artefact than a clean repo would be. It's the receipt for the work, which is the more interesting object.
What I learned
Templates are starting points. The mistake isn't using them; the mistake is treating them as finished products. If you're going to run a blog on someone else's code, plan for the day you'll need to take responsibility for it yourself.
The good news is that taking over isn't as difficult as the security alert count makes it look. And it's significantly less of a challenge with a capable AI in the loop — not because it hands you a finished site, but because it shortens the gap between 'this is broken' and 'here's how to fix it.'
I still made every decision. I still owned every commit. But the parts that would have taken me hours of documentation-hunting and trial-and-error took minutes of conversation.
That's a meaningful shift in what one person can maintain. And an indicator to me that personal sites will grow more ambitious in scope, not less, as AI tools get better. The limit used to be how much I had time to learn. Now it's whether I can tell a good fix from a bad one. Thank you for reading.

