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 was the unforced error
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 visitor to the blog paid for the inefficiency.
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. The images were indeed a problem, but the data layer was a bigger problem and a fixable one.
The right collaborator doesn't just answer the question you asked; they tell you the question you should be asking.
Images were the visible win
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 user-visible result is the page loading faster. The visitor-invisible result is bandwidth costs being a fraction of what they were. Both matter.
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 hard as the security alert count makes it look. And it's significantly less hard with a capable AI in the loop — not because it does the work for you, but because it shortens the loop between "this is broken" and "here is what to change."
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 maintenance ceiling is no longer "what you have time to learn," it's "what you have judgement to direct."
The repo is yours. The decisions are yours. And the work, however much it accumulates while you weren't watching, is finite. Thank you for reading.
Prompt for key visual
Midjourney: "Editorial illustration, low-angle view of a plumber crouched forward on the kitchen floor, his back to the camera, reaching into the dark cabinet beneath a kitchen sink. The worn leather soles of his work boots are visible in the foreground as he crouches, denim overalls and a rolled-sleeve plaid shirt stretched across his back. His head and shoulders disappear into the shadowed cabinet, face unseen. A small puddle of water has spread from beneath the cabinet onto the patterned linoleum floor near his boots, catching a glint of light. A heavy pipe wrench and open toolbox rest on the floor beside him, with smaller fittings scattered nearby. Modest mid-century American kitchen, retro cabinetry, lived-in domestic atmosphere. Warm directional light from the right, deep painterly shadows, gouache illustration with visible brushwork, muted earth-tone palette, mid-century American editorial style. --ar 16:9 --s 350 --no deformed hands, extra fingers, text, photorealism"

