S SvelteKurs
M1 · Svelte-Komponenten

Markup & Ausdrücke

Das Markup einer Komponente ist HTML, angereichert um JavaScript-Ausdrücke in geschweiften Klammern. Attribute, Shorthand, Spread — und ein wichtiger Sicherheitshinweis zum @html-Tag.

Ausdrücke in geschweiften Klammern

Der Kern des Svelte-Markups ist einfach: Alles, was zwischen { } steht, ist ein JavaScript-Ausdruck. Svelte wertet ihn aus und rendert das Ergebnis als Text. Das ist nicht auf Variablen beschränkt — jeder gültige Ausdruck funktioniert.

Profil.svelte svelte
<script lang="ts">
  let name = $state('Anna');
  let punkte = $state(42);
</script>

<!-- Jeder JS-Ausdruck in { } wird ausgewertet und gerendert: -->
<h1>Hallo {name}</h1>
<p>Punkte: {punkte}</p>
<p>Verdoppelt: {punkte * 2}</p>
<p>Länge des Namens: {name.length}</p>
<p>Groß: {name.toUpperCase()}</p>

ℹ️ Ausdrücke, keine Anweisungen

In die Klammern gehört ein Ausdruck (etwas, das einen Wert ergibt), keine Anweisung. {punkte * 2} geht, {if (...) ...} nicht — für Bedingungen und Schleifen gibt es eigene Block-Tags, die wir uns in Modul M4 ansehen.

Ausdrücke in Attributen

Dieselben geschweiften Klammern funktionieren in Attributwerten. Du kannst ganze Attribute aus Ausdrücken setzen oder Text und Ausdruck innerhalb eines Strings mischen:

svelte
<script lang="ts">
  let url = $state('https://svelte.dev');
  let aktiv = $state(true);
</script>

<!-- Ausdrücke funktionieren auch in Attributen: -->
<a href={url}>Zur Doku</a>

<!-- Mischung aus Text und Ausdruck via String: -->
<img src="/bilder/{name}.png" alt="Bild von {name}" />

<!-- Boolesche Attribute: bei false wird das Attribut weggelassen -->
<button disabled={!aktiv}>Absenden</button>

Ein praktisches Detail: Ist der Wert eines booleschen Attributs wie disabled gleich false, lässt Svelte das Attribut komplett weg — genau das Verhalten, das HTML erwartet.

Attribut-Shorthand

Wenn ein Attribut denselben Namen hat wie die Variable, die es füllt, wird die Wiederholung schnell lästig (src={src}). Dafür gibt es eine Kurzform: Schreib einfach {src}.

svelte
<script lang="ts">
  let src = $state('/logo.svg');
  let value = $state('Hallo');
</script>

<!-- Lang: Attributname = Variablenname -->
<img src={src} alt="Logo" />
<input value={value} />

<!-- Shorthand: {src} ist die Kurzform von src={src} -->
<img {src} alt="Logo" />
<input {value} />

Spread: {...obj}

Hast du ein ganzes Objekt voller Attributwerte, musst du sie nicht einzeln durchreichen. Der Spread-Operator verteilt alle Felder eines Objekts als Attribute auf das Element — sehr nützlich, um Attribute an eine Kind-Komponente „durchzureichen":

svelte
<script lang="ts">
  const attrs = {
    type: 'text',
    placeholder: 'Dein Name',
    maxlength: 20,
    class: 'eingabe'
  };
</script>

<!-- Spread verteilt alle Objekt-Felder als Attribute: -->
<input {...attrs} />

<!-- Entspricht: -->
<input type="text" placeholder="Dein Name" maxlength={20} class="eingabe" />

<!-- Kombinierbar; spätere Attribute gewinnen: -->
<input {...attrs} placeholder="Überschrieben" />

Du kannst Spread und einzelne Attribute kombinieren. Bei gleichem Namen gewinnt das, was später steht.

Kommentare

Kommentare im Markup sind schlicht HTML-Kommentare. Sie bleiben Teil des Markups (nicht des <script>) und dürfen mehrzeilig sein:

svelte
<!-- Ein ganz normaler HTML-Kommentar. -->
<h1>{titel}</h1>

<!--
  Mehrzeilige Kommentare gehen auch.
  Sie bleiben im Markup, nicht im <script>.
