JSX ist die Syntax-Erweiterung, mit der React-Komponenten Markup direkt in JavaScript schreiben können. Auf den ersten Blick sieht JSX aus wie HTML – es ist aber JavaScript mit ein paar zusätzlichen Regeln. In diesem Artikel lernst du, wie JSX funktioniert, welche Regeln du beachten musst, wo es sich von HTML unterscheidet und wie JavaScript-Ausdrücke direkt im Markup eingebettet werden.
Was ist JSX?
JSX steht für JavaScript XML. Es ist eine Syntax-Erweiterung für JavaScript, die es erlaubt, HTML-ähnliches Markup direkt im Code zu schreiben. JSX ist optional – React lässt sich auch ohne JSX nutzen – aber praktisch jedes React-Projekt verwendet es, weil der Code damit deutlich lesbarer wird.
JSX wird zur Build-Zeit von einem Compiler (typischerweise Babel oder SWC) in normale JavaScript-Funktionsaufrufe übersetzt. Aus folgendem JSX:
const greeting = <h1 className="title">Hallo, Welt</h1>;… macht der Compiler diesen JavaScript-Aufruf:
const greeting = React.createElement(
'h1',
{ className: 'title' },
'Hallo, Welt'
);JSX ist also nur syntaktischer Zucker für createElement-Aufrufe. Im Browser läuft am Ende immer JavaScript.
Die drei wichtigsten Regeln
Regel 1 – Genau ein Wurzel-Element
Jeder JSX-Block, der von einer Funktion zurückgegeben wird, muss genau ein umschließendes Element haben. Mehrere Geschwister-Elemente ohne Wrapper sind nicht erlaubt.
function Profile() {
return (
<h1>Titel</h1>
<p>Beschreibung</p>
);
}function Profile() {
return (
<div>
<h1>Titel</h1>
<p>Beschreibung</p>
</div>
);
}Wenn du keinen zusätzlichen DOM-Knoten erzeugen willst, nutze ein Fragment (<>...</>):
function Profile() {
return (
<>
<h1>Titel</h1>
<p>Beschreibung</p>
</>
);
}Der Hintergrund: Eine Funktion kann nur einen Wert zurückgeben. Da JSX zu Funktionsaufrufen kompiliert wird, braucht es immer eine einzelne Wurzel.
Regel 2 – Alle Tags müssen geschlossen sein
In HTML sind Self-Closing-Tags wie <br> oder <img> ohne abschließenden Schrägstrich erlaubt. In JSX nicht:
<img src="logo.png">
<br>
<input type="text"><img src="logo.png" />
<br />
<input type="text" />Regel 3 – camelCase für Attribute
Da JSX zu JavaScript wird, dürfen Attribute keine reservierten JavaScript-Schlüsselwörter sein und keine Bindestriche enthalten (Bindestriche wären in JS-Property-Namen ein Minus). Daher gilt:
| HTML | JSX | Hinweis |
|---|---|---|
class | className | class ist in JS reserviert |
for | htmlFor | for ist in JS reserviert |
tabindex | tabIndex | camelCase |
onclick | onClick | Event-Handler immer camelCase |
stroke-width | strokeWidth | SVG-Attribute camelCase |
aria-label | aria-label | Ausnahme: aria-* bleibt |
data-id | data-id | Ausnahme: data-* bleibt |
JavaScript in JSX einbinden
In JSX kannst du jeden gültigen JavaScript-Ausdruck in geschweifte Klammern setzen. Was zwischen { } steht, wird zur Render-Zeit ausgewertet.
function Profile() {
const name = 'Anna';
const age = 30;
return (
<div>
<h1>{name}</h1>
<p>{name.toUpperCase()}</p>
<p>Geboren: {2026 - age}</p>
<p>{age >= 18 ? 'Volljährig' : 'Minderjährig'}</p>
</div>
);
}Wichtig: In { } sind nur Ausdrücke erlaubt – also alles, was einen Wert ergibt. Anweisungen wie if, for oder let funktionieren dort nicht. Für Bedingungen nimmt man den Ternär-Operator oder &&, für Schleifen die Methode map() (siehe Conditional Rendering und Schleifen).
Doppelte Klammern für Objekte
Möchtest du ein Objekt einbinden – etwa für Inline-Styles – brauchst du zwei Klammern: die äußeren für JSX, die inneren für das Objekt-Literal.
function Box() {
return (
<div style={{ padding: '1rem', background: '#eee' }}>
Hallo
</div>
);
}CSS-Properties werden in JSX-Style-Objekten ebenfalls in camelCase geschrieben (backgroundColor statt background-color).
Attribute: statisch vs. dynamisch
Statische Werte werden wie in HTML in Anführungszeichen gesetzt. Dynamische Werte kommen in geschweifte Klammern.
function Avatar({ user }) {
return (
<img
src={user.avatarUrl}
alt={`Avatar von ${user.name}`}
width="120"
height="120"
className="avatar"
/>
);
}Boolesche Attribute funktionieren wie in HTML – ein einzelnes Attribut ohne Wert ist gleichbedeutend mit attribut={true}:
// Gleichwertig:
<input disabled />
<input disabled={true} />
// Dynamisch:
<input disabled={isLoading} />Kommentare in JSX
JSX-Kommentare werden ebenfalls in geschweiften Klammern als JavaScript-Block-Kommentare geschrieben:
function Page() {
return (
<main>
{/* Das ist ein JSX-Kommentar */}
<h1>Titel</h1>
</main>
);
}Außerhalb von JSX (im normalen Funktions-Code) gelten die normalen JavaScript-Kommentare // und /* */.
Häufige Stolperfallen
class statt className
Der Klassiker. Das Markup rendert teils, aber der CSS-Selektor greift nicht – React warnt in der Konsole.
Mehrere Wurzel-Elemente ohne Wrapper
Erkennt der Compiler sofort: „Adjacent JSX elements must be wrapped in an enclosing tag."
Großbuchstaben für eigene Komponenten
Tags, die mit einem Kleinbuchstaben beginnen, werden als HTML-Element interpretiert. Tags mit Großbuchstaben als React-Komponente. <button> ist ein HTML-Button, <Button> ist deine eigene Komponente. Verwechslungen führen zu schwer auffindbaren Fehlern.
Strings ohne Klammern
<p>Hallo {name}</p> ist korrekt. <p>Hallo name</p> rendert das Wort „name" als Text.
Whitespace in geschweiften Klammern
React rendert false, null, undefined und true nicht. Aber 0 wird gerendert – ein häufiger Fehler bei {users.length && <List />}. Bei leerer Liste rendert React die Zahl 0. Besser: explizit casten mit users.length > 0 && <List />.
{users.length && <UserList users={users} />}{users.length > 0 && <UserList users={users} />}JSX ohne JSX – warum man das wissen sollte
Da JSX nur Zucker für createElement ist, lässt sich derselbe Baum auch komplett ohne JSX schreiben. Praktisch wird das nie genutzt, ist aber gut zu kennen, weil es das mentale Modell schärft.
import { createElement } from 'react';
function Greeting() {
return createElement(
'div',
{ className: 'box' },
createElement('h1', null, 'Hallo'),
createElement('p', null, 'Welt')
);
}Die Signatur lautet createElement(type, props, ...children). Genau das, was JSX im Hintergrund tut.