ai copilot ember development tutorial

How We Built This Blog in About 10 Minutes with Copilot πŸš€

In my first post I talked about why I'm writing about AI-augmented development. This one is about something more concrete:

Can you go from zero to a working, decent-looking blog in about 10 minutes with modern tools and AI?

Short answer: yes. Longer answer: here's how we did it, what Copilot actually helped with, and where we still had to think.

The Goal

We wanted a simple, modern blog for Crunchy Bananas that:

  • Uses markdown for posts (easy to write, review, and version)
  • Runs on a modern stack we actually like using (Ember.js, TypeScript, Vite)
  • Has a fun, banana-flavored design 🍌
  • Can be deployed without dragging in a whole CMS or backend

Basically: something nice enough to keep, but fast enough to build that we didn't overthink it and stall.

Quick note: If you're wondering "Why Ember?" check out my first post where I talk about the framework choice and why Ember's conventions work well with AI-augmented development.

The Stack

Here's the stack that made this possible:

  • GitHub Copilot – AI pair programmer that scaffolds a surprising amount of boilerplate
  • Ember.js with TypeScript – Convention-driven framework + types for real projects
  • Vite – Fast dev server and build tooling
  • Tailwind CSS v4 – Utility-first styling that lets you get something decent-looking in minutes
  • Markdown – Plain text content that lives in Git with the code

Nothing exotic. The interesting part is how these pieces combine with AI.

The Process

Step 1: Project Setup (~2 minutes)

We started from a fresh Ember app using the Vite-based setup. First, we made sure to install the right version of the Ember CLI (v6.9+) to get Vite support:

npm install -g ember-cli@6.9
ember new crunchy-blog
cd crunchy-blog
pnpm install

I started a chat with Copilot and described what I wanted: a markdown-based blog with a list of posts and a post detail route.

Nothing fancy β€” just enough context so its suggestions were pointed in the right direction.

Step 2: Blog Architecture (~3 minutes)

The basic idea:

  • Store metadata about posts in a JSON file
  • Store the actual content as markdown files
  • Have a service that loads the metadata and fetches markdown on demand
  • Use standard Ember routes/templates to render the blog index and individual posts

With that in mind, I asked Copilot for:

"An Ember service (TypeScript) that loads blog metadata from /public/blog-posts/index.json and fetches markdown content by slug."

It got us most of the way there. We ended up with something like this:

// app/services/blog.ts
import Service from '@ember/service';
import { marked } from 'marked';

interface BlogPost {
  id: string;
  title: string;
  date: string;
  slug: string;
  excerpt: string;
}

interface BlogPostWithContent extends BlogPost {
  content: string;
}

export default class BlogService extends Service {
  async loadPosts(): Promise<BlogPost[]> {
    const response = await fetch('/blog-posts/index.json');
    const data = await response.json();
    return data.posts;
  }

  async loadPost(slug: string): Promise<BlogPostWithContent | null> {
    const posts = await this.loadPosts();
    const post = posts.find((p) => p.slug === slug);

    if (!post) {
      return null;
    }

    const response = await fetch(`/blog-posts/${slug}.md`);
    const markdown = await response.text();
    const html = marked(markdown);

    return { ...post, content: html };
  }
}

Complete working example: See the blog companion file for full route and template implementations.

Copilot suggested most of this, and we nudged it into the right shape:

  • Fixed up types
  • Adjusted the paths
  • Made sure the return type matched what the routes/templates expect

Worth noting: I gave Copilot very little context here β€” just a quick description in the chat. No custom instructions, no detailed best practices, no project-specific guidelines. For our larger production projects, we have extensive instruction files, style guides, and architectural patterns that help AI generate much more precise code.

There's also an Ember MCP server that could make this even easier by giving AI direct access to Ember conventions and documentation. I'll explore MCP integrations in future posts, but for this quick build, basic chat context was enough.

The key point: Ember's conventions did a lot of the heavy lifting. Routes, services, and templates all go where you'd expect, which makes it much easier for AI to play along β€” even with minimal context.

Step 3: Tailwind CSS Setup (~1 minute)

Tailwind v4 setup was basically:

pnpm add -D tailwindcss@next @tailwindcss/vite@next

Then:

  • Add the Tailwind Vite plugin
  • Import the Tailwind base styles into our main CSS

Done.

Not everyone loves Tailwind, and that's fine. The important part is: pick some styling approach that lets you move quickly. Tailwind happens to be that for us. For full details on the Tailwind v4 setup with Ember and Vite, check out our complete setup guide.

Step 4: Design & Branding (~4 minutes… plus a few more for fun)

