Eine Markdown-Tabelle von Grund auf bauen, ohne Striche zu zählen
Veröffentlicht am 30.7.2026 · 11 Min. Lesezeit · Entwickler-Tools
Daniel Okonkwo — Front-end-Entwickler und Tech-Redakteur bei OneKitly
Web-Performance · Dateiformate
Anhand von 4 Quellen geprüft
Die kleinste gültige Markdown-Tabelle sind zwei Zeilen, nicht drei: eine Kopfzeile und darunter eine Trennzeile. | Name | Rolle | gefolgt von | --- | --- | ist eine vollständige, gültige GitHub-Tabelle mit Kopf und ohne Körper — die Spezifikation sagt das ausdrücklich, und das erzeugte HTML hat schlicht kein tbody. Weglassen darfst du die Trennzeile nicht. Eine Kopfzeile für sich ist ein Absatz mit Pipes darin und erscheint als wörtlicher Text. Diese Zeile ist weder Zierde noch Ausrichtungszucker: In GitHub Flavored Markdown ist sie das Signal, das einen gewöhnlichen Absatz in eine Tabelle verwandelt, und deshalb ist sie hier Pflicht, während Renderer mit anderer Tabellensyntax ohne sie auskommen. Daraus folgen zwei Regeln. Die Trennzeile muss genau so viele Zellen haben wie die Kopfzeile, sonst wird die Tabelle gar nicht erkannt. Und jede andere Zeile darf beliebig viele Zellen haben: zu wenige, dann werden leere eingefügt; zu viele, dann wird der Überschuss verworfen. Führende und abschließende Pipes sind optional — die Spezifikation empfiehlt sie der Klarheit halber — und die Leerzeichen, die die Spalten ausrichten, wirken sich auf die Ausgabe überhaupt nicht aus. Pipe-Tabellen gehören nicht zu CommonMark. Die Kernspezifikation, Version 0.31.2, definiert Leaf- und Container-Blöcke und enthält gar kein Tabellenkonstrukt; GitHubs Tabellen sind eine Erweiterung darauf. Eine Tabelle, die im Pull Request tadellos aussieht, kann in einem strengen CommonMark-Prozessor als Rohtext erscheinen, und das ursprüngliche Markdown von 2004 hatte ebenfalls keine Tabellen. Bevor du eine schreibst, sieh nach, welcher Renderer sie liest.
Das Kleinste, das noch eine Tabelle ist, sind zwei Zeilen: eine Kopfzeile und eine Trennzeile. Hier steht, warum die zweite in GitHub Flavored Markdown Pflicht ist, wo Pipe-Tabellen überhaupt nicht existieren, und was ein Generator kann, was Tippen von Hand nicht kann.
Das Kleinste, das noch eine Tabelle ist
Tippe eine Zeile in den Generator — Name,Rolle — und er erzeugt zwei: | Name | Rolle | oben, | --- | --- | darunter. Das ist die ganze Tabelle. Sie hat eine Kopfzeile, keine Datenzeilen, und sie ist gültig: Die Spezifikation von GitHub Flavored Markdown enthält ein Beispiel genau dieser Form und merkt nur an, dass im HTML kein tbody-Element erzeugt wird. Es fehlt nichts. Ob das nützlich ist, steht auf einem anderen Blatt, aber die Syntax ist vollständig.
Lösche jetzt die zweite Zeile, und du hast nichts. Eine Kopfzeile für sich ist ein Absatz, der zufällig senkrechte Striche enthält, und jeder GFM-Renderer druckt sie genau so: Name | Rolle, als Text, Pipes inklusive. Das ist der häufigste Grund, warum eine von Hand getippte Tabelle scheitert, und der Grund ist strukturell, nicht stilistisch. Ein Markdown-Parser entscheidet über die Art eines Blocks anhand seines Anfangs. Eine Raute macht eine Überschrift, ein Größer-als-Zeichen ein Zitat, vier Leerzeichen Code. Ein Absatz mit Pipes könnte alles sein: eine Shell-Pipeline, eine Wahrheitstabelle, ein Stück Prosa über Wahrscheinlichkeit. Die Trennzeile ist das Einzige, was dem Parser sagt, dass genau dieser Absatz eine Tabelle ist, und ohne sie lässt es sich nicht erschließen.
Der Generator kann sie nicht vergessen, und darin liegt der größte Teil seines Nutzens. Er baut Kopfzeile, Trennzeile und jede Datenzeile aus einer einzigen Spaltenzahl, sodass die drei nie auseinanderlaufen können. Gib ihm eine Kopfzeile ohne Körper, und er gibt die zweizeilige Tabelle aus. Schalte „erste Zeile ist die Kopfzeile“ ab, und er erfindet Spalte 1, Spalte 2 und so fort, denn die Syntax kennt keine kopflose Form, und Namen zu erfinden ist das einzig Ehrliche, was bleibt. Gib ihm Zeilen ungleicher Länge, und er richtet sie aus, füllt die kurzen mit leeren Zellen und sagt dir über der Ausgabe, wie viele Zeilen er auffüllen musste.
Wo eine Pipe-Tabelle schlicht nicht existiert
Markdown, wie John Gruber es 2004 veröffentlichte, hat keine Tabellen. Lies das ursprüngliche Syntaxdokument, und du findest Überschriften, Zitate, Listen, Codeblöcke, waagerechte Linien, Links, Hervorhebung, Bilder und Inline-HTML — und nichts über Spalten. Tabellen waren nie darin, und der Notausgang, den das Dokument für alles anbietet, was es nicht abdeckt, ist rohes HTML zu schreiben.
CommonMark, der Versuch, Markdown eine eindeutige Spezifikation zu geben, hat sie nicht ergänzt. Version 0.31.2 vom Januar 2024 definiert Leaf-Blöcke — thematische Trenner, Überschriften, Codeblöcke, HTML-Blöcke, Absätze — und Container-Blöcke — Blockzitate, Listenpunkte, Listen — und es gibt keinen Tabellenabschnitt. Die Spezifikation räumt selbst ein, dass manche Dialekte die ursprüngliche Syntax um Konventionen für Fußnoten und Tabellen erweitert haben, womit Tabellen klar außerhalb des Kerns liegen. GitHubs Tabellen stehen in einem eigenen Dokument, der GFM-Spezifikation, unter der Überschrift Tables (extension).
Die praktische Folge ist, dass es von den eingeschalteten Erweiterungen des Prozessors abhängt, wo deine Tabelle rendert, und nicht von Markdown als solchem. Eine Repository-README auf GitHub ist sicher. Ein Static-Site-Generator, ein Doku-Build, ein Chat-Client, ein Kommentarfeld in einem Bugtracker: Jedes davon ist eine eigene Entscheidung dessen, der es zusammengesetzt hat. Wenn das Ziel wichtig ist, dauert der Test zwei Minuten — füge dort die zweizeilige Minimaltabelle ein und schau hin. Erscheint sie als Tabelle mit einer Kopfzeile, ist die Erweiterung an und alles Weitere funktioniert; erscheint sie als Textzeile mit Strichen darunter, schreibe stattdessen das HTML oder nimm eine Liste.
Was der Generator zählt und du nicht
Die Trennzeile hat einen Boden von drei Zeichen, und der ist nicht willkürlich: :-: ist die kürzeste Zelle, die noch an jedem Ende einen Ausrichtungsmarker tragen kann. Der Generator wendet diesen Boden auf jede Spalte an, sodass eine einbuchstabige Kopfzelle als | a | über | --- | herauskommt, nie über | - |. Er berechnet außerdem die Breite jeder Spalte aus der breitesten Zelle dieser Spalte, Kopfzelle eingeschlossen — genau die Rechnerei, die niemand bei einer Tabelle mit zwölf Zeilen von Hand machen will.
Interessant ist, wie er eine Zelle misst, denn ein Zeichen ist keine Spalte. Dieser Generator zählt Anzeigespalten, keine Zeichen: Die Ideogramme 東京 sind zwei Zeichen, und er zählt vier; ein é aus e plus kombinierendem Akzent sind zwei Zeichen, und er zählt eins; ein einzelnes Emoji sind zwei UTF-16-Einheiten, und er zählt zwei. Zeichen zu zählen ist das, was ein naiver Generator tut, und es verzieht die Quelle, sobald die Daten das lateinische Alphabet verlassen. Das Familien-Emoji ist der Fall, an dem naive Breitenzähler scheitern: vier Figuren, durch Zero-Width-Joiner zusammengeschweißt, sieben Codepunkte, elf UTF-16-Einheiten und eine einzige Glyphe von zwei Spalten Breite. Dieser Generator meldet zwei, denn ein Zero-Width-Joiner belegt keine eigene Spalte und kostet den Codepunkt danach seine eigene dazu. Nichts davon erreicht die gerenderte Tabelle — die Polsterung ist dafür da, dass die Quelle im Editor gerade steht, und nur dort kann sie richtig oder falsch sein.
Es gibt einen kompakten Modus, der das Auffüllen ganz abschaltet. Damit behält die Trennzeile ihre drei Zeichen — | :-- | :-: | --: | für links, mittig und rechts — und jede andere Zelle wird ohne jede Polsterung geschrieben. Das ist die Einstellung, die du für eine breite Tabelle in einer versionierten Datei willst, denn eine aufgefüllte Tabelle setzt ihre ganze Spalte neu, sobald ein Wert länger wird, und eine Ein-Wort-Korrektur wird zu einem Diff, der jede Zeile berührt.
Eine bestehende Tabelle bearbeiten
Der Generator nimmt eine bestehende Markdown-Tabelle als Eingabe, was der schnellste Weg ist, eine Spalte zu ergänzen oder einen Tippfehler zu beheben, ohne alles neu auszurichten. Füge die Tabelle ein: Das Trennzeichen wird als Pipe erkannt, die leeren Rand-Zellen werden entfernt, die Strichzeile wird erkannt und verworfen, und übrig bleibt dein Raster. Escapte Pipes überstehen die Reise: Eine Zelle mit ps \| grep wird beim Einlesen zu ps | grep entschärft und beim Ausgeben wieder escaped, sodass die Tabelle identisch zurückkommt und nicht eine Spalte breiter. Der CSV-Konverter auf dieser Seite schließt dieselbe Schleife von seinem Ende her: Er escapt den Backslash vor dem Pipe, sodass eine Zelle, die schon \| enthielt, eine erneute Umwandlung übersteht.
Auch die Ausrichtung übersteht die Reise, was weniger selbstverständlich ist, als es klingt, denn die Zeile, die sie trägt, ist genau die, die weg muss. Das Werkzeug liest die Doppelpunkte der Trennzeile aus, bevor es sie herausfiltert — :--- links, ---: rechts, :---: zentriert, ein bloßes --- gar nichts — und setzt damit die Ausgangsausrichtung jeder Spalte. Füge eine Tabelle ein, deren Regel | :--- | ---: | lautet, und die rechtsbündige Zahlenspalte kommt rechtsbündig zurück. Was du von Hand einstellst, gewinnt weiterhin: Die Auswahl je Spalte und die Schalter Standard, Links, Zentriert und Rechts über der Tabelle überschreiben das Gelesene, du kannst eine Ausrichtung also absichtlich ändern. Was du nicht mehr kannst, ist eine versehentlich zu verlieren.
| Teil | Pflicht? | Was passiert, wenn er fehlt oder falsch ist |
|---|---|---|
| Kopfzeile | Ja | Es gibt keine kopflose Form; der Generator erfindet Spalte 1, Spalte 2, statt sie wegzulassen |
| Trennzeile aus Strichen | Ja | Gar keine Tabelle — die Kopfzeile erscheint als Absatz mit Pipes |
| Zellenzahl der Trennzeile gleich der Kopfzeile | Ja | Die Tabelle wird nicht erkannt und fällt auf wörtlichen Text zurück |
| Datenzeilen | Nein | Kopf- plus Trennzeile ist eine gültige Tabelle; das HTML hat schlicht kein tbody |
| Zellenzahl in einer Datenzeile | Nein | Zu wenige, dann werden leere eingefügt; zu viele, dann wird der Überschuss ignoriert |
| Führende und abschließende Pipes | Nein | Optional; die Spezifikation empfiehlt sie zur Klarheit und gegen Parsing-Mehrdeutigkeit |
| Auffüll-Leerzeichen | Nein | Keine Wirkung auf die Ausgabe; sie sind da, damit ein Mensch die Quelle lesen kann |
| Eine Leerzeile in der Tabelle | Nie | Die Tabelle bricht bei der ersten Leerzeile oder dem Beginn eines anderen Blocks ab |
Häufige Fragen
- Was ist die kleinste gültige Markdown-Tabelle?
- Zwei Zeilen: eine Kopfzeile und darunter eine Trennzeile. | Name | über | --- | ist eine vollständige Tabelle mit einer Spalte, einer Kopfzelle und ohne Körper, und die GFM-Spezifikation enthält genau so ein Beispiel und merkt nur an, dass im HTML kein tbody-Element erscheint. Kleiner geht es nicht. Es gibt keine einzeilige Tabelle, und es gibt keinen Weg zu Datenzeilen ohne Kopfzeile — hat deine Datenmenge keine natürliche Kopfzeile, schreibt der Generator Spalte 1, Spalte 2 und so weiter, was die Syntax erzwingt. Die kleinste Trennzelle hat drei Zeichen, denn :-: ist die kürzeste Form, die an beiden Enden noch einen Ausrichtungsmarker trägt.
- Warum braucht GitHub die Strichzeile, wo andere Renderer ohne auskommen?
- Weil ein Pipe in Markdown nichts bedeutet. Jedes andere Blockkonstrukt kündigt sich mit einem Zeichen am Zeilenanfang an — Raute, Größer-als, Strich, eine Zahl mit Punkt — aber eine Zeile mit Pipes ist von Prosa nicht zu unterscheiden. GitHubs Tabellensyntax löst das, indem sie eine zweite Zeile verlangt, deren Zellen nur Bindestriche und optionale Doppelpunkte enthalten, was kein Absatz je zufällig erzeugen würde. Renderer mit anderer Tabellensyntax haben das Problem nicht, weil sie Tabellen anders markieren: Manche Wikis nutzen ein eigenes Eröffnungszeichen, und Formate wie reStructuredText zeichnen die Tabelle mit einem Zeichenraster. Die Anforderung folgt aus der Notation, sie ist keine Regel, die GitHub aus Strenge erfunden hat.
- Kann ich eine Tabelle ohne Kopfzeile machen?
- In der Syntax nicht. Die erste Zeile ist immer die Kopfzeile, und die Trennzeile sitzt immer darunter, eine kopflose Tabelle lässt sich also nicht ausdrücken. Der Generator behilft sich mit erfundenen Namen — Spalte 1, Spalte 2 —, wenn du „erste Zeile ist die Kopfzeile“ abwählst, und das ist ein Kompromiss und keine Lösung. Zwei Alternativen, wenn die Kopfzeile wirklich keinen Inhalt hat: Gib den Spalten leere Zellen, was zulässig ist und auf GitHub als leere Kopfzeile erscheint, oder schreibe die Tabelle als HTML und lass das thead ganz weg. Der Trick mit der leeren Kopfzeile sieht meist besser aus als erfundene Namen, und beides ist eine Bearbeitung entfernt.
- Meine Tabelle rendert auf GitHub, aber nicht auf meiner Doku-Seite. Warum?
- Weil Tabellen eine Erweiterung sind und dein Doku-Build sie nicht aktiviert hat. CommonMark 0.31.2 hat gar kein Tabellenkonstrukt, also behandelt jeder Prozessor, der nur die Kernspezifikation umsetzt, deine Tabelle als drei gewöhnliche Absätze. GitHubs Tabellen stehen in einem eigenen Dokument unter der Überschrift Tables (extension). Die meisten Static-Site-Generatoren unterstützen sie, aber diese Unterstützung ist ein Plugin oder ein Konfigurationsschalter und keine Selbstverständlichkeit. Prüfe die Markdown-Konfiguration des Builds, suche nach einer GFM- oder Pipe-Table-Option, und wenn es keine gibt, ergänze das Plugin oder weiche auf eine HTML-Tabelle aus, die jeder Renderer mit Inline-HTML korrekt zeigt.
- Behält eine Tabelle ihre Ausrichtung, wenn ich sie zum Bearbeiten wieder einfüge?
- Ja. Die Trennzeile muss weiterhin entfernt werden — sonst käme die Strichzeile als Datenzeile an —, aber die Doppelpunkte werden vorher ausgelesen und werden zur Startausrichtung jeder Spalte: :--- kommt als :--- zurück, ---: als ---:, :---: als :---:, und ein bloßes --- bleibt bloß. Alle vier GFM-Formen überstehen den Rundweg. Der Rest der Reise ist ebenfalls treu: führende und abschließende leere Zellen fallen weg, und ein escaptes \| wird beim Einlesen entschärft und beim Ausgeben wieder escaped, sodass aus einer Zelle ps \| grep keine zwei Spalten werden. Was du selbst wählst, überschreibt nach wie vor das Gelesene: Nimm eine Ausrichtung im Auswahlfeld einer Spalte oder einen der Schalter über der Tabelle, und deine gewinnt. Das ist die gewollte Reihenfolge: Die eingefügte Tabelle schlägt vor, deine Einstellung entscheidet.
Artikel, die dich interessieren könnten
Alle Ratgeber →Ähnliche Tools
Das hier beschreibt das Verhalten eines Dateiformats und eines Renderers, geprüft an der genannten Spezifikation und am Code des Werkzeugs, wie er heute aussieht. Renderer sind sich uneinig: GitHub, GitLab, ein Static-Site-Generator und die Vorschau deines Editors sind vier verschiedene Implementierungen, und was in der einen läuft, kann in der anderen scheitern. Nichts davon ist eine Zusage über deine Veröffentlichungskette — teste das Ergebnis dort, wo es wirklich erscheinen wird, und behandle jedes Werkzeug, dieses eingeschlossen, als etwas zum Nachprüfen und nicht zum Glauben.
Quellen
- GitHub — GitHub Flavored Markdown Spec, version 0.29-gfm (2019-04-06), section 4.10 Tables (extension): the delimiter row is required and must match the header row in cell count; body rows may vary in length, with missing cells inserted empty and excess cells ignored; leading and trailing pipes are recommended but optional; the table is broken at the first empty line or the beginning of another block-level structure
- CommonMark — CommonMark Spec version 0.31.2 (2024-01-28): the core specification defines leaf blocks (thematic breaks, headings, code blocks, HTML blocks, paragraphs) and container blocks (block quotes, list items, lists) and contains no table construct — tables are named in the introduction as one of the extensions others added to the original syntax
- Daring Fireball — Markdown: Syntax, the original 2004 specification by John Gruber — covers headers, blockquotes, lists, code blocks, horizontal rules, links, emphasis, images and inline HTML, and contains no table syntax at all; the documented fallback for anything it does not cover is to write HTML
- GitHub Docs — Organizing information with tables: the practical rules as GitHub documents them, including escaping a pipe as \| inside a cell and the fact that the vertical bars of a row do not need to line up
Hast du einen Fehler in diesem Artikel entdeckt?