-->

Text vs. HTML: Escaping & {@html}

Ein {ausdruck} gibt seinen Wert immer als Text aus. Enthält der Wert HTML-Tags, werden sie escapt und erscheinen als sichtbarer Text — nicht als Markup. Das ist die sichere Standardeinstellung.

Willst du eine Zeichenkette bewusst als echtes HTML rendern, brauchst du das Spezial-Tag {@html ...}:

svelte
<script lang="ts">
  let text = $state('Hallo <strong>Welt</strong>');
</script>

<!-- Standard: Text wird ESCAPT, Tags erscheinen als Text -->
<p>{text}</p>
<!-- Ausgabe: Hallo <strong>Welt</strong> (als sichtbarer Text) -->

<!-- {@html ...}: Inhalt wird als echtes HTML gerendert -->
<p>{@html text}</p>
<!-- Ausgabe: Hallo Welt (mit fettem "Welt") -->

🚫 Sicherheitshinweis: XSS-Gefahr

{@html} umgeht das Escaping vollständig. Gibst du damit Inhalte aus, die von Nutzern stammen (Kommentare, Formulareingaben, API-Daten), öffnest du Tür und Tor für Cross-Site-Scripting (XSS). Nutze {@html} nur für Inhalte, denen du vollständig vertraust — oder bereinige sie vorher mit einem Sanitizer wie DOMPurify. Die weiteren Spezial-Tags behandeln wir in Modul M4.

🛤️ Rails-Vergleich: <%= %>, h() und html_safe

Das kennst du aus ERB: <%= @name %> escapt seinen Inhalt automatisch — genau wie {ausdruck} in Svelte. Der Helper h() escapt explizit, und mit raw bzw. html_safe umgehst du das Escaping bewusst.

view.html.erb ruby
<%# ERB: <%= %> gibt escapten Text aus (wie {ausdruck} in Svelte) %>
<h1>Hallo <%= @name %></h1>
<p>Punkte: <%= @punkte * 2 %></p>

<%# Escaping ist auch hier Standard. Zum bewussten Umgehen: %>
<%= raw @html_inhalt %>          <%# oder @html_inhalt.html_safe %>
<%# Rails-Helper h() escapt explizit: %>
<%= h @user_input %>

{@html} ist damit das direkte Gegenstück zu Rails' raw/html_safe — mit derselben Verantwortung: Was du hindurchlässt, muss vertrauenswürdig sein.

Live ausprobieren

Unten steht ein Objekt produkt im $state. Die Anzeige rechnet und formatiert live mit — ändere die Felder und beobachte jeden Ausdruck im Markup:

▶ Objekt-Felder live im Markup · Interaktiv

Svelte-Tasse

Preis: 12.00 €

Status: ✅ Auf Lager

Doppelpack: 24.00 €

Der Code hinter der Anzeige — nur Ausdrücke, keine Block-Logik:

Produkt.svelte svelte
<script lang="ts">
  let produkt = $state({
    name: 'Svelte-Tasse',
    preis: 12,
    verfuegbar: true
  });
</script>

<article>
  <h3>{produkt.name}</h3>
  <p>Preis: {produkt.preis.toFixed(2)} €</p>
  <p>Status: {produkt.verfuegbar ? 'Auf Lager' : 'Ausverkauft'}</p>
  <p>Doppelpack: {(produkt.preis * 2).toFixed(2)} €</p>
</article>

Zusammenfassung

💡 Das Wichtigste

  • {ausdruck} rendert jeden JS-Ausdruck als escapten Text.
  • Ausdrücke funktionieren auch in Attributen; false lässt Attribute weg.
  • Shorthand: {src} statt src={src}.
  • Spread {...obj} verteilt Objekt-Felder als Attribute (später gewinnt).
  • {@html} rendert rohes HTML — nur bei vertrauenswürdigem Inhalt (XSS!).

🎯 Probier es selbst

Erweitere die Produkt-Demo um ein Feld rabatt (0–50, als Slider) im $state. Zeige im Markup den reduzierten Preis mit einem Ausdruck an, z. B. {(produkt.preis * (1 - rabatt / 100)).toFixed(2)} €. Alles bleibt ein einziger Ausdruck — ganz ohne {#if}.