Once the wiring was in place, we made it look like Crunchy Bananas and not "default starter project":

  • Sprinkled banana emojis 🍌 into the UI where it made sense
  • Used a yellow β†’ orange gradient for the main accent areas
  • Added a simple layout for the blog index (cards) and post pages (centered, readable content)
  • Refined spacing, typography, and hover states with Tailwind utilities

Copilot helped here too: things like suggesting reasonable class lists for cards, headings, and buttons. We still did the actual design decisions, but it sped up the part where you type out rounded-xl shadow-sm px-4 py-3 for the 200th time.

Honestly, we probably spent more time nudging colors and spacing than we did on the routing and services.

The Results

In roughly 10 minutes of focused work (plus a bit extra for polish), we had:

  • βœ… A working blog with markdown-backed posts
  • βœ… A clean, responsive layout that doesn't scream "I shipped this in a panic"
  • βœ… Type-safe code with TypeScript and Ember
  • βœ… A fast dev loop courtesy of Vite
  • βœ… Content that lives right next to the code (just add a .md file and update index.json)

No database. No CMS. No "admin UI." Just Git.

What Copilot Actually Did (and Didn't Do)

A quick reality check, in the spirit of AI transparency:

Copilot was great at:

  • Scaffolding the initial BlogService
  • Filling in Ember route boilerplate
  • Suggesting reasonable Tailwind class combinations
  • Handling repetitive bits (types, imports, simple utility functions)

We still had to:

  • Decide on the architecture (service + static files + routes)
  • Choose URL shapes and directory structure
  • Make design choices that actually look like "Crunchy Bananas"
  • Add proper TypeScript signatures for type-safe templates
  • Test and tweak until it felt right

It didn't magically "build a blog." It collapsed the time between "I know what I want" and "I have the code in front of me."

Note on TypeScript: We later added proper TemplateOnlyComponent (TOC) signatures to all templates for full type safety. This wasn't strictly necessary to get the blog working, but it eliminates runtime errors and provides better IDE autocomplete. See the companion file for the complete typed examples.

What We Learned

A few takeaways from doing this end-to-end:

AI-assisted development is a multiplier, not a magic trick

Copilot didn't replace thinking. It just made the implementation part go much faster.

Modern tools stack really well

Ember's conventions, Vite's speed, Tailwind's utilities, and TypeScript's guardrails all play nicely together β€” and with AI.

Static blogs are still underrated

For a lot of use cases, "markdown in Git" beats "full CMS + database + auth + plugin ecosystem."

TypeScript is still worth it, even for "quick" projects

Even in a tiny codebase, types caught silly mistakes that would've turned into runtime "why is this undefined?" bugs.

Try It Yourself

If you want to do something similar, the rough recipe looks like this:

  1. Pick a modern framework you're comfortable with
    Ember, SvelteKit, Astro, whatever fits your brain

  2. Add Tailwind (or your styling approach of choice)

  3. Use Copilot (or your AI of choice) to scaffold:

    • A service/module that loads metadata
    • Routes/pages for list + detail views
  4. Store your content as markdown in the repo

  5. Deploy to Netlify, Vercel, or GitHub Pages

The whole stack is free for personal projects, and you can go from idea to "here's a link" in an evening. Or, if you lean into this kind of setup, in about 10 minutes.

What's Next?

Now that the blog is up, we have a solid base to build on:

  • Add search
  • Implement tags and categories
  • Generate an RSS feed
  • Add syntax highlighting for code blocks
  • Play with MCP-backed workflows (e.g., have AI draft post metadata)

None of that is blocking us from publishing. The important part is: we have a working blog right now.

We can iterate in public.

Closing Thoughts

Building a blog doesn't need to be a weeks-long side project. With the right tools and a clear picture of what you want, you can get to "good enough to ship" fast β€” and then spend your energy on writing instead of yak-shaving.

Is this blog perfect? No.
Does it do the job? Absolutely.
Can we keep improving it? Definitely.

That's modern web dev in a nutshell.

Now go build something. πŸŒπŸš€


Want the code? We cleaned up this blog into an open-source starter kit: crunchy-ember-blogkit. Same stack, different colors, ready to customize.


AI Transparency: The blog itself was built entirely using GitHub Copilot with Claude Sonnet 4.5. This post was written by me with assistance from Claude Sonnet 4.5 and GPT-5.1.


This post was written after we actually built this blog. The "about 10 minutes" is real for getting to a working version. The extra styling time? That's just us being picky. πŸ˜‰

If this article helped you ship code, consider sponsoring my work.

No perks. No ads. Just support for writing that doesn't waste your time.