
March 11, 2026
What if you could run a fully functional Rails application — backend, database, file storage, and all — directly inside a browser tab, with zero servers?
WebAssembly (Wasm) makes this possible. By compiling Ruby and Rails into a Wasm module, the entire application stack executes client-side. This post explores a practical journey: from scaffolding your Rails app to shipping it as a Progressive Web App (PWA) that runs entirely offline.
This article is an original write-up, providing a practical, technical deep-dive into running Ruby on Rails in the browser via WebAssembly. It was inspired by the following works:
- “Ruby on Rails on WebAssembly” – web.dev, 2025. Explains how to compile Rails apps into Wasm, run databases in-browser, and leverage PWA for offline full-stack apps. Read more
- “Developing Ruby in the Browser” – RubyKaigi 2023 Presentation by Yuma Sawai. Introduces ruby.wasm, demonstrates browser execution of Ruby, and showcases frameworks bridging browser APIs with Ruby logic. View presentation
💡 The concepts and technical examples in this article are originally written, synthesizing these sources into a cohesive guide and practical walkthrough for developers.
1️⃣ Start with a Standard Rails App
Begin with a conventional scaffold — for example, a simple blogging platform:
$ rails new --css=tailwind blog_wasm$ cd blog_wasm$ bin/rails generate scaffold Post title:string date:date body:text$ bin/rails db:migrate && bin/dev
At this stage, the app runs locally on Puma backed by SQLite. The goal? Move this entire stack into the browser.
2️⃣ Compile Ruby to WebAssembly
Ruby 3.2+ supports Wasm. The wasmify-rails gem bundles the Ruby VM, gems, native extensions, and standard library into a single .wasm module:
$ bundle add wasmify-rails$ bin/rails wasmify:install$ bin/rails wasmify:build
The result: a self-contained binary (~70 MB) ready to run in any modern browser. Native extensions like nokogiri or bcrypt are compiled automatically.
3️⃣ Service Workers as Your Web Server
In-browser, a Service Worker replaces Puma:
let vm;const initVM = async () => { if (!vm) vm = await initRailsVM('/app.wasm'); return vm;};self.addEventListener('fetch', (event) => { event.respondWith( initVM().then((vm) => { const rackHandler = new RackHandler(vm); return rackHandler.handle(event.request); }) );});
All requests are intercepted, translated to Rack format, and handled by Rails in Wasm — no changes to your app logic required.
4️⃣ In-Browser Databases
Active Record remains unchanged. SQLite compiled to Wasm (or PGlite for Postgres) serves as your persistence layer:
export function registerSQLiteInterface(worker, db) { worker.sqlite = { exec: (sql) => db.exec(sql, { returnValue: 'resultRows' }), changes: () => db.changes(), };}
Database configuration:
# config/database.ymlwasm: adapter: sqlite3_wasm js_interface: 'sqlite'
Rails queries run normally — the adapter routes them to the in-browser SQLite engine.
5️⃣ File Storage with OPFS
Active Storage uploads are handled via the Origin Private File System (OPFS):
async function saveFile(name, data) { const dir = await navigator.storage.getDirectory(); const fh = await dir.getFileHandle(name, { create: true }); const writable = await fh.createWritable(); await writable.write(data); await writable.close();}
A custom adapter wraps these calls. All file uploads and retrievals happen client-side.
6️⃣ Launch as a PWA
Tie it all together:
$ bin/rails wasmify:pwa$ bin/rails wasmify:pack$ cd pwa && yarn dev
Open http://localhost:5173/ — your full-stack Rails app runs entirely in the browser. MVC, Active Record, Active Storage, all offline-ready.
7️⃣ Real-World Use Cases
- Offline-First Apps: Journals, note-taking, or dashboards that work seamlessly offline.
- Rapid Prototyping & Demos: Share fully interactive Rails apps instantly via URL.
- Educational Playgrounds: Students experiment with Rails without installing Ruby or databases.
- Personal Data-Heavy Apps: Local dashboards combining analytics, file uploads, and complex logic.
- Media Management: Photo libraries stored in SQLite Wasm and OPFS, fully offline.
8️⃣ Performance Considerations
- Memory: Each VM ~300 MB.
- Compilation: Native extensions must be Wasm-compiled.
- Execution Speed: Slower than native for CPU-heavy tasks.
- Data Sync: Remote replication requires careful design.
- Initial Load: 70 MB Wasm bundle — PWA caching recommended.
Rails abstractions remain intact — same models, controllers, views.
9️⃣ Advanced Patterns
- In-Browser Background Jobs: Active Job via in-VM queues.
- Offline Sync: Queue offline mutations and sync later via Service Workers.
- Gem Testing & Experimentation: Load gems, run unit tests, experiment entirely in-browser.
Conclusion
WebAssembly transforms Rails into a portable, self-contained runtime. Backend logic, database queries, background jobs, and file management happen locally — opening new possibilities for offline-first apps, instant demos, and in-browser learning environments.
Rails on Wasm blurs the line between client and server. The tooling is maturing, constraints exist, but the developer experience remains familiar, and the potential is limitless.
