File Uploads

Datastar handles file uploads by encoding them as Base64 and sending them as part of your signals.

1. The Frontend

Bind your file input to a signal. Datastar automatically converts the files into a list of objects.

async def home(c: Context, w: Writer):
    w.html(
        Div(
            data.signals({"my_files": []}),
            Input({"type": "file", "multiple": True}, data.bind("my_files")),
            Button(data.on("click", at.post("/upload")), "Upload")
        )
    )

2. The Backend

On the server, decode the Base64 data and save the file.

import base64

async def handle_upload(c: Context, w: Writer):
    signals = await c.signals()
    files = signals.get("my_files", [])

    for file in files:
        name = file["name"]
        content_b64 = file["contents"] # Format: "data:image/png;base64,..."

        # Strip the header
        if "," in content_b64:
            content_b64 = content_b64.split(",", 1)[1]

        binary_data = base64.b64decode(content_b64)

        with open(f"./uploads/{name}", "wb") as f:
            f.write(binary_data)

    w.sync({"my_files": []}) # Clear input on client

Important Notes

  • File Size: Base64 encoding increases file size by ~33%. For very large files (GBs), consider a direct multipart form upload or a pre-signed S3 URL.
  • Structure: Each file object in the signal contains:
    • name: Filename
    • contents: Base64 string
    • mime: MIME type (e.g., image/png)
  • Security: Always sanitize filenames before saving them to disk to prevent path traversal attacks.