Building a personal blog from scratch can feel daunting — choosing the right framework, wiring up components, writing deployment pipelines, and keeping the whole thing maintainable. In this post I want to share exactly how I used GitHub Copilot as an AI pair programmer to accelerate every stage of building this site, and how GitHub Pages makes hosting it completely free and effortless.
Why GitHub Copilot?
I already spend most of my day inside VS Code working with infrastructure-as-code, Kubernetes manifests, and CI/CD pipelines. GitHub Copilot integrates directly into that workflow without switching context. Instead of jumping to documentation or Stack Overflow, I can describe what I need in a comment or a chat message and get working code back in seconds.
For a side project like a personal blog, that speed-up is the difference between shipping something and leaving it as a forever-draft.
Choosing the Stack
I wanted a static site — fast, secure, and cheap to host. After a quick research session (with Copilot Chat summarising the trade-offs), I settled on:
| Tool | Role |
|---|---|
| Astro | Static site generator with component islands |
| Tailwind CSS | Utility-first styling |
| Markdown | Content authoring |
| GitHub Pages | Hosting & CDN |
| GitHub Actions | Build & deploy pipeline |
Copilot Chat helped me compare Astro, Next.js, and Hugo side-by-side without leaving my editor, and suggested Astro because of its zero-JS-by-default philosophy — perfect for a content-heavy blog.
Scaffolding the Project
I started with npm create astro@latest to scaffold the project, then immediately opened Copilot Chat and asked:
“I have a fresh Astro project. I want a blog with a homepage, a blog listing page, and individual post pages. What folder structure should I use and can you generate the base layouts?”
Within a few exchanges I had:
src/layouts/BaseLayout.astro— shared<head>, header, and footersrc/layouts/BlogPost.astro— layout for individual posts with metadatasrc/components/Header.astroandsrc/components/Footer.astrosrc/pages/index.astro— homepagesrc/pages/blog/index.astro— post listingsrc/pages/blog/[...slug].astro— dynamic post route
What would have taken me the better part of an afternoon took about 30 minutes, with most of that time spent reviewing and tweaking the generated output rather than writing from scratch.
Building Components with Copilot
BlogCard Component
I needed a card to display post previews. I typed:
// BlogCard component: receives title, description, pubDate, heroImage, slug props
// Renders a card with the hero image, date, title, description, and a "Read more" link
Copilot completed the full component, including responsive image handling and accessible link text. I asked a follow-up in chat to add tag badges, and it updated the component inline.
Navigation and Active Link Highlighting
I asked Copilot Chat:
“In Astro, how do I highlight the active nav link using
Astro.url?”
It gave me the exact pattern — comparing Astro.url.pathname to each href — and autocompleted the implementation as I typed it.
Dark Mode Toggle
Tailwind’s dark: variant makes dark mode straightforward, but wiring up the toggle with localStorage persistence always involves a bit of boilerplate. A single prompt got me a working script that:
- Reads the saved preference from
localStorageon page load - Falls back to the OS preference via
prefers-color-scheme - Toggles the
darkclass on<html>and saves the new preference
Writing Content
Every blog post is a Markdown file in src/content/blog/. The Astro content collections API validates frontmatter automatically. When I defined the schema in src/content.config.ts, Copilot autocompleted the Zod schema based on the frontmatter fields I had already written in my first post.
For the posts themselves, Copilot is useful for:
- Generating outlines — I describe the topic and ask for a heading structure
- Expanding sections — I write a rough bullet list and ask Copilot to turn it into paragraphs
- Code snippets — CLI commands and config examples are autocompleted accurately
- Proofreading — Copilot Chat can suggest clearer phrasing
I still write the narrative voice myself; Copilot handles the scaffolding so I can focus on the ideas.
Setting Up GitHub Pages
GitHub Pages can serve any static site from a repository. For an Astro project the recommended approach is to let a GitHub Actions workflow build the site and push the output to the gh-pages branch (or use the new Pages artifact upload action).
The Workflow File
Copilot Chat wrote the first draft of .github/workflows/deploy.yml when I asked:
“Write a GitHub Actions workflow that builds an Astro site and deploys it to GitHub Pages using the official GitHub Pages action.”
The result used withastro/action — the official Astro action that handles Node setup, dependency installation, building, and artifact upload in a single step — plus actions/deploy-pages for the actual deployment. Here is the workflow used by this blog (see deploy.yml):
name: Deploy to GitHub Pages
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install, build, and upload your site
uses: withastro/action@v3
with:
node-version: 22
deploy:
needs: build
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
A few things worth noting:
withastro/actionhandles the full build pipeline (install → build → upload artifact) in one step, so there is no need to manually callnpm ci,npm run build, orupload-pages-artifactseparately.workflow_dispatchlets you trigger a manual deployment from the GitHub Actions UI without pushing a commit.concurrencyensures only one deployment runs at a time and that in-progress deployments are never cancelled mid-flight.
Enabling Pages in the Repository
- Go to Settings → Pages in your repository.
- Under Build and deployment, choose GitHub Actions as the source.
- Push a commit to
main— the workflow runs automatically and your site is live athttps://<username>.github.io/<repo>.
A custom domain is just as easy: add a CNAME file to the public/ directory in Astro (so it ends up in dist/) and point your DNS to GitHub’s servers.
The Development Loop
My typical workflow for a new post or feature:
- Branch —
git checkout -b feature/my-new-post - Write — draft content or code with Copilot inline suggestions
- Review — Copilot Chat reviews the diff and flags anything odd
- Commit & push —
git push origin feature/my-new-post - Pull request — GitHub Actions runs a build check on the PR
- Merge to
main— the deploy workflow fires and the site updates in under two minutes
Lessons Learned
Copilot is Best as a First Draft Machine
Copilot is fastest when I treat its output as a starting point, not a finished product. I always review generated code for correctness, style consistency, and accessibility before committing.
Chat vs. Inline Completions
- Inline completions shine for repetitive patterns: frontmatter, component props, Tailwind class strings.
- Copilot Chat is better for architectural questions, debugging, and multi-file changes.
Trust but Verify
Copilot occasionally generates outdated API usage (e.g., deprecated Astro APIs). Cross-referencing with the official Astro docs takes seconds and is always worth it.
GitHub Pages: The Hosting Story
GitHub Pages has been rock-solid for this site. Key benefits:
- Free for public repositories
- Automatic HTTPS via Let’s Encrypt
- Global CDN backed by Fastly
- Zero maintenance — no servers, no containers, no bills
For a personal blog with static content, there is genuinely no reason to reach for anything more complex.
Wrapping Up
GitHub Copilot cut the time I spent on boilerplate in half and kept me in flow while building this site. Paired with GitHub Pages, the result is a fast, free, and fully automated publishing pipeline — from writing a Markdown file to a live post in two minutes.
If you are thinking about starting your own blog, this stack is hard to beat. The barrier to entry is low, the tooling is excellent, and having an AI pair programmer in your corner makes the whole process genuinely enjoyable.
Have questions about the setup? Feel free to reach out or open a discussion in the repository.