Svelte kompiliert Komponenten zu nativem JavaScript ohne Runtime-Overhead. In Kombination mit Rails 7 entsteht eine Architektur, die das bewährte Rails-Backend mit einem minimal schlanken Frontend verbindet. Aber ist das immer die richtige Wahl? In diesem Artikel zeigen wir das konkrete Setup, vergleichen Svelte mit dem Rails-eigenen Hotwire/Turbo-Stack und teilen unsere Erfahrungen aus realen Projekten.

Svelte auf Rails mit Tailwind | Ruby on Rails 7

Wann lohnt sich Svelte statt Hotwire/Turbo?

Rails 7 liefert mit Hotwire (Turbo + Stimulus) bereits ein leistungsfähiges Frontend-Toolkit. Bevor Sie Svelte einbinden, sollten Sie ehrlich prüfen, ob Ihr Projekt es wirklich braucht.

Hotwire/Turbo ist die bessere Wahl, wenn:

  • Ihre App vorwiegend serverseitig gerenderte Seiten mit wenig Client-Interaktivität hat
  • Sie schnelle Turnaround-Zeiten brauchen und das Team bereits Rails kennt
  • CRUD-Operationen mit Live-Updates (Turbo Streams) den Grossteil der UI ausmachen

Svelte lohnt sich, wenn:

  • Komplexe, zustandsbehaftete UI-Komponenten nötig sind (Dashboards, Drag-and-Drop, Echtzeit-Editoren)
  • Sie eigenständige Widgets bauen, die unabhängig vom Rails-Rendering arbeiten
  • Bundle-Grösse kritisch ist: Eine typische Svelte-Komponente kompiliert zu 2-5 KB gzip, während React-Äquivalente 40-50 KB Runtime mitbringen

USEO’s Take: Zwei Ökosysteme bedeuten doppelte Wartungskosten

Aus unserer Projekterfahrung bei USEO: Svelte in eine Rails-App einzubinden funktioniert technisch einwandfrei. Die eigentliche Herausforderung liegt im langfristigen Betrieb. Sie pflegen zwei Dependency-Bäume (Gemfile + package.json), zwei Build-Pipelines und brauchen Entwickler, die beide Welten verstehen.

In einem Kundenprojekt haben wir ein Dashboard mit ca. 15 Svelte-Komponenten in einer Rails 7.1 App betrieben. Die initialen Bundle-Grössen waren hervorragend (gesamtes Svelte-Bundle: 38 KB gzip vs. geschätzte 120+ KB mit React). Aber jedes Rails-Upgrade erforderte auch die Prüfung der Node-Toolchain-Kompatibilität. Unser Rat: Setzen Sie Svelte gezielt für die Teile ein, wo Hotwire nicht ausreicht, statt die gesamte App damit zu bauen.

Voraussetzungen: Versionen und Werkzeuge

WerkzeugVersionHinweis
Ruby>= 3.2.0Empfohlen: 3.3.x für YJIT-Performance
Rails>= 7.1.07.0 funktioniert, aber 7.1 hat bessere ESBuild-Integration
Node.js>= 18.0 (LTS)Node 16 ist EOL seit September 2023
npm>= 9.0Wird mit Node 18 ausgeliefert
Bundler>= 2.4gem install bundler
ESBuild>= 0.19Wird über jsbundling-rails installiert
svelte-on-rails>= 1.0.0Das zentrale Integrations-Gem

Prüfen Sie Ihre Versionen:

ruby --version    # >= 3.2.0
rails --version   # >= 7.1.0
node --version    # >= 18.0.0
npm --version     # >= 9.0.0

Rails-Projekt mit ESBuild erstellen

rails new svelte_rails_app --javascript=esbuild --css=tailwind
cd svelte_rails_app
bundle install

Warum --javascript=esbuild? ESBuild kompiliert JavaScript 10-100x schneller als Webpack. Ein typischer Svelte-Build mit 20 Komponenten dauert unter 200ms.

Warum Tailwind statt Bootstrap? Tailwind generiert nur genutztes CSS per PurgeCSS. In Kombination mit Svelte ergibt das ein Gesamt-CSS-Bundle von ca. 8-12 KB gzip.

