Create interactive Ruby on Rails tutorials that run entirely in the browser.
No server. No Docker. No setup friction. Just open a URL and start learning.
RailsConf 2025 Talk · EuRuKo 2025 Talk · Original Framework Docs
TutorialKit.rb is a fork of TutorialKit by StackBlitz, extended to run a full Ruby on Rails environment in the browser via WebAssembly. It was built at Evil Martians and funded by the Ruby Association Grant 2025.
Learners open a URL and get a live Rails 8 app with an editor, terminal, and browser preview — all running client-side. Tutorial authors write lessons in Markdown with frontmatter config. No backend infrastructure required.
# Scaffold a new tutorial project
npx create-tutorialkit-rb my-tutorial
cd my-tutorial
# Build the WASM binary — pick ONE of the two approaches:
npm run build:wasm # Full build from source (5-20 min, supports C extension gems)
npm run pack:wasm # Pack gems onto prebuilt base binary (~30s, pure-Ruby gems only)
# Start the dev server
npm run devYou only need to rebuild when you change the Gemfile. Day-to-day lesson authoring (Markdown, Ruby app code) requires no WASM rebuilds.
Open http://localhost:4321/ — you'll see the tutorial UI with a live Rails app running in your browser.
TutorialKit.rb runs a three-layer stack entirely in the browser:
Browser (Astro/React UI)
└─ WebContainer (virtual Node.js)
└─ Ruby 3.3 + Rails 8.0 (WebAssembly)
Browser layer — Astro/React components render the tutorial UI: editor panels, file tree, terminal, and a preview iframe.
WebContainer layer — StackBlitz's WebContainer provides a virtual Node.js environment. Express.js bridges HTTP requests between the preview iframe and Rails via Rack.
Ruby/Rails layer — A custom Ruby 3.3 binary compiled to WebAssembly with gems embedded in the binary's virtual filesystem. Boot takes 2-5 seconds because gem loading reads from linear memory (microseconds per file) rather than crossing the WASM-to-JS boundary.
Full MVC, ActiveRecord (via PGLite), generators (rails generate), forms, sessions, flash messages, Turbo/Hotwire, Propshaft asset pipeline, rails new with built-in authentication, IRB console, and outbound HTTP (Net::HTTP, Faraday, HTTParty — routed through a JS fetch bridge).
Single-threaded (no real threads), no raw sockets (HTTP works via fetch bridge), no process spawning (commands run inline), no streaming HTTP, CORS proxy required for external APIs.
The WASM binary (~80MB) contains the Ruby interpreter, stdlib, and all gems. There are two build strategies:
Compiles Ruby, all gems (including C extensions), and the app into a single WASM binary. This is the robust, proven approach.
npm run build:wasm # Incremental build (5-20 min)
npm run build:wasm:clean # Full clean rebuildUse this when your tutorial gems include WASM-compatible C extensions (e.g., bcrypt, websocket-driver). C extensions must be cross-compiled into the binary by rbwasm build — they can't be added after the fact.
Downloads a prebuilt base binary (Ruby + stdlib + C extensions) from GitHub Releases, then packs your pure-Ruby gems on top via wasi-vfs pack. Functionally identical to the monolithic build, but significantly faster on the first build (~30 seconds vs 5-20 minutes).
npm run pack:wasm # Pack gems onto base (~30s)
npm run pack:wasm:clean # Clean + repackLimitations: still experimental (basic workflows work, but build:wasm is more battle-tested), and cannot include C extension gems beyond what's already in the base binary — those require a full monolithic build because rbwasm build must cross-compile them from C source to WASM.
The official @ruby/3.3-wasm-wasi binary cannot run Rails. Its POSIX function stubs (fchmod, chmod, etc.) crash at runtime with NotImplementedError: false(). The custom binary built via rbwasm build disables these code paths at compile time using autoconf flags.
Tutorials are organized hierarchically: Parts > Chapters > Lessons. Each lesson is a directory with a content.md (Markdown + frontmatter) and optional _files/ (starter code) and _solution/ (solution code) directories.
src/content/tutorial/
1-getting-started/ # Part
meta.md
1-introduction/ # Chapter
meta.md
1-welcome/ # Lesson
content.md
_files/
app/controllers/home_controller.rb
_solution/
app/controllers/home_controller.rb
Lessons support live code editing, terminal commands, database operations, IRB console, and preview — all configured via frontmatter. See the TutorialKit docs for the full content authoring guide.
The scaffolded project includes a template Rails 8 app with:
- Built-in authentication with single-click auth flow
- BEM-based CSS design system
- PGLite database (PostgreSQL-compatible, runs in-browser)
- Turbo/Hotwire integration
Rails code can make real HTTP requests to external APIs. The bridge routes Net::HTTP calls through JavaScript's fetch():
Ruby Net::HTTP → JS fetch bridge → Browser fetch → CORS proxy → External API
A Cloudflare Worker CORS proxy is included in packages/template/cors-proxy/. Deploy it with:
cd packages/template/cors-proxy
npx wrangler login
npm run deployConfigure allowed hosts in wrangler.toml. Run the dev server with the proxy:
npm run proxy:dev # Starts dev server + CORS proxy togetherSee packages/template/cors-proxy/ for full setup.
The template includes 6 Claude Code skills in packages/template/.claude/skills/ that provide domain knowledge for AI-assisted tutorial authoring:
| Skill | What it knows |
|---|---|
tutorial-quickstart |
End-to-end workflow from scaffold to deployment |
tutorial-lesson-config |
All frontmatter options and inheritance rules |
tutorial-content-structure |
Parts/chapters/lessons hierarchy and Markdown features |
rails-lesson-recipes |
Five tested lesson blueprints (terminal, code-editing, database, full-app, console) |
rails-file-management |
_files/, _solution/, templates, workspace paths |
rails-wasm-author-constraints |
What works in WASM, gem compatibility, PGLite behavior |
When working with Claude Code in a scaffolded tutorial, these skills activate automatically based on context — ask about lesson structure, WASM compatibility, or deployment and the relevant skill provides authoritative guidance.
Build the static site and deploy to any static hosting provider (Netlify, Vercel, Cloudflare Pages, etc.):
npm run buildThe host must serve pages with cross-origin isolation headers:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Seven packages are published under the @tutorialkit-rb scope. Releases are automated via GitHub Actions:
- Trigger the Release workflow with a version number
- It creates a version-bump PR with changelogs
- Merge the PR to publish to npm and create a git tag
The release workflow is defined in .github/workflows/release.yaml.
| Workflow | Purpose |
|---|---|
release.yaml |
Version bump PR + npm publish + git tag |
publish-ruby-base.yaml |
Build and publish base WASM binary to GitHub Releases |
deploy-cors-proxy.yaml |
Deploy CORS proxy to Cloudflare Workers |
Validate a WASM binary in a Node.js environment locally:
npm run smoke # Runs VM init, Rails bootstrap, HTTP bridge testsTests cover: WASM compilation, Rails boot, rails new, scaffold generation, Express-to-Rack bridge, and outbound HTTP. Takes ~2.5 seconds.
For contributors working on TutorialKit.rb itself (not tutorial authors):
# Prerequisites: Node.js 18.18+, pnpm 8.15.6
pnpm install
pnpm run build # Build all core packages
pnpm run dev # Dev mode with hot reloading (localhost:4321)| Package | npm | Purpose |
|---|---|---|
@tutorialkit-rb/astro |
Published | Astro integration and components |
@tutorialkit-rb/react |
Published | React UI (editor, terminal, file tree, preview) |
@tutorialkit-rb/runtime |
Published | WebContainer lifecycle and lesson execution |
@tutorialkit-rb/theme |
Published | Theming system and CSS |
@tutorialkit-rb/types |
Published | TypeScript type definitions |
@tutorialkit-rb/cli |
Published | CLI tooling |
create-tutorialkit-rb |
Published | npx create-tutorialkit-rb scaffolder |
packages/template |
Internal | Template project with Rails WASM runtime |
MIT