Skip to content

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 / nameInput 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:

server:
  host: 192.168.1.100
  port: 8080
  name: myapp
timezone: America/New_York
#!/bin/sh
mkdir -p /opt/myapp/config
cp config/app.yaml /opt/myapp/config/
podman compose up -d

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:

services:
  myapp:
    image: ghcr.io/example/myapp:latest
    ports:
      - "8080:8080"

Installation

pip install mkdocs-zip-bundle-plugin mkdocs-placeholder-plugin

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:

<div class="auto-input-table" data-columns="description,input"></div>

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.

Configuration reference →