Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To put generated image bytes inside an HTML document, encode those bytes as Base64 and use a data:image/...;base64, URL in the <img> element. In a Django application, however, a static-file URL or a saved media-file URL is usually the better fit when the image can remain a separate file. The right method depends on whether the HTML must be self-contained, whether the image ships with your code, or whether it is created or uploaded at runtime.

Choose the representation before writing code

HTML displays an image when img src points to usable image data. That source can be an ordinary URL, a Django-generated static or media URL, or a data: URL containing the bytes themselves. These approaches are not interchangeable deployment models.

Requirement Recommended method Where the bytes live
The document must carry the image bytes Base64 data URL Inside the HTML attribute
The image is packaged with the application Django static files Static-file storage, referenced by a generated URL
The image is uploaded or generated at runtime and saved Django media storage and ImageField.url Configured media storage, with a URL stored through the model field

Base64 is useful for a standalone HTML artifact or a small inline asset. Django’s static and media systems keep image bytes in separately served files, which is generally easier to cache, replace and deliver in production. The references consulted do not establish a universal image-size cutoff or a performance benchmark, so choose based on document portability and deployment needs rather than an invented threshold.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Embed generated bytes with a Base64 data URL

Understand the data-URL format

A binary image data URL has this form:

data:[media-type];base64,[encoded-bytes]

For a PNG, the complete prefix is data:image/png;base64,. A JPEG must use image/jpeg; WebP uses image/webp. The declared media type must match the actual bytes, or browsers and downstream consumers may reject or misinterpret the image. MDN documents data URLs as a way to embed small files inline: https://developer.mozilla.org/en-US/docs/Web/URI/Reference/Schemes/data.

Convert Python image bytes

import base64

def png_data_url(image_bytes: bytes) -> str:
    """Return a data URL for bytes that are actually a PNG."""
    encoded = base64.b64encode(image_bytes).decode("ascii")
    return f"data:image/png;base64,{encoded}"

base64.b64encode returns bytes, so decode the encoded result to ASCII before inserting it into HTML. The Python Base64 documentation is at https://docs.python.org/3/library/base64.html.

Complete Django example using generated PNG bytes

The image-generation library can be anything that returns PNG bytes. This example uses Pillow to create a small image, then passes the data URL to a template.

# views.py
import base64
from io import BytesIO

from django.shortcuts import render
from PIL import Image, ImageDraw


def make_png_bytes() -> bytes:
    image = Image.new("RGB", (640, 360), "#16213e")
    draw = ImageDraw.Draw(image)
    draw.text((32, 32), "Generated by Python", fill="white")
    output = BytesIO()
    image.save(output, format="PNG")
    return output.getvalue()


def generated_image_page(request):
    image_bytes = make_png_bytes()
    encoded = base64.b64encode(image_bytes).decode("ascii")
    image_src = f"data:image/png;base64,{encoded}"
    return render(request, "images/generated.html", {"image_src": image_src})
<!-- templates/images/generated.html -->
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Generated image</title>
</head>
<body>
  <img src="{{ image_src }}" alt="Generated illustration">
</body>
</html>

Django escapes template variables by default. That is desirable here: the value is an attribute string, not trusted markup. Keep the data: prefix and encoded payload together; do not put the Base64 text in a separate element and expect the browser to decode it automatically.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

When inline data is the wrong choice

  • Do not use a data URL merely because the image was generated in Python. Save it through Django storage when the image should have its own URL.
  • Do not claim that Base64 makes an image universally faster or smaller; the supplied technical references provide no such benchmark.
  • Keep the MIME type accurate and avoid treating a filename extension as proof of file content.

Serve a generated image as a Django static asset

Static files are for assets that belong to the application and are deployed with it. A typical namespaced layout is:

my_app/
  static/
    my_app/
      generated-example.png

In the template, load Django’s static tag and construct the URL through configured static-file storage:

{% load static %}
<img src="{% static 'my_app/generated-example.png' %}" alt="Generated example">

The official static-files guide explains this template tag and the development server behavior: https://docs.djangoproject.com/en/dev/howto/static-files/. Namespacing the path (for example, my_app/...) prevents collisions when multiple apps contain files with the same filename.

Development versus production

When DEBUG is true, Django’s development setup can serve static files automatically. That helper is for development, not a production static-file server. Production needs a deliberate static deployment strategy, such as the storage and web-server or object-storage arrangement appropriate to your project and Django version. Run your project’s documented static collection and delivery process before relying on a static URL outside development.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Save runtime-generated images with ImageField

Use media storage when an image is uploaded or generated while the application runs and should persist independently of a release. Configure MEDIA_ROOT as the storage location and MEDIA_URL as its public URL prefix, then expose the model field’s URL in the template.

# settings.py
MEDIA_ROOT = BASE_DIR / "media"
MEDIA_URL = "/media/"

# models.py
from django.db import models

class Artwork(models.Model):
    image = models.ImageField(upload_to="artwork/")
    created_at = models.DateTimeField(auto_now_add=True)
# views.py
from django.shortcuts import get_object_or_404, render
from .models import Artwork

def artwork_detail(request, pk):
    artwork = get_object_or_404(Artwork, pk=pk)
    return render(request, "images/detail.html", {"artwork": artwork})
