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:

TypeScript JSX im Quellcode
const greeting = <h1 className="title">Hallo, Welt</h1>;

… macht der Compiler diesen JavaScript-Aufruf:

TypeScript Nach dem Compile-Schritt
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.

TypeScript − Falsch
function Profile() {
    return (
        <h1>Titel</h1>
        <p>Beschreibung</p>
    );
}
TypeScript + Mit Wrapper-Div
function Profile() {
    return (
        <div>
            <h1>Titel</h1>
            <p>Beschreibung</p>
        </div>
    );
}

Wenn du keinen zusätzlichen DOM-Knoten erzeugen willst, nutze ein Fragment (<>...</>):

TypeScript + Mit 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:

TypeScript − Falsch (HTML-Stil)
<img src="logo.png">
<br>
<input type="text">
TypeScript + JSX-konform
<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:

HTMLJSXHinweis
classclassNameclass ist in JS reserviert
forhtmlForfor ist in JS reserviert
tabindextabIndexcamelCase
onclickonClickEvent-Handler immer camelCase
stroke-widthstrokeWidthSVG-Attribute camelCase
aria-labelaria-labelAusnahme: aria-* bleibt
data-iddata-idAusnahme: 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.

TypeScript Variablen, Aufrufe, Ausdrücke
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.

TypeScript Inline-Styles
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.

TypeScript Statisch und dynamisch gemischt
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}:

TypeScript Boolesche Attribute
// 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:

TypeScript Kommentare
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 />.

TypeScript − Bug: 0 wird gerendert
{users.length && <UserList users={users} />}
TypeScript + Sicher
{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.

TypeScript Ohne JSX
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.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Grundlagen

Zur Übersicht