wagtail-link-field
A flexible link block and field for Wagtail CMS that supports internal pages, external URLs, documents, anchors, email, phone, and custom links.
Features
- LinkBlock: A StreamField block for flexible link selection
- LinkField: A model field that stores link data as JSON
- LinkPanel: A panel for editing LinkField in Wagtail admin
- Template tags: For rendering links in templates
- Dynamic field visibility: JavaScript shows/hides relevant fields based on selected link type
- Clean JSON storage: Only relevant fields are stored, irrelevant ones are stripped
Installation
pip install wagtail-link-field
Add to your INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"wagtail_link_field",
# ...
]
Usage
LinkBlock in StreamField
from wagtail.fields import StreamField
from wagtail import blocks
from wagtail_link_field import LinkBlock
class MyPage(Page):
body = StreamField([
("link", LinkBlock()),
], blank=True, use_json_field=True)
# Restrict to specific link types
body_restricted = StreamField([
("link", LinkBlock(link_types=["internal-link", "external-link"])),
], blank=True, use_json_field=True)
LinkField with LinkPanel
from django.db import models
from wagtail_link_field import LinkField, LinkPanel
class MyPage(Page):
cta = LinkField(blank=True, null=True)
content_panels = Page.content_panels + [
LinkPanel("cta"),
# Or restrict link types:
LinkPanel("cta_restricted", link_types=["internal-link", "external-link"]),
]
Accessing link data in Python
Both LinkBlock values and LinkField values provide the same methods:
# From a LinkField
page = MyPage.objects.first()
link = page.cta # Returns LinkFieldValue instance
# From a LinkBlock in StreamField
for block in page.body:
if block.block_type == 'link':
link = block.value # Returns LinkValue instance
# Both provide the same methods:
url = link.url() # Computed URL (e.g., "/about/", "https://example.com", "mailto:test@example.com")
is_external = link.is_external() # True for external, email, phone, document, custom links
title = link.title() # Page/document title if available, otherwise None
safe_title = link.safe_title() # Formatted title (page title or cleaned URL/email/phone)
action = link.action # Link type: "internal-link", "external-link", etc.
Using in templates
Load the template tags:
{% load wagtail_link_field_tags %}
Option 1: Use render_link tag (recommended)
Automatically renders a complete <a> tag with proper attributes:
{# Basic usage - uses safe_title automatically #}
{% render_link page.cta %}
{# Output: <a href="/about/">About Us</a> #}
{# With CSS class #}
{% render_link page.cta css_class="btn btn-primary" %}
{# With extra attributes #}
{% render_link page.cta extra_attrs='data-action="click"' %}
{# Works with StreamField blocks too #}
{% for block in page.body %}
{% if block.block_type == 'link' %}
{% render_link block.value css_class="content-link" %}
{% endif %}
{% endfor %}
The render_link tag automatically:
- Adds
target="_blank"andrel="noopener noreferrer"for external links - Uses
safe_title()for the link text (page title or formatted URL/email/phone)
Option 2: Use filters for custom markup
{# Get the URL #}
<a href="{{ page.cta|link_url }}">Click here</a>
{# Get page/document title (returns None if not available) #}
<a href="{{ page.cta|link_url }}">
{{ page.cta|link_title|default:"Read more" }}
</a>
{# Get formatted title (always returns something user-friendly) #}
<a href="{{ page.cta|link_url }}">
{{ page.cta|link_safe_title }}
</a>
{# Build custom markup #}
{% if page.cta %}
<a href="{{ page.cta|link_url }}"
class="{% if page.cta.is_external %}external-link{% endif %}">
{{ page.cta|link_safe_title }}
</a>
{% endif %}
Option 3: Access methods directly
{# Access methods on the value object #}
<a href="{{ page.cta.url }}"
{% if page.cta.is_external %}target="_blank" rel="noopener noreferrer"{% endif %}>
{{ page.cta.safe_title }}
</a>
{# Check link type #}
{% if page.cta.action == "internal-link" %}
<a href="{{ page.cta.url }}" class="internal">{{ page.cta.title|default:page.cta.safe_title }}</a>
{% elif page.cta.action == "email-link" %}
<a href="{{ page.cta.url }}" class="email">{{ page.cta.safe_title }}</a>
{% endif %}
When to use each approach:
{% render_link %}: Quick, automatic rendering with sensible defaults- Filters (
|link_url,|link_safe_title): Custom HTML structure but simpler syntax - Direct methods (
.url,.safe_title): Maximum control and conditional logic
Available link types
| Type | Description | Fields |
|---|---|---|
internal-link |
Link to a Wagtail page | internal_link, page_query_string |
document-link |
Link to a Wagtail document | document_link |
external-link |
External URL | external_link |
anchor-link |
Same-page anchor | anchor_link |
email-link |
Email address | url_link (auto-prefixed with mailto:) |
phone-link |
Phone number | url_link (auto-prefixed with tel:) |
custom-link |
Custom URL scheme | url_link |
Restricting link types
Pass link_types to LinkBlock or LinkPanel:
# Only internal and external links
LinkBlock(link_types=["internal-link", "external-link"])
# Only documents
LinkPanel("download", link_types=["document-link"])
Optional links
Set required=False for optional links:
# LinkBlock (default is required=True)
LinkBlock(required=False)
# LinkField (use blank=True, null=True)
cta = LinkField(blank=True, null=True)
Development
Setup
# Clone the repo
git clone https://github.com/infofactory/wagtail-link-field.git
cd wagtail-link-field
# Install with uv
uv sync --extra testing
Running tests
# Run tests
python testmanage.py test
# Run with coverage
python -m coverage run testmanage.py test
python -m coverage report -m
Running linting
ruff check .
ruff format --check .
Running tox (matrix testing)
tox
License
BSD-3-Clause. See LICENSE for details.
Metadata
Release files for wagtail-link-field 0.1.6
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| wagtail_link_field-0.1.6.tar.gz | 17.4 kB | With provenance | Details for wagtail_link_field-0.1.6.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| wagtail_link_field-0.1.6-py3-none-any.whl | Python 3 | none | any | Details for wagtail_link_field-0.1.6-py3-none-any.whl |
Total release size: 38.2 kB
Release files / wagtail_link_field-0.1.6.tar.gz
| Download URL | wagtail_link_field-0.1.6.tar.gz |
|---|---|
| Size | 17.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fd5022b2998a88551348d654329e8753cb1e2d20dee23453f22f4d31d01f9a14
|
|
BLAKE2b-256 checksum How to use checksums |
d21ea1cb2d9ab8c2e71951261fb8349143e045217f2a441dc332d2126bf360c0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.
Transparency logRelease files / wagtail_link_field-0.1.6-py3-none-any.whl
| Download URL | wagtail_link_field-0.1.6-py3-none-any.whl |
|---|---|
| Size | 20.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
294eb7a448a59e7ce6f8c30bd838259d5943e177a5db2b44571e43e87ba345ab
|
|
BLAKE2b-256 checksum How to use checksums |
3a7be942e5c5ddbe61f4478ada76d3ec3fa91fae19b556676aaf41fd46c35396
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.
Transparency logSecurity for wagtail-link-field 0.1.6
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for wagtail_link_field-0.1.6.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 14, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.