October DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsSlow PC?RecommendedPC slow today? Run a repair scan before it gets worseResolve common Windows issues and optimize system performance.Scan NowOctober DealsAmazon USDeal season is back - check today's better picksAmazon US: current deals, useful picks and tech finds.See Picks×
Skip to content
CSS

How to Export HTML, JavaScript, and CSS to PDF with Django wkhtmltopdf

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

Use django-wkhtmltopdf with the platform-appropriate wkhtmltopdf executable, then expose a Django PDFTemplateView. The integration renders your template with wkhtmltopdf’s Qt WebKit engine and returns a PDF response. The reliable setup is: install both layers, register the Django app, collect static files, make every asset reachable by the converter, configure the executable path when necessary, and choose an explicit JavaScript readiness strategy for dynamic content.

What you are installing

There are two separate components:

  • django-wkhtmltopdf: Django views and response integration. Its stated purpose is to let a Django site output dynamic PDFs.
  • wkhtmltopdf: the command-line renderer. It converts HTML to PDF with the Qt WebKit engine and supplies switches for JavaScript, delays, scripts, CSS, viewport sizing, images, links, local-file permissions, and load-error handling.

The Python package does not replace the executable. Install a binary suitable for the operating system and CPU architecture where your Django process runs. The integration searches for wkhtmltopdf on PATH; set WKHTMLTOPDF_CMD when it is installed elsewhere.

Install and configure the integration

1. Install the Python package and binary

Install the package in the same virtual environment as Django:

python -m pip install django-wkhtmltopdf

Install the wkhtmltopdf binary using the package supplied for your target platform. Verify that the executable is available to the service account, not only to your interactive shell:

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

If that command is not on PATH, configure its absolute path in Django settings.

2. Register the app and executable

# settings.py
INSTALLED_APPS = [
    # ...
    "wkhtmltopdf",
]

# Only needed when wkhtmltopdf is not on PATH.
WKHTMLTOPDF_CMD = "/absolute/path/to/wkhtmltopdf"

WKHTMLTOPDF_CMD_OPTIONS = {
    "quiet": True,
    "margin-top": "15mm",
    "margin-right": "15mm",
    "margin-bottom": "15mm",
    "margin-left": "15mm",
}

Boolean values represent switches; options that require a value use a string (for example, title or a margin). Keep options conservative at first, then add the controls your layout requires.

Prepare a template that renders outside the browser

Create a normal Django template, but assume that the converter may run without your browser’s session, hostname resolution, or development server conveniences.

<!-- templates/reports/invoice.html -->
<!doctype html>
<html lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>{{ invoice.number }}</title>
  <link rel="stylesheet" href="{{ css_url }}">
</head>
<body>
  <h1>Invoice {{ invoice.number }}</h1>
  <p>Issued {{ invoice.issued_at }}</p>
  <table>
    {% for line in invoice.lines %}
      <tr><td>{{ line.description }}</td><td>{{ line.total }}</td></tr>
    {% endfor %}
  </table>
  <script src="{{ chart_js_url }}"></script>
</body>
</html>

The UTF-8 meta element prevents many non-ASCII failures. Fonts must also be installed or fetched successfully by the renderer.

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

Make CSS, JavaScript, images, and fonts reachable

Set STATIC_ROOT and populate it with Django’s static-file collection command. The installation guidance specifically requires collected static files because the converter must read them, including on a local machine.

# settings.py
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles"
python manage.py collectstatic

Use absolute URLs, or another URL form that the converter can resolve, for stylesheets, scripts, images, and fonts. A browser showing the page correctly does not prove that the wkhtmltopdf process can reach those URLs. If you use local files, grant only the directories required by the renderer with its --allow option. External images and links are enabled by default, while local-file access is restricted unless explicitly permitted.

For pages built from widgets, identify the CSS and JavaScript assets the widget actually needs and include those assets in the generated HTML. Avoid relying on browser extensions, relative paths that resolve only under your development server, or authentication cookies that the converter will not possess.

Return a PDF from a Django URL

Use PDFTemplateView

# urls.py
from django.urls import path
from wkhtmltopdf.views import PDFTemplateView

urlpatterns = [
    path(
        "reports/invoice/<int:pk>/pdf/",
        PDFTemplateView.as_view(
            template_name="reports/invoice.html",
            filename="invoice.pdf",
        ),
        name="invoice-pdf",
    ),
]

The default response is a PDFTemplateResponse. The view renders the named template and sends the generated file as a download. Set filename=None when you want inline display rather than a forced download:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
PDFTemplateView.as_view(
    template_name="reports/invoice.html",
    filename=None,
)

For object-specific data, subclass the view and provide context in the usual Django way:

# views.py
from django.shortcuts import get_object_or_404
from wkhtmltopdf.views import PDFTemplateView
from .models import Invoice

class InvoicePDFView(PDFTemplateView):
    template_name = "reports/invoice.html"
    filename = "invoice.pdf"

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        invoice = get_object_or_404(Invoice, pk=self.kwargs["pk"])
        context["invoice"] = invoice
        context["css_url"] = self.request.build_absolute_uri("/static/reports/invoice.css")
        context["chart_js_url"] = self.request.build_absolute_uri("/static/reports/chart.js")
        return context

