5 Min. Lesezeit

Hugo Output Formats – ein Inhalt, viele Darstellungen

Hugo Output Formats erklärt: Ein Inhalt, mehrere Darstellungen – Web, Print-PDF und mehr aus derselben Quelldatei. Mit echter Config und den Stolperfallen.

Es gibt diesen Moment, in dem man denselben Inhalt zum zweiten Mal pflegt. Der Lebenslauf liegt als schöne Webseite vor – und dann braucht jemand eine PDF-Version. Also kopiert man den Text in ein zweites Dokument, formatiert ihn neu, und ab jetzt gibt es zwei Wahrheiten, die man bei jeder Änderung synchron halten muss. Das geht ein, zwei Mal gut. Dann driften sie auseinander.

Hugo löst genau dieses Problem mit Output Formats: eine Quelldatei, beliebig viele Darstellungen. Web, Print, JSON, was immer du brauchst – alles aus demselben Markdown, immer in Sync. So funktioniert es.

Das Prinzip: Content und Darstellung trennen

Hugo trennt grundsätzlich zwischen was (dein Markdown unter content/) und wie (deine Templates unter layouts/). Output Formats erweitern das “wie” um eine zweite Dimension: Dieselbe Seite kann durch mehrere Templates laufen und mehrere Dateien erzeugen.

Standardmäßig rendert Hugo jede Seite nach HTML. Aber du kannst sagen: “Diese Seite bitte auch als Print-Variante.” Dann erzeugt Hugo aus einem Markdown zwei HTML-Dateien – eine fürs Web, eine fürs Drucken, jede mit eigenem Layout.


Schritt 1: Den Media Type definieren

Ein Output Format braucht einen Media Type – die Angabe, welche Art Datei am Ende rausfällt. Für eine Print-HTML-Seite ist das schlicht HTML:

# config.toml
[mediaTypes]
  [mediaTypes."text/html"]
    suffixes = ["html"]

HTML kennt Hugo zwar von Haus aus, aber es schadet nicht, den Block explizit zu haben – spätestens wenn du eigene Formate wie application/manifest+json ergänzt.


Schritt 2: Das Output Format definieren

Jetzt das eigentliche Format. Hier am Beispiel einer Print-Variante:

# config.toml
[outputFormats]
  [outputFormats.print]
    name      = "print"
    mediaType = "text/html"
    path      = "print"
    isHTML    = true

Was die Felder bedeuten:

  • name – der interne Name, über den du das Format ansprichst ("print").
  • mediaType – verweist auf den oben definierten Typ.
  • path – der Unterordner im Output. Mit path = "print" landet die Druckversion unter /print/... statt die Web-Version zu überschreiben. Das ist wichtig – ohne eigenen path würden sich beide HTML-Dateien dieselbe URL teilen und kollidieren.
  • isHTML – sagt Hugo, dass es echtes HTML ist (relevant für Features wie relative URLs und Aliase).

Schritt 3: Die Standard-Outputs festlegen

Per Default soll natürlich nicht jede Seite eine Print-Version bekommen. Also bleibt der globale Standard schlank:

# config.toml
[outputs]
  page = ["html"]

Das heißt: Normale Seiten gibt es nur als HTML. Das Print-Format aktivierst du gezielt pro Seite – im nächsten Schritt.


Schritt 4: Output Formats pro Seite aktivieren

Und hier liegt die Stolperfalle, die mich Nerven gekostet hat. In der Seiten-Frontmatter aktivierst du zusätzliche Formate mit einem flachen Array:

+++
title = "Mein Lebenslauf"
outputs = ["html", "print"]
+++
# YAML-Variante:
---
title: "Mein Lebenslauf"
outputs: ["html", "print"]
---

⚠️ Die Falle: In der Frontmatter ist es ein flaches Array outputs = ["html", "print"]. Die Tabellen-Schreibweise [outputs] page = [...] funktioniert nur in der Site-Config (config.toml), nicht in der Seiten-Frontmatter. Schreibt man sie versehentlich in die Frontmatter, passiert: nichts. Hugo ignoriert sie kommentarlos, die Print-Version wird nie erzeugt, und man sucht eine halbe Stunde nach dem Fehler, den es gar nicht gibt.