Ihre Gemfile sollte diese Gems enthalten:

gem 'jsbundling-rails'   # ~> 1.3
gem 'turbo-rails'        # ~> 2.0
gem 'stimulus-rails'     # ~> 1.3

Svelte installieren und konfigurieren

Gem und npm-Pakete hinzufügen

bundle add svelte-on-rails
rails generate svelte_on_rails:install
npm install svelte@4 esbuild-svelte@0.8

Die Installation erstellt die nötige ESBuild-Konfiguration und das Komponentenverzeichnis.

Build-Konfiguration prüfen

Nach der Installation sollte esbuild.config.mjs einen Svelte-Plugin-Eintrag enthalten:

import sveltePlugin from 'esbuild-svelte';
import sveltePreprocess from 'svelte-preprocess';

export default {
  plugins: [
    sveltePlugin({
      preprocess: sveltePreprocess(),
    }),
  ],
};

Starten Sie den Dev-Server und prüfen Sie, ob der Build fehlerfrei durchläuft:

bin/dev

Der Procfile.dev startet sowohl Rails als auch den ESBuild-Watcher.

Erste Testkomponente

Erstellen Sie app/frontend/javascript/components/HelloWorld.svelte:

<script>
  export let name = 'Welt';
</script>

<div class="hello">
  <h2>Hallo {name}</h2>
  <p>Svelte läuft in Rails.</p>
</div>

<style>
  .hello {
    padding: 1rem;
    border: 1px solid #e2e8f0;
    border-radius: 0.5rem;
    margin: 1rem 0;
  }
</style>

In einer Rails-View einbinden:

<%= svelte_component('HelloWorld', { name: 'Rails-Entwickler' }) %>

Daten zwischen Rails und Svelte austauschen

Der häufigste Anwendungsfall: Rails liefert Daten per Props an Svelte-Komponenten.

Controller vorbereiten

class DashboardController < ApplicationController
  def show
    @metrics = {
      revenue: current_company.monthly_revenue,
      users: current_company.active_users_count,
      uptime: current_company.uptime_percentage,
      last_updated: Time.current.iso8601
    }
  end
end

Svelte-Komponente mit Props

<script>
  export let revenue;
  export let users;
  export let uptime;
  export let lastUpdated;

  const formatCurrency = (amount) =>
    new Intl.NumberFormat('de-CH', {
      style: 'currency',
      currency: 'CHF'
    }).format(amount);

  const formatDate = (iso) =>
    new Intl.DateTimeFormat('de-CH', {
      day: '2-digit',
      month: '2-digit',
      year: 'numeric',
      hour: '2-digit',
      minute: '2-digit'
    }).format(new Date(iso));
</script>

<div class="dashboard-card">
  <h3>Monatsübersicht</h3>
  <ul>
    <li>Umsatz: {formatCurrency(revenue)}</li>
    <li>Aktive Nutzer: {users.toLocaleString('de-CH')}</li>
    <li>Uptime: {uptime}%</li>
  </ul>
  <small>Stand: {formatDate(lastUpdated)}</small>
</div>

View

<%= svelte_component('DashboardCard', @metrics) %>

Beachten Sie: svelte-on-rails konvertiert Ruby snake_case automatisch zu JavaScript camelCase. Aus last_updated wird lastUpdated.

Bundle-Grössen im Vergleich

Wir haben die Bundle-Grössen für ein identisches Dashboard (5 Komponenten, Chart-Rendering, Echtzeit-Updates) gemessen:

StackJS Bundle (gzip)CSS Bundle (gzip)Build-Zeit
Svelte 4 + ESBuild22 KB4 KB180ms
React 18 + ESBuild68 KB4 KB320ms
Vue 3 + Vite54 KB5 KB450ms
Hotwire/Turbo + Stimulus18 KB4 KB90ms

Hotwire ist kleiner, bietet aber keine Client-seitige Reaktivität für komplexe UI-Logik. Svelte liegt nah an Hotwire bei der Grösse, bietet aber ein vollständiges Komponentenmodell.

Ordnerstruktur und Konventionen

