Tutorial Quickstart
End-to-end guide: scaffold a project, write your first lesson, and deploy.
Step 1: Scaffold
npx create-tutorialkit-rb my-tutorial
The CLI prompts for:
| Prompt | Default | Notes |
|---|---|---|
| Tutorial name | random (e.g., "fierce-turtle") | Used as package.json name |
| Directory | ./{name} |
Where files are created |
| Hosting provider | Skip | Vercel, Netlify, or Cloudflare — adds COEP/COOP headers |
| Package manager | npm | npm, yarn, pnpm, or bun |
| Init git repo? | Yes | Creates initial commit |
| Edit Gemfile? | Yes | Opens ruby-wasm/Gemfile in $EDITOR |
Skip all prompts with --defaults, or pass flags directly:
npx create-tutorialkit-rb my-tutorial -p pnpm --provider netlify --git
What Gets Created
my-tutorial/
├── src/
│ ├── content/tutorial/ ← Your tutorial content goes here
│ │ ├── meta.md ← Tutorial root config (already set up)
│ │ └── 1-getting-started/ ← Sample part with starter lessons
│ ├── templates/default/ ← WebContainer runtime (don't modify)
│ └── components/ ← UI components
├── ruby-wasm/
│ └── Gemfile ← Add gems here, then rebuild WASM
├── bin/build-wasm ← Rebuilds the WASM binary
├── astro.config.ts
└── package.json
Step 2: Add Your Gems
Edit ruby-wasm/Gemfile to include the gems your tutorial needs:
# ruby-wasm/Gemfile
source "https://rubygems.org"
gem "wasmify-rails", "~> 0.4.0"
gem "rails", "~> 8.0.0"
# Your tutorial's gems
gem "action_policy"
gem "devise"
Then build the WASM binary:
npm run build:wasm # Takes up to 20 minutes on first run
Subsequent rebuilds are faster thanks to caching, but still take a few minutes.
Step 3: Start the Dev Server
npm run dev # Starts at http://localhost:4321/
The sample tutorial loads immediately. You'll see the starter lessons from the scaffold.
Step 4: Write Your First Lesson
4a. Create the Directory Structure
src/content/tutorial/
├── meta.md ← Already exists (tutorial root)
└── 1-basics/
├── meta.md ← Part metadata
└── 1-hello-rails/
├── content.md ← Your lesson
├── _files/ ← Starting code
│ └── workspace/
│ └── app/
│ └── controllers/
│ └── pages_controller.rb
└── _solution/ ← Solution code
└── workspace/
└── app/
└── controllers/
└── pages_controller.rb
4b. Write the Part Metadata
# src/content/tutorial/1-basics/meta.md
---
type: part
title: The Basics
---
4c. Write the Lesson
# src/content/tutorial/1-basics/1-hello-rails/content.md
---
type: lesson
title: Hello Rails
focus: /workspace/app/controllers/pages_controller.rb
previews: [3000]
mainCommand: ['node scripts/rails.js server', 'Starting Rails server']
prepareCommands:
- ['npm install', 'Preparing Ruby runtime']
- ['node scripts/rails.js db:prepare', 'Prepare development database']
terminalBlockingPrepareCommandsCount: 2
custom:
shell:
workdir: '/workspace'
---
# Hello Rails
Open `app/controllers/pages_controller.rb` and add a `home` action:
\`\`\`ruby title="app/controllers/pages_controller.rb" ins={2-4}
class PagesController < ApplicationController
def home
render plain: "Hello from Rails on WebAssembly!"
end
end
\`\`\`
Visit the preview to see your message.
4d. Add Starting Files
Put a skeleton file in _files/:
# _files/workspace/app/controllers/pages_controller.rb
class PagesController < ApplicationController
# Add your action here
end
4e. Add Solution Files
Put the completed code in _solution/:
# _solution/workspace/app/controllers/pages_controller.rb
class PagesController < ApplicationController
def home
render plain: "Hello from Rails on WebAssembly!"
end
end
4f. Delete the Sample Content
Remove the scaffold's starter lessons once you have your own:
rm -rf src/content/tutorial/1-getting-started/
rm -rf src/content/tutorial/2-controllers/
The rails-app Template
The scaffold includes a pre-built rails-app template at src/templates/rails-app/ with authentication, styling, and seed data ready to go. Most tutorials should extend this template rather than building from scratch.
What's Included
- Authentication — session-based login via
Authenticationconcern (app/controllers/concerns/authentication.rb) - Quick login — password-free login buttons on the sign-in page for tutorial convenience
- CSS design system — modern BEM-based stylesheet with CSS custom properties
- Seed users — Alice and Bob created in
db/seeds.rb - Layout — nav bar with brand, user name, login/logout; flash messages;
.containerwrapper
Authentication Flow
The template uses Rails 8's authentication generator pattern:
Authenticationconcern addsrequire_authenticationas abefore_action- Controllers opt out with
allow_unauthenticated_access Current.useris available everywhere viaCurrent.session.userauthenticated?helper works in both controllers and views
Quick login lets tutorial users sign in with one click instead of typing credentials:
SessionsController#newpopulates@preauthenticate_users(all users by default)SessionsController#preauthenticatelogs in by user ID (no password)- The
sessions/_preauthenticate_user.html.erbpartial renders each quick-login button - Route:
post :preauthenticate, on: :collectionunderresource :session
To customize quick-login users in a lesson, override the sessions controller in _files/:
# _files/workspace/app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
allow_unauthenticated_access only: %i[new create preauthenticate]
def new
# Show only specific users for this lesson
@preauthenticate_users = User.where(role: "agent").order(:name)
end
# ... rest inherited from template
end
CSS Design System
The template's application.css uses pure CSS with custom properties and BEM naming. Use these classes in your lesson ERB files — no extra setup needed.
CSS Custom Properties (:root variables):
| Category | Variables | Example |
|---|---|---|
| Colors | --color-primary, --color-danger, --color-success, --color-warning, --color-info |
color: var(--color-primary) |
| Text | --color-text, --color-text-muted, --color-text-inverse |
color: var(--color-text-muted) |
| Background | --color-bg, --color-bg-white, --color-border |
background: var(--color-bg) |
| Spacing | --space-xs through --space-2xl |
padding: var(--space-md) |
| Typography | --font-sans, --font-mono, --font-size-sm through --font-size-3xl |
font-size: var(--font-size-lg) |
| Radius | --radius-sm through --radius-xl |
border-radius: var(--radius-md) |
| Shadows | --shadow-sm, --shadow-md |
box-shadow: var(--shadow-sm) |
BEM Components:
| Component | Classes | Usage |
|---|---|---|
| Button | .btn, .btn--primary, .btn--danger, .btn--small, .btn--link |
Links, submits, actions |
| Input | .input, .input--error |
Text fields, selects, textareas |
| Card | .card, .card__header, .card__body, .card__footer |
Content containers |
| Alert | .alert, .alert--error, .alert--success, .alert--info, .alert--warning |
Flash messages, notices |
| Badge | .badge, .badge--primary, .badge--success, .badge--danger, .badge--warning |
Status labels, role tags |
| Nav | .nav, .nav__brand, .nav__link, .nav__user |
Top navigation (in layout) |
| Form | .form__group, .form__label, .form__hint, .form__errors, .form__actions |
Form layout |
| Table | .table |
Data tables with hover rows |
| Page header | .page-header |
Title + action button row |
| Hero | .hero, .hero__title, .hero__subtitle, .hero__actions |
Landing/home pages |
| Quick login | .quick-login, .quick-login__btn, .quick-login__name, .quick-login__email |
Sign-in page |
| Utility | .text-muted, .text-sm, .mt-md, .mb-md, .inline-actions, .container |
Spacing, text helpers |
Customizing the Demo App for Your Domain
To turn the generic demo app into your tutorial's domain (e.g., a Help Desk, a Store, etc.):
1. Rename the app module in config/application.rb:
module Helpdesk # was DemoApp
class Application < Rails::Application
2. Add your models. Create migrations in db/migrate/ and models in app/models/. Update db/schema.rb to match.
3. Add controllers and views. Put CRUD controllers in app/controllers/ and ERB views in app/views/. Use the BEM classes from the CSS design system.
4. Update routes in config/routes.rb.
5. Update seeds in db/seeds.rb with domain-specific sample data. Keep the default password s3cr3t for all users so the quick-login flow works.
6. Update the layout — change the brand name in app/views/layouts/application.html.erb, add nav links for your resources.
7. Update the home page — replace the hero content in app/views/home/index.html.erb.
Step 5: Use a Template for Pre-Built State
If your lesson needs an existing Rails app (not just an empty workspace), create a template:
src/templates/my-app/
├── .tk-config.json → { "extends": "../default" }
└── workspace/
├── app/
├── config/
├── db/
└── ...
Then reference it from your lesson's _files/.tk-config.json:
{
"extends": "../../../../../templates/my-app"
}
See the rails-file-management skill for details on template inheritance.
Step 6: Deploy
Tutorials need Cross-Origin-Embedder-Policy and Cross-Origin-Opener-Policy headers for WebContainers to work. If you chose a hosting provider during scaffold, these are already configured.
Build for Production
npm run build # Produces a static site in dist/
Manual Header Configuration
If you didn't choose a provider during scaffold, add these headers to every response:
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Vercel (vercel.json)
{
"headers": [
{
"source": "/(.*)",
"headers": [
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" },
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" }
]
}
]
}
Netlify (netlify.toml)
[[headers]]
for = "/*"
[headers.values]
Cross-Origin-Embedder-Policy = "require-corp"
Cross-Origin-Opener-Policy = "same-origin"
Cloudflare (public/_headers)
/*
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin
Customizing Look & Feel (Branding)
To match your tutorial's branding to your project's documentation site, update these files:
Logos
Replace public/logo.svg (light mode) and public/logo-dark.svg (dark mode) with your project's logo SVG. Use a dark fill (e.g., #0F4D8A) for the light-mode version and a light fill (e.g., #E4E6E9) for the dark-mode version.
Title in Top Bar
Edit src/components/TopBar.astro — add a <span> after the logo images inside the <a> tag:
<span class="ml-2 text-sm font-medium text-tk-elements-topBar-iconButton-iconColor whitespace-nowrap">
Your Tutorial Title
</span>
Favicon
Replace public/favicon.svg with your project's icon. Optionally add a public/favicon.ico for broader browser support.
Accent Colors (UnoCSS Theme)
Override the accent palette in uno.config.ts to change buttons, links, active tabs, and badges site-wide:
import { defineConfig } from '@tutorialkit-rb/theme';
export default defineConfig({
theme: {
colors: {
accent: {
50: '#EFF6FF',
100: '#E5F0FF',
200: '#B6D4FF',
300: '#75B5FF',
400: '#4DA6FF', // dark mode accent
500: '#0E7EF1', // primary interactive
600: '#0F4D8A', // primary brand
700: '#0C3F72',
800: '#09325A',
900: '#072848',
950: '#041A30',
},
},
},
content: {
pipeline: { include: '**' },
},
});
Generate your scale from your brand's primary color. The 600 slot is the main brand color; 500 is for hover/interactive states; 400 is used in dark mode.
Component Hardcoded Colors
Some components use hardcoded Tailwind color classes instead of theme tokens. Search for and replace these:
src/components/HelpDropdown.tsx— Reload button usesbg-blue-600. Change tobg-accent-600 hover:bg-accent-700.src/components/HeadTags.astro— Rails path link colors. Update hex values to match your brand.
Rails Demo App CSS
Update the primary color in src/templates/rails-app/workspace/app/assets/stylesheets/application.css:
:root {
--color-primary: #0F4D8A; /* your brand color */
--color-primary-hover: #0C3F72; /* darker shade */
--color-primary-light: #EFF6FF; /* tinted background */
}
OG Meta (Social Sharing Image)
Add Open Graph meta tags so your tutorial shows a rich preview when shared on social media, Slack, etc.
1. Generate a cover image. Use a tool like myogimage.com to create a 1200×630 OG image with your tutorial title and branding.
2. Save it as public/cover.png.
3. Add the meta key to your tutorial root src/content/tutorial/meta.md:
---
type: tutorial
meta:
image: /cover.png
title: Your Tutorial Title
description: |
A short description of what your tutorial teaches
# ... rest of frontmatter
---
The meta fields map to standard OG tags (og:image, og:title, og:description) and are rendered in <head> automatically by TutorialKit.
GitHub Link
Update the repo URL in src/components/GitHubLink.astro:
<a href="https://github.com/your-org/your-repo" ...>
Common Issues
| Problem | Cause | Fix |
|---|---|---|
build:wasm fails |
Missing WASI SDK or build tools | Check rbwasm prerequisites |
| Preview shows nothing | Server not started | Add mainCommand: ['node scripts/rails.js server', ...] |
| Terminal stuck on "Preparing" | WASM binary not built | Run npm run build:wasm first |
| Files not appearing in editor | Wrong path | All Rails files must be under workspace/<app>/ |
| Database empty | No db:prepare in prepareCommands |
Add ['node scripts/rails.js db:prepare', '...'] |
| Deploy fails with blank page | Missing COEP/COOP headers | Add headers per provider instructions above |
Next Steps
| Want to... | See skill |
|---|---|
| Structure parts, chapters, lessons | tutorial-content-structure |
| Configure frontmatter options | tutorial-lesson-config |
| Organize Rails files properly | rails-file-management |
| Check if a feature works in WASM | rails-wasm-author-constraints |
| Get a recipe for a specific lesson type | rails-lesson-recipes |