Svelte on Rails
Combining two beautiful worlds: Rails backend power and Svelte frontend joy.
For most interfaces, Hotwire is already a great choice. But when you need richer frontend interactivity, Svelte can be the perfect lightweight layer on top of your Rails app.
There are two important hurdles to understand when using Svelte in a Rails environment.
First hurdle: translations and other Rails-side data must be passed into Svelte components through props. To simplify this, the to_svelte helper is provided.
Second hurdle: Svelte components must be compatible with server-side rendering.
This means you cannot use browser-only APIs such as window or document in code that runs during server-side rendering. The same applies to directly imported modules that use those APIs. If browser-only code runs later, for example after a button click, that is usually fine.
A simple test while developing a component is to reload the page. If you do not see a blinking error message, you are good to go.
Because of this, building a Svelte component in a Hotwire-based Rails environment is not quite as simple as building one in SvelteKit.
The advantage is that you can keep delivering a classic, backend-powered Rails app — with backend-driven tests — while adding rich interactivity exactly where you need it, beyond what Hotwire can provide.
Installation + optional Showcase
Copy, paste, and run the following commands in your terminal:
1. Create a new Rails app
If you start from scratch:
rails new svelte-on-rails-testapp app --skip-javascript && cd svelte-on-rails-testapp
2. Install svelte-on-rails
bundle add svelte-on-rails
rails g svelte_on_rails:install
The installer is designed to be carefully and safe:
- it checks prerequisites
- it uses an isolated sandbox for checking npm dependencies before applying changes to the app.
- it won’t overwrite existing files without asking
Just follow the prompts.
3. Install and run the Showcase
rails g svelte_on_rails:showcase
- Restart your app
- open Browser on localhost:3000/svelte_on_rails_showcase
Additional: On Models, where you want to use the to_svelte helper, add exposes_to_svelte, for example:
class Book < ApplicationRecord
exposes_to_svelte
end
Code Example
View helper:
<%= svelte('components/Stats', user: @user.to_svelte(:login_count, :name) %>
Component:
app/frontend/javascript/components/User.svelte:
<script>
let { user } = $props();
</script>
<h1>{ user._schema.self.name } { user.name } { user._schema.login_count.label }: { user.login_count }</h1>
Output:
<h1>Benutzer Josef Logins: 77</h1>
<!--
the #to_svelte helper
• returns useful meta-data for models and columns
• serializes data
-->
On the server logs, with the «Completed Line»:
17:35:52 web.1 | [SOR] ...
17:35:52 web.1 | [SOR] Stats.svelte returned from cache (1.4ms)
17:35:52 web.1 | Completed 200 OK in 43ms ( ... | ... | Svelte: 2 cached, 1 rendered, 3.3ms)
# or
12:36:45 web.1 | Completed 200 OK in 831ms ( ... | ... | SVELTE BUILD FAILED!)
# or
12:36:45 web.1 | Completed 200 OK in 831ms ( ... | ... | Svelte: build, 3 warnings, 3 cached, 786.7ms)
# or
12:35:22 web.1 | Completed 200 OK in 44ms ( ... | ... | Svelte: 3 cached, 2.1ms)
When you see something like «SVELTE BUILD FAILED» you will find a detailed error message with the links to the files.
Before You Build Your First App
- Check out the Hotwire Svelte Helpers Example App:
- This set of helpers makes it easy to create dropdowns, tooltips, and modals with Svelte and Stimulus.
- It includes form helpers for Svelte that interact with the Rails backend.
- Z-index issues are solved by configurable rendering into a root-level container.
- Consider using Svelte components within
app/views, just like partials:- See configuration
- Testing:
- For testing we use this Playwright setup.
- Important: The gem adds the class
hydratedto every component after it has finished hydrating. Append it to your selectors so tests don’t interact with a component before its event listeners are attached, for example:page.wait_for_selector(".my-component.hydrated .any-button").click - This aligns to the
.stimulus-connectedclass of@csedl/hotwire-svelte-helpers. - Important: The
hydratedclass is added to the root element of the Svelte component. This is different from thesvelte:optionstagoption,
- Performance:
- Add the
redisgem to your Gemfile and setuse_caching: trueinconfig/svelte_on_rails.yml. - Use
watch_changes: truein development only. - Check uncached props.
- Add the
Why?
Svelte`s reactivity is the missing piece for Rails+Hotwired.
Why was Svelte invented? Watch the Rethinking Reactivity video (especially 5:00–7:10).
Features
- Server-Side Rendering
- SSR Server (Default / similar to
react_on_rails pro)- Fine-grained fragment caching
- ActiveRecord integration – pass models directly with
.to_svelte- optional support for placing
.sveltecomponents directly inapp/views- Plays nicely with Hotwire Turbo (option: SSR only for initial page load)
- Fully integrated with
rails assets:precompile- Built-in interface for server → client communication via ActionCable / Turbo Streams
- Fully configurable
- Detailled logging for performance optimization and error detection
Enjoy it! 😉
For Questions or Issues, please open an issue on GitLab.
Best Regards,
Chris