<!-- templates/images/detail.html -->
<img src="{{ artwork.image.url }}" alt="Generated artwork">

Django stores the file path through the configured storage and exposes the resolved URL through .url; it does not ordinarily put image bytes in the database. Storage can be customized, so do not assume that MEDIA_ROOT always means a local disk. Model-field documentation: https://docs.djangoproject.com/en/5.2/ref/models/fields/#imagefield.

Saving bytes produced by a generator

from io import BytesIO
from django.core.files.base import ContentFile

png_buffer = BytesIO()
image.save(png_buffer, format="PNG")
artwork = Artwork()
artwork.image.save("generated.png", ContentFile(png_buffer.getvalue()), save=True)

The filename is a storage name, not a guarantee that the content is safe. Validate content, control where user-supplied files can be stored, and apply the permissions and serving policy required by your deployment. Django’s model-field reference discusses upload validation and the security risks of unsafe file placement.

Development media serving is not production delivery

The development guide documents a debug-only URL helper for local media. It explicitly is not suitable for production. Configure production media delivery through the storage system and infrastructure you operate; ensure the resulting .url is reachable by the browser that renders the page.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Validate images before displaying or saving them

Django’s ImageField form validation depends on Pillow supporting the image format. Install and configure Pillow for every format your application accepts. Validation checks that the content is an image; an extension alone is not sufficient.

The form documentation also notes a lifecycle detail: validation closes the underlying uploaded-file descriptor. Metadata such as dimensions may remain available, but later operations that need to read pixels can require reopening the image. If your workflow validates an upload and then transforms it, explicitly reopen it from storage or from a fresh stream rather than assuming the original descriptor is still open. See https://docs.djangoproject.com/en/6.0/ref/forms/fields/#imagefield.

  • Validate the decoded content and permitted format, not only the filename.
  • Limit dimensions and file sizes according to your application’s risk and resource budget.
  • Keep untrusted uploads in controlled storage and serve them according to your security policy.
  • Use an accurate content type when returning or embedding generated bytes.

Or skip the browser setup

If your goal is to obtain a clean image of a rendered webpage rather than create pixels in Pillow, ScreenshotNeo provides a single HTTP request that returns PNG, JPEG, WebP or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups and chat widgets; each step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server exposes take_screenshot, get_page_info and capture_pdf to Claude, Cursor and other MCP clients.

cURL:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the full parameter reference and options in the ScreenshotNeo documentation. The service supports full-page and selector captures, device and viewport settings, retina scale, custom CSS or JavaScript, waits, request blocking, headers and cookies, geolocation, PDFs, resizing, caching, signed links, asynchronous jobs, bulk capture and a usage API. Every feature is on every plan. The Free plan includes 1,000 shots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot the common failures

The browser shows a broken image icon

  • Check that the data URL begins with the correct MIME type and includes ;base64,.
  • Confirm the bytes really are PNG, JPEG or another format named in the URL.
  • For static or media images, open the rendered src URL directly and inspect the HTTP response.

The template prints a huge or malformed value

Pass one complete string as the context value and use it in src. Do not add line breaks or shell-style quoting to the encoded payload. Django’s normal HTML escaping should remain enabled.

{% static %} cannot find the file

Verify that the app is installed, the file is under the app’s static directory, the template contains {% load static %}, and the path includes the namespace. Check your project’s static settings and production collection process.

ImageField.url is empty or returns an inaccessible URL

Confirm that the model instance has a file, that storage saved it successfully, and that the configured media URL is actually served by your development or production infrastructure. A database path alone does not make a file publicly reachable.

Validation succeeds but later image processing fails

Reopen the validated file or create a new stream before reading pixels. Pillow-backed form validation may close the uploaded-file descriptor after validation.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The image is unexpectedly billed by a screenshot service

For ScreenshotNeo, inspect the X-Page-Verdict and X-Billed response headers. Failed loads, blank pages, bot checks, timeouts and cache hits are identified and are not billed under the stated service behavior.

Practical decision checklist

  • Need one portable HTML file containing pixels? Use a Base64 data URL.
  • Need an asset released with your Django code? Use namespaced static files and {% static %}.
  • Need persistent user or runtime-generated images? Save with configured media storage and render .url.
  • Need to process uploads after validation? Reopen the file before pixel operations.
  • Need a screenshot of a live page instead of generating image bytes? Use the ScreenshotNeo request or MCP tools.

Frequently Asked Questions

Can I put a Base64 image directly in a Django template file?

Yes, but it is cleaner to construct the complete data URL in Python and pass it as context. Keep the template responsible for the src attribute rather than image encoding logic.

Does an ImageField store the image inside the database?

Normally it stores a path managed by the configured storage and exposes that file through .url; storage backends can change where the bytes reside.

Which Django documentation versions apply?

The cited model-field reference is for Django 5.2, the form-field reference is for Django 6.0, and the static-files link is the development guide. Match URLs and settings to the Django release your project runs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Bottom Line

Use a Base64 data URL only when the HTML must contain the image bytes. For Django applications, static files suit deployed assets, while ImageField and media storage suit runtime or user-generated images; validate content and configure production delivery for whichever URL-based approach you choose.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.