The {{<csv>}} shortcode provides a simple way to embed CSV files in your documentation from a global static/csv directory. This approach works well for large and/or complex tables that you want to keep separate from your markdown files.
Using this shortcode also enables you to update your CSV files programmatically without needing to update the markdown files that reference them.
How it works #
The {{<csv>}} shortcode accepts 3 positional arguments: filename, delimiter, and excludedColumns.
filenamespecifies the CSV file to embed. The file resides in thestatic/csvdirectory. You don’t need to include the.csvextension.delimiterdefines the character that separates columns in the CSV file. Default:,.excludedColumnsaccepts a comma-separated list of column numbers to exclude from the table. This parameter has case-sensitivity and defaults to an empty string.
Examples #
The table in the following examples comes from /static/csv/food.csv.
Full table with default delimiter #
{{<csv food>}}
| Item | Type | Origin |
|---|---|---|
| Apple | Fruit | USA |
| Banana | Fruit | Ecuador |
| Carrot | Vegetable | Canada |
| Tomato | Vegetable | Italy |
| Bread | Grain | France |
| Cheese | Dairy | Switzerland |
Full table with excluded column #
{{<csv food "," "Origin">}}
| Item | Type |
|---|---|
| Apple | Fruit |
| Banana | Fruit |
| Carrot | Vegetable |
| Tomato | Vegetable |
| Bread | Grain |
| Cheese | Dairy |
Source code #
go
{{- $filename := .Get 0 -}}
{{- $delimiter := .Get 1 | default "," -}}
{{- $skipColumns := split (.Get 2 | default "") "," -}}
{{- $cssClass := .Get 3 | default "table table--dense table--zebra table--glass table--rounded" -}}
{{- $lazyQuotes := .Get 4 | default false -}}
{{- $csvResource := resources.Get (printf "csv/%s.csv" $filename) -}}
{{- $csvContent := "" -}}
{{- if $csvResource -}}
{{- $csvContent = $csvResource.Content -}}
{{- else -}}
{{/* Fallback: try reading from static directory */}}
{{- $staticPath := printf "static/csv/%s.csv" $filename -}}
{{- $csvContent = readFile $staticPath -}}
{{- if not $csvContent -}}
{{- errorf "CSV shortcode: File not found in assets/csv/%s.csv or static/csv/%s.csv on page %s" $filename $filename .Page.RelPermalink -}}
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
<strong>CSV Error:</strong> File "{{ $filename }}.csv" not found in assets/csv/ or static/csv/ directories.
</div>
{{- end -}}
{{- end -}}
{{- if $csvContent -}}
{{- $unmarshalOptions := dict "delimiter" $delimiter -}}
{{- if $lazyQuotes -}}
{{- $unmarshalOptions = merge $unmarshalOptions (dict "lazyQuotes" true) -}}
{{- end -}}
{{- $csvData := "" -}}
{{- if $csvResource -}}
{{- $csvData = $csvResource | transform.Unmarshal $unmarshalOptions -}}
{{- else -}}
{{- $csvData = $csvContent | transform.Unmarshal $unmarshalOptions -}}
{{- end -}}
{{- if not $csvData -}}
{{- errorf "CSV shortcode: Failed to parse CSV data from '%s' on page %s" $filename .Page.RelPermalink -}}
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
<strong>CSV Error:</strong> Failed to parse CSV data from "{{ $filename }}". The file may be empty or incorrectly formatted.
</div>
{{- else if eq (len $csvData) 0 -}}
{{- errorf "CSV shortcode: Empty CSV data from '%s' on page %s" $filename .Page.RelPermalink -}}
<div class="bg-yellow-100 border border-yellow-400 text-yellow-700 px-4 py-3 rounded mb-4">
<strong>CSV Warning:</strong> No data found in "{{ $filename }}".
</div>
{{- else -}}
{{- $headers := index $csvData 0 -}}
{{- $filteredHeaders := slice -}}
{{- $filteredIndices := slice -}}
{{- range $index, $col := $headers -}}
{{- if not (in $skipColumns $col) -}}
{{- $filteredHeaders = $filteredHeaders | append $col -}}
{{- $filteredIndices = $filteredIndices | append $index -}}
{{- end -}}
{{- end -}}
<div class="table__container table__container--rounded">
<table class="{{ $cssClass }}" role="table">
<caption class="sr-only">Data from {{ $filename }}</caption>
<thead class="table__head">
<tr>
{{- range $filteredHeaders -}}
<th scope="col" class="table__cell">
{{- . | markdownify -}}
</th>
{{- end -}}
</tr>
</thead>
<tbody class="table__body">
{{- range $rowIndex, $row := after 1 $csvData -}}
<tr class="table__row">
{{- range $filteredIndices -}}
<td class="table__cell table__cell--nowrap">
{{- if lt . (len $row) -}}
{{- index $row . | markdownify -}}
{{- else -}}
<span class="text-gray-400">—</span>
{{- end -}}
</td>
{{- end -}}
</tr>
{{- end -}}
</tbody>
</table>
</div>
{{- end -}}
{{- end -}}