mkdocs-zip-bundle-plugin
Turn code blocks into downloadable files — directly from your MkDocs docs. Tag any fenced code block with a bundle ID and filename and the plugin injects a download button. Group multiple blocks under one ID to produce a ZIP archive. Pair with mkdocs-placeholder-plugin and the downloaded files contain the reader's own values, not your defaults.
Live demo
Edit the fields below. Every code block on this page updates live. Click any download button — the file you get has your values in it, not the defaults.
| Description / name | Input element |
|---|---|
| Application name | |
| Host IP address | |
| Application port | |
| Configuration directory path | |
| Timezone | |
| User ID | |
| Group ID |
Single file download
Add data-zip-bundle and data-zip-filename to a code block. The plugin injects a download button — no ZIP, just the raw file.
```yaml { data-zip-bundle="compose-only" data-zip-filename="compose.yaml" }
services:
myapp:
...
```
Result:
services:
myapp:
image: ghcr.io/example/myapp:latest
container_name: myapp
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
ports:
- "8080:8080"
volumes:
- /opt/myapp:/config
restart: unless-stopped
Multi-file ZIP bundle
Use the same data-zip-bundle ID on multiple blocks. The button appears after the last one and downloads all files as a single ZIP.
```yaml { data-zip-bundle="full-bundle" data-zip-filename="compose.yaml" }
...
```
```ini { data-zip-bundle="full-bundle" data-zip-filename="app.env" }
...
```
```bash { data-zip-bundle="full-bundle" data-zip-filename="setup.sh" }
...
```
Result:
services:
myapp:
image: ghcr.io/example/myapp:latest
container_name: myapp
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
ports:
- "8080:8080"
volumes:
- /opt/myapp:/config
restart: unless-stopped
APP_NAME=myapp
HOST=192.168.1.100
PORT=8080
CONFIG_PATH=/opt/myapp
TZ=America/New_York
PUID=1000
PGID=1000
#!/bin/sh
set -e
mkdir -p /opt/myapp
chown 1000:1000 /opt/myapp
podman compose up -d
echo "Done. Access at http://192.168.1.100:8080"
Nested directories in the ZIP
Use paths as filenames — the plugin creates the directory structure inside the ZIP automatically.
```yaml { data-zip-bundle="nested" data-zip-filename="config/app.yaml" }
...
```
```bash { data-zip-bundle="nested" data-zip-filename="scripts/setup.sh" }
...
```
Result:
Custom button label
Use data-zip-label to override the auto-generated button text.
```yaml { data-zip-bundle="labeled" data-zip-filename="compose.yaml" data-zip-label="Download my config" }
...
```
Result:
Installation
Full setup
mkdocs.yml
markdown_extensions:
- attr_list # required — reads data-zip-* attributes
- pymdownx.superfences
plugins:
- search
- zip-bundle:
include_jszip: true
- placeholder:
placeholder_file: placeholder-plugin.yaml
placeholder-plugin.yaml
settings:
normal_prefix: "@"
normal_suffix: "@"
auto_placeholder_tables: false # place the table manually where you want it
placeholders:
APP_NAME:
default: myapp
description: Application name
PORT:
default: "8080"
description: Application port
HOST_IP:
default: 192.168.1.100
description: Host IP address
Your page
Place the input table wherever you want it in your markdown:
Then tag your code blocks. The download button is injected automatically. At click time, the button reads the live rendered text — so whatever the reader typed into the inputs is what ends up in the downloaded file.