# SEO, Output Formats & CI/CD ## SEO & Structured Data ### JSON-LD Structured Data Add schema.org structured data to your theme for search engines. Best placed in the `` block of `baseof.html`. **Article schema (`layouts/partials/jsonld/article.html`):** ```go-html-template {{ if eq .Kind "page" }} {{ end }} ``` **Breadcrumb schema (`layouts/partials/jsonld/breadcrumb.html`):** ```go-html-template {{ $breadcrumb := collections.Slice }} {{ range .Ancestors.Reverse }} {{ $breadcrumb = $breadcrumb | append (dict "@type" "ListItem" "position" (len $breadcrumb | add 1) "name" .Title "item" .Permalink )}} {{ end }} {{ $breadcrumb = $breadcrumb | append (dict "@type" "ListItem" "position" (len $breadcrumb | add 1) "name" .Title "item" .Permalink )}} ``` **Organization/Brand schema (`layouts/partials/jsonld/organization.html`):** ```go-html-template ``` ### Open Graph & Twitter Cards Hugo ships built-in `internal/opengraph.html` and `internal/twitter_cards.html` partials: ```go-html-template {{ template "_internal/opengraph.html" . }} {{ template "_internal/twitter_cards.html" . }} ``` **Override the built-in templates** by placing your own at: ``` layouts/partials/opengraph.html layouts/partials/twitter_cards.html ``` The built-in templates read from: - `.Title`, `.Description`, `.Permalink` - `.Params.images` (array, first image used) - `.Site.Params.images` (default images) - `.Date`, `.Lastmod` (for `article:published_time`, `article:modified_time`) ### Canonical URLs ```go-html-template {{ if .Params.canonicalURL }} {{ else }} {{ end }} ``` Enable in config: `canonifyURLs: true` ### Sitemap Customization **Config:** ```yaml sitemap: changefreq: weekly priority: 0.5 filename: sitemap.xml ``` **Per-page overrides** in front matter: ```yaml --- sitemap: priority: 0.8 changefreq: monthly disable: true --- ``` **Custom sitemap template** (`layouts/sitemap.xml`): ```go-html-template {{ printf "" | safeHTML }} {{ range .Data.Pages }}{{ if not .Params.sitemap.disable }} {{ .Permalink }} {{ .Lastmod.Format "2006-01-02" }} {{ with .Params.sitemap.changefreq }}{{ . }}{{ end }} {{ if ge .Params.sitemap.priority 0 }}{{ with .Params.sitemap.priority }}{{ . }}{{ end }}{{ else }}{{ if .IsHome }}1.0{{ else }}0.8{{ end }}{{ end }} {{ end }}{{ end }} ``` ## Custom Output Formats Create non-HTML output formats (JSON, AMP, etc.). **Config:** ```yaml outputFormats: JSON: mediaType: application/json baseName: index isPlainText: true notAlternative: true AMP: mediaType: text/html baseName: amp path: amp isHTML: true ``` **Per-page output format selection:** ```yaml --- outputs: home: [HTML, RSS, JSON] page: [HTML, AMP] section: [HTML, RSS, JSON] --- ``` **JSON output template** (`layouts/_default/index.json.json`): ```go-html-template {{- $pages := .Site.RegularPages -}} {{- $limit := .Site.Params.jsonLimit | default 20 -}} { "site": { "title": {{ .Site.Title | jsonify }}, "url": {{ .Site.BaseURL | jsonify }}, "pages": {{ len $pages }} }, "pages": [ {{- range first $limit $pages }} { "title": {{ .Title | jsonify }}, "url": {{ .Permalink | jsonify }}, "summary": {{ .Summary | plainify | jsonify }}, "date": {{ .Date.Format "2006-01-02" | jsonify }}, {{- with .Params.tags }}"tags": {{ . | jsonify }},{{ end }} "wordCount": {{ .WordCount }} }{{- if not (eq . (index (first $limit $pages) (sub (len (first $limit $pages)) 1))) }},{{ end }} {{- end }} ] } ``` ### Output Format Template Naming Convention Templates for custom formats follow: `layouts/
/