Skip to content

Start Here

Tutorial: Build a Blog

You’ll build a complete blog application. Posts with titles and bodies. Comments under each post. Users who sign up, log in, and can only edit their own content. Forms that validate inline without page reloads. One browser test that clicks through the whole flow.

By the end you’ve touched every part of Wheels that matters in day-to-day work: models, migrations, seeds, scaffolds, validations, associations, forms, routes, filters, Turbo Frames, Turbo Streams, authentication, and tests.

You’ll learn: everything in The Basics and most of Digging Deeper. This tutorial is the single best introduction to Wheels 4.0.

A blog for an individual author. The final app supports:

  • Posts — a title, body, status (draft / published / archived), and publication date
  • Comments — readers can comment on published posts without signing up
  • Users — authors sign up with email and password, then manage their own posts
  • Turbo UI — form errors appear inline, new comments append without page reloads, page transitions have zero flash
  • Tests — one model spec, one controller spec, one browser spec clicking through the full flow
  • Framework: Wheels 4.0 on Lucee 7 (via the bundled wheels CLI)
  • Database: SQLite, zero setup. Same engine the tutorial runs against in CI.
  • Frontend: Turbo Drive for page transitions, Turbo Frames for inline errors, Turbo Streams for comment updates. Loaded from a CDN script tag in Part 4; the installable wheels-hotwire package (wheels packages add wheels-hotwire) is the canonical home once you’re past the tutorial.
  • Styling: simple.css, wired into the generated app/views/layout.cfm by wheels new. Classless — semantic HTML renders polished without any markup changes. The wheels-basecoat package is available as an optional shadcn/ui-quality component-kit upgrade after the tutorial.
  • Auth: Wheels’ built-in SessionStrategy for session cookies. Hand-rolled version shown first for understanding, built-in version shown second for shipping.
  • Tests: WheelsTest (BDD), plus one Playwright-driven browser spec.
PartTopicTime
1. Hello, WheelsScaffold an app. Add a route, a controller action, a view.20 min
2. Your First ModelGenerate a Post model. Run a migration. Seed data. Build the index and show actions by hand.25 min
3. CRUD ScaffoldThrow it away. Run wheels generate scaffold. Tour the generated files and meet Wheels’ package system.25 min
4. Validations and Turbo FramesAdd model validations. Wrap the form in a <turbo-frame> so errors come back inline.30 min
5. Comments and Turbo StreamsAdd a Comment model. Associate it with Post. Nested routes. Submit comments via Turbo Streams.35 min
6. AuthenticationHand-roll sessions first to understand the mental model. Then replace with wheels.auth.SessionStrategy.45 min
7. Testing and DeployingModel spec, controller spec, one browser spec. Deployment overview. What to read next.35 min
Bonus: Style with wheels-basecoatOptional. Install the basecoat package and convert the post view to shadcn/ui-quality components. Meets the Wheels package system end-to-end.30 min
  • “Where we left off” — the first section of every part after Part 1. A file tree and schema summary so you can resume without reading the previous part end-to-end.
  • Numbered steps — each change to the code is a numbered step in a <Steps> block. You’ll see the file path, the before state when relevant, and the final content.
  • Checkpoint — before moving on, a concrete check you can run (usually curl against a URL or wheels test).
  • Troubleshooting — three common failure modes with fixes, including the error message you’d see.
  • SQLite. All development runs against SQLite in db/development.sqlite. Zero setup. Matches the CI testing platform.
  • Real names. Code examples use Post, Comment, user.email, publishedAt — not Foo, Bar, or someField.
  • Complete code. Each block shows the whole function or view, not a fragment. You can always copy a block and have the feature work.
  • Validated. Code blocks annotated with {test:compile} or {test:cli} are validated by the doc site’s verify-docs harness against a real wheels CLI. Blocks without an annotation (shell transcripts, illustrative excerpts) are reviewed but not executed.