Protect the URL with your normal authentication and authorization checks. The renderer must still be able to request every asset and data endpoint used by the template.

Control JavaScript and asynchronous content

JavaScript is enabled by default. A page that starts a chart after an API request can therefore finish conversion before the chart exists unless you tell wkhtmltopdf when it is ready.

Choose one readiness mechanism

  • Fixed delay: --javascript-delay 1500 waits the specified milliseconds after page load. The documented default is 200 ms. Increase it only as far as your slowest expected render requires.
  • Window status: have your page set a status value after data and charts are complete, then use --window-status ready. This is more deterministic than guessing a delay.
  • Additional script: use --run-script when a small post-load action must be executed by the renderer.
  • Disable execution: use --disable-javascript for static documents where scripts are unnecessary.

For a chart, expose a clear completion signal rather than waiting indefinitely:

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.
<script>
  renderChart(data).then(function () {
    window.status = "pdf-ready";
  });
</script>

Whichever method you choose, ensure that the required JavaScript files and API endpoints are reachable from the rendering process. A longer delay cannot repair a blocked request.

Layout, pages, and visual fidelity

wkhtmltopdf can set paper size, orientation, margins, DPI, viewport dimensions, image and background behavior, and link loading. Smart shrinking is enabled by default: it changes the pixel-to-DPI relationship to fit content. Disable it when fixed measurements are more important than automatic fitting.

WKHTMLTOPDF_CMD_OPTIONS = {
    "page-size": "A4",
    "orientation": "Portrait",
    "margin-top": "12mm",
    "margin-right": "12mm",
    "margin-bottom": "12mm",
    "margin-left": "12mm",
    "viewport-size": "1280x900",
    "javascript-delay": 800,
    "background": True,
    # "disable-smart-shrinking": True,  # enable for fixed-scale layouts
}

Use CSS print rules and deliberate page breaks for multipage documents. Test long tables, unusually large images, and narrow viewports; those cases expose wrapping and scaling problems that a short sample page hides.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Debug before changing production options

Inspect the HTML first

Render the view as HTML (the package documents an ?as=html inspection path), then open that output and verify that the exact CSS, images, fonts, and scripts have usable URLs. This separates Django template errors from converter errors.

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

Common symptoms and fixes

Symptom Likely cause Fix
Blank or unstyled PDF Collected files are missing or URLs cannot be resolved Set and populate STATIC_ROOT; inspect the HTML; use absolute or otherwise reachable asset URLs.
Chart or dynamic widget missing Conversion finished before asynchronous work completed Increase javascript-delay, use window-status, or add run-script; confirm network calls complete.
Local image or font is blocked Local-file access is restricted Serve the asset through a reachable URL or grant only its directory with --allow.
Unexpected wrapping or scale Viewport, margins, page size, or smart shrinking Set page and viewport values explicitly, then decide whether to disable smart shrinking.
Accented characters or symbols are broken Missing UTF-8 declaration or font Add the UTF-8 meta tag and make a suitable font available to the renderer.
Intermittent conversion failures Missing dependencies are being ignored Configure load-error and media-error handling deliberately so failures are visible rather than silently hidden.

Performance, reliability, and security considerations

  • Readiness versus throughput: every JavaScript delay holds a renderer process longer. Prefer a completion signal for variable network work and keep the page’s requests finite.
  • Asset locality: serving versioned static assets from a dependable URL is usually easier to reproduce than relying on ad-hoc local paths. If local access is unavoidable, allow only the required directories.
  • Failure visibility: do not suppress load or media errors until you understand which dependencies are optional. A PDF that renders without a chart may be worse than a failed request that alerts you.
  • Authentication: the converter does not automatically inherit a user’s browser session. Pass data through the server-side template or arrange an explicitly reachable, authorized endpoint.
  • Output validation: check the HTTP status, content type, nonzero file size, and presence of expected text or pages before storing or emailing a PDF.

Or skip the browser setup

If you only need a clean capture or PDF from a URL, ScreenshotNeo provides a single HTTP request and an MCP server for AI agents. Before capture it accepts cookie/consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the result with X-Page-Verdict and X-Billed headers.

For PDF output, use the API documented at ScreenshotNeo’s documentation. The same service supports full-page captures, custom CSS and JavaScript, waits for selectors, delays or network idle, device and viewport controls, cookies and headers, and PDF paper, margin, orientation, and page-range settings.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. 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.

Frequently Asked Questions

Can I use wkhtmltopdf without Django?

Yes. wkhtmltopdf is independently usable from its command line; django-wkhtmltopdf adds Django views and response handling.

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

Should I wait with a delay or window status?

Use a status signal when completion time varies with network or data. Use a short fixed delay only when rendering time is predictable.

Why does my browser page work while the PDF does not?

The converter has a separate process, viewport, filesystem policy, font environment, and network access. Verify each dependency from the converter’s point of view.

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.

Leave a Reply

Your email address will not be published. Required fields are marked *

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

Read next

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.