Add analytics and other <head> tags#
Goal: get a tracking script, a site-verification tag or a preconnect into every page's <head>
without changing anything else about the shell.
You need: a templates/ directory your Django project loads ahead of mdjango. This is the one
template mdjango invites you to override; the rest of the templates reference
is a closed surface. Colours and type do not go here — they load through a setting, covered in
Change the colours and type.
1. Shadow the head slot#
mdjango renders an empty component, cotton/docs/head_extra.html, at the end of every page's
<head>. Override it by creating a file at the same path inside a templates directory your project
loads before mdjango:
your_project/
templates/
cotton/
docs/
head_extra.html # your version wins over mdjango's empty one
For Django to find yours first, that directory must be ahead of mdjango on the loader path — either
an entry in TEMPLATES[0]["DIRS"] (searched before any app), or an app listed before mdjango in
INSTALLED_APPS with APP_DIRS on.
2. Put your tags in it#
Whatever you write renders verbatim inside <head>. A privacy-friendly analytics one-liner:
<script defer data-domain="docs.example.com" src="https://plausible.io/js/script.js"></script>
Google Analytics 4 is two tags — an external loader and an inline bootstrap; both go in the slot together:
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments); }
gtag("js", new Date());
gtag("config", "G-XXXXXXX");
</script>
The slot is not only for analytics. A verification <meta> or a preconnect works the same way:
<meta name="google-site-verification" content="…">
<link rel="preconnect" href="https://plausible.io">
3. Keep CSS in the setting, not the slot#
The two override channels do not overlap — use one each:
- Colours, type, the seven CSS seeds →
MDJANGO_EXTRA_CSS, a setting (see Change the colours and type). - Scripts and
<head>tags → the head slot.
head_extra.html is the only template mdjango supports overriding. Shadowing any other component —
the header, the base document, the page — is unsupported: a project that needs different chrome (a
logo, a footer, a different layout) has outgrown mdjango.
4. The export picks it up too#
If you export a static site, mdjango_build renders through your
project's own template loaders, so your head_extra.html lands in every exported page with no extra
step. One constraint: the export does not rewrite URLs, so tags in the slot must use absolute
URLs — as third-party snippets already do. A relative src would resolve against the page's own deep
path and 404.
What the slot cannot do for you#
- A third-party script runs only where its host is reachable. Opened from disk (a
file://export with no server) or under a Content-Security-Policy that blocks the host, it silently does nothing and the page still renders. This is the trade-off for a tag mdjango does not ship — the external dependency is yours to accept. If your site sets a strict CSP, add the analytics host to it, the way the production guide notes you must already allow mdjango's own inline theme script. - The slot is head-only. It cannot add a footer, change the header, or alter the layout. That is
by design: it renders inside
<head>, so it reaches nothing else.