mkdocs-zip-bundle-plugin
Turn code blocks into downloadable files — for MkDocs/Material and Zensical. Tag any fenced code block with a bundle ID and filename and a download button is injected. 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.
Using Zensical? Skip the plugin install — see Using with Zensical.
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.