app/
├── frontend/
│   └── javascript/
│       ├── components/        # Svelte-Komponenten (PascalCase)
│       │   ├── DashboardCard.svelte
│       │   ├── UserProfile.svelte
│       │   └── shared/        # Wiederverwendbare Basis-Komponenten
│       │       └── Modal.svelte
│       └── utils/             # Hilfsfunktionen
│           └── formatters.js
├── views/                     # Rails ERB Templates
└── assets/                    # Rails Asset Pipeline

Halten Sie Svelte-Komponenten in PascalCase und gruppieren Sie wiederverwendbare Teile in einem shared/ Unterverzeichnis. Für Utility-Funktionen (Formatierung, API-Calls) nutzen Sie utils/.

Typische Probleme und deren Lösung

ESBuild findet Svelte-Dateien nicht

Prüfen Sie den entryPoints-Pfad in esbuild.config.mjs. Er muss auf das Verzeichnis zeigen, das Ihre Svelte-Imports enthält.

HMR funktioniert nicht korrekt

ESBuild unterstützt kein vollständiges HMR für Svelte. Der Watcher erkennt Änderungen und baut neu, aber der Browser lädt die ganze Seite. Für echtes HMR brauchen Sie Vite statt ESBuild:

rails new my_app --javascript=esbuild  # Kein natives HMR
# Alternative mit Vite:
bundle add vite_rails
npm install vite @sveltejs/vite-plugin-svelte

Der Nachteil von Vite: zusätzliche Konfigurationskomplexität. Für die meisten Projekte reicht der ESBuild-Watcher mit Page Reload.

Turbo und Svelte kollidieren

Turbo ersetzt den <body> bei Navigation, was Svelte-Komponenten zerstört. Lösung: Komponenten bei turbo:load neu initialisieren oder data-turbo="false" auf Links setzen, die zu Seiten mit Svelte-Komponenten führen.

document.addEventListener('turbo:load', () => {
  // Svelte-Komponenten hier neu mounten
});

Fazit: Svelte gezielt statt pauschal einsetzen

Svelte in Rails 7 funktioniert. Die Integration über svelte-on-rails und ESBuild ist stabil, die Bundle-Grössen sind exzellent. Aber der Rails-Weg mit Hotwire deckt 80% der typischen Anforderungen ab, ohne ein zweites Ökosystem einzuführen.

Unsere Empfehlung: Starten Sie mit Hotwire/Turbo. Wenn Sie an die Grenzen von Stimulus stossen (komplexe Client-Logik, verschachtelte Zustände, Drag-and-Drop), binden Sie Svelte für genau diese Komponenten ein. So halten Sie die Wartungskosten niedrig und profitieren trotzdem von Sveltes Compiler-Vorteilen.

FAQs

Brauche ich Svelte, wenn ich Rails 7 mit Hotwire nutze?

In den meisten Fällen nicht. Hotwire (Turbo + Stimulus) deckt Navigation, Formulare und einfache Interaktivität ab. Svelte lohnt sich erst bei zustandsbehafteter Client-Logik: Dashboards, interaktive Editoren oder Drag-and-Drop-Interfaces.

Wie gross ist der Wartungsaufwand für beide Stacks?

Sie pflegen zwei Dependency-Bäume: Ruby-Gems (Bundler) und npm-Pakete (Node). Jedes Rails-Upgrade erfordert auch die Prüfung der JavaScript-Toolchain. Bei USEO planen wir dafür ca. 20% mehr Zeit pro Major-Upgrade ein.

Kann ich Svelte-Komponenten schrittweise einführen?

Ja, das ist der empfohlene Weg. Sie können einzelne Svelte-Komponenten in bestehende ERB-Views einbetten, ohne die gesamte Anwendung umzubauen. Jede Komponente ist eigenständig und hat keine Abhängigkeiten zu anderen Svelte-Teilen.

Welche Node-Version brauche ich mindestens?

Node 18 LTS oder neuer. Node 16 hat seit September 2023 keinen Support mehr. Für Svelte 4 empfehlen wir Node 20 LTS, das bis April 2026 unterstützt wird.

Verwandte Artikel