Guide: Customising output¶
{% seo_head %} renders everything in one call. When you need finer-grained
control, such as rendering individual blocks in specific places or changing the
HTML structure, you have two mechanisms: granular template tags and template overrides.
Granular template tags¶
Instead of {% seo_head %}, use individual tags to place each block exactly
where you want it:
{% load seo_suite %}
<head>
<meta charset="utf-8">
{% seo_title %}
{% seo_meta %}
{% seo_canonical %}
{% seo_hreflang %}
{% seo_opengraph %}
{% seo_twitter %}
{% seo_jsonld %}
{% seo_extra_head %}
</head>
Each tag reads the same seo context variable set by the context processor or
SeoViewMixin. All tags are safe to call on a page with no SEO context; they
render nothing.
| Tag | Renders |
|---|---|
{% seo_title %} |
<title> element |
{% seo_meta %} |
meta description, meta keywords, meta robots |
{% seo_canonical %} |
<link rel="canonical"> |
{% seo_hreflang %} |
<link rel="alternate" hreflang> elements |
{% seo_opengraph %} |
<meta property="og:*"> elements |
{% seo_twitter %} |
<meta name="twitter:*"> elements |
{% seo_jsonld %} |
<script type="application/ld+json"> blocks |
{% seo_extra_head %} |
Extra fragments from registered renderers and the seo_head_rendering signal |
Rendering h1 in the body¶
The h1 field is not rendered by any of the head tags (it goes in the <body>,
not <head>). Access it via the context variable:
Overriding the HTML templates¶
Every partial template is stored under templates/seo_suite/ and can be
overridden by placing your own version in any app that comes before seo_suite
in INSTALLED_APPS, or in a DIRS-based template directory.
The template names and what they receive:
| Template | Context variables |
|---|---|
seo_suite/head.html |
seo, jsonld_blocks (list of serialised strings), extra_fragments |
seo_suite/_title.html |
seo |
seo_suite/_meta.html |
seo |
seo_suite/_canonical.html |
seo |
seo_suite/_hreflang.html |
seo |
seo_suite/_opengraph.html |
seo |
seo_suite/_twitter.html |
seo |
seo_suite/_jsonld.html |
jsonld_blocks |
seo_suite/_extra_head.html |
extra_fragments |
Example: add a custom title wrapper¶
Create templates/seo_suite/_title.html in your project:
{# myproject/templates/seo_suite/_title.html #}
{% if seo.full_title %}
<title>{{ seo.full_title }}</title>
<meta property="og:title" content="{{ seo.full_title }}">
{% endif %}
The seo object is a FinalizedSeoMetadata instance; see
SeoMetadata attributes for all available
properties.
Example: add a nonce to JSON-LD script tags¶
{# myproject/templates/seo_suite/_jsonld.html #}
{% for block in jsonld_blocks %}
<script type="application/ld+json" nonce="{{ request.csp_nonce }}">{{ block }}</script>
{% endfor %}
Adding extra head content via signals¶
The seo_head_rendering signal fires every time {% seo_head %} or
{% seo_extra_head %} renders. Receivers can return an HTML string (or a list
of strings) to be appended to the <head>:
# myapp/apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
from seo_suite.signals import seo_head_rendering
seo_head_rendering.connect(my_head_fragment)
def my_head_fragment(sender, request, metadata, **kwargs):
if metadata and metadata.canonical_url:
return f'<link rel="preconnect" href="https://fonts.gstatic.com">'
return None
Receivers may return a single string, a list of strings, or None. All
non-empty strings are collected and rendered by {% seo_extra_head %}.
Accessing the seo object in templates¶
Whether you use {% seo_head %} or individual tags, the full
FinalizedSeoMetadata object is available as {{ seo }} in any template that
uses the seo_suite.context.seo context processor. Useful attributes:
{{ seo.full_title }} {# title + title_suffix #}
{{ seo.title }} {# title only #}
{{ seo.h1 }} {# page heading #}
{{ seo.meta_description }} {# meta description string #}
{{ seo.canonical_url }} {# absolute canonical URL #}
{{ seo.robots }} {# robots directive #}
{{ seo.og }} {# dict of og:* values #}
{{ seo.twitter }} {# dict of twitter:* values #}