Schritt 5: Das Template für das neue Format

Jetzt braucht Hugo ein Layout, das es für das Print-Format benutzt. Die Namenskonvention: <layout>.<format>.html. Für eine Single-Seite im Print-Format also:

layouts/
├─ _default/
│  ├─ single.html         ← normale Web-Version
│  └─ single.print.html   ← Print-Version (gleicher Content, anderes Markup)

Das Schöne: Beide Templates bekommen denselben .Content. Du renderst exakt denselben Markdown-Body – nur eingebettet in unterschiedliches HTML. Die Web-Version steckt im vollen Seitenlayout mit Navigation und Theme; die Print-Version ist ein nacktes, eigenständiges A4-Dokument:

<!-- single.print.html — minimal, eigenständig, druckoptimiert -->
<!DOCTYPE html>
<html lang="de">
<head>
  <meta charset="utf-8">
  <title>{{ .Title }}</title>
  <style>
    @page { size: A4; margin: 2cm; }
    body  { font-family: serif; color: #000; background: #fff; }
    /* eigene, immer-helle Palette – unabhängig vom Site-Theme */
  </style>
</head>
<body>
  <h1>{{ .Title }}</h1>
  {{ .Content }}   {{/* derselbe Content wie im Web */}}
</body>
</html>

Ein Detail aus der Praxis: Druckdokumente sollten immer hell sein, egal welches Theme die Seite hat. Papier ist weiß. Die Print-Vorlage bringt also ihre eigene Farbpalette mit und initialisiert kein Dark/Light-Theme.


Inhalte je Format ein- und ausblenden

Manchmal soll etwas nur im Web erscheinen, nicht im Druck – ein “Jetzt Demo ansehen”-Button etwa ergibt auf Papier keinen Sinn. Dafür baut man sich einen kleinen Shortcode:

{{/* layouts/shortcodes/webonly.html */}}
<div class="web-only">{{ .Inner | markdownify }}</div>

Im Markdown:

{{< webonly >}}
[→ Live-Demo ansehen](https://example.com)
{{< /webonly >}}

Und im Print-CSS einfach ausblenden:

/* nur in single.print.html */
.web-only { display: none; }

Derselbe Quelltext, der Button erscheint im Web und verschwindet im Druck. Keine zwei Versionen.


Wann lohnt sich das?

Output Formats sind kein Selbstzweck. Sinnvoll werden sie überall, wo derselbe Inhalt mehrere Gesichter braucht:

  • Lebenslauf → Web-Ansicht + druckbares PDF aus einer Quelle
  • Rechnungen / Angebote → Bildschirm-Tool + Druckvorlage
  • Blog → HTML + ein index.json als Such-Index oder RSS-Erweiterung
  • AMP / Reader-Modus → schlanke Parallel-Variante einer Seite

Die Faustregel: Sobald du dabei bist, Inhalt zu kopieren, um eine zweite Darstellung zu bekommen – stopp. Das ist der Moment für ein Output Format.


Kurze Übersicht

1. [mediaTypes]    → welcher Datentyp?         (text/html)
2. [outputFormats] → Format definieren         (name, path, isHTML)
3. [outputs]       → globaler Standard         (page = ["html"])
4. Frontmatter     → pro Seite aktivieren      (outputs = ["html","print"])  ← flaches Array!
5. Template        → single.<format>.html      (gleicher .Content, anderes Markup)
6. Shortcodes/CSS  → Inhalte je Format steuern  (.web-only { display:none })

Der eigentliche Gewinn ist nicht die Technik – es ist die Single Source of Truth. Eine Datei ändern, und Web und Druck ziehen automatisch mit. Kein “habe ich das im PDF auch geändert?”, keine zwei Wahrheiten, die langsam auseinanderdriften. Genau dafür ist Hugo gebaut: Content einmal pflegen, beliebig oft darstellen.