When we talk to content teams who've just moved from WordPress to Wagtail, the reaction is usually the same: they love the interface, but they feel like they're only using a small part of it. They're right. Wagtail's editorial features run deep, and many of the best ones require deliberate configuration to surface.
This post is aimed at developers building Wagtail sites and at content teams who want to know what to ask for. Every feature here is either built into Wagtail or achievable with a single third-party package.
Snippets: reusable content your editors control
Snippets are Wagtail's system for content objects that aren't pages — things like testimonials, staff profiles, navigation items, banners, or call-to-action blocks. They're managed through the Wagtail admin, editable by non-technical users, and choosable from within StreamField.
fromwagtail.snippets.modelsimportregister_snippet @register_snippetclassTestimonial(models.Model): author_name = models.CharField(max_length=100) author_role = models.CharField(max_length=100, blank=True) quote = models.TextField() published = models.BooleanField(default=True) panels = [ FieldPanel("author_name"), FieldPanel("author_role"), FieldPanel("quote"), FieldPanel("published"), ]def__str__(self):returnf"{self.author_name} — {self.quote[:50]}"
Editors manage testimonials independently of any page, and a SnippetChooserPanel or SnippetChooserBlock lets them choose which ones to embed on a given page. Update the testimonial once, and every page that uses it reflects the change immediately.
From Wagtail 4.0, snippets gained preview, draft, and publish states — meaning snippet content can go through the same approval workflow as pages. This is significant: your marketing team can draft a new banner, preview it in context, and have it approved before it goes live.
Bulk actions
Wagtail's page explorer supports bulk operations via the checkboxes on the page listing view. Out of the box: bulk publish, bulk unpublish, bulk move, and bulk delete.
For a site with a large content team publishing frequently, bulk publish alone saves hours per week — select all 20 articles you've been drafting, publish in one click, done. No clicking into each page individually.
You can also add custom bulk actions in code, which is useful for domain-specific operations like "export selected pages to CSV" or "mark selected events as featured":
fromwagtail.snippets.bulk_actionsimportSnippetBulkActionclassMarkFeaturedAction(SnippetBulkAction): display_name ="Mark as featured"action_type ="mark_featured"aria_label ="Mark selected items as featured"@classmethoddefexecute_action(cls, objects, **kwargs):forobjinobjects: obj.is_featured =Trueobj.save(update_fields=["is_featured"])
Scheduled publishing
Every Wagtail page can be set to publish at a specific date and time — no plugin required, no cron job to configure manually. Editors set the go-live date directly in the publishing sidebar.
For this to work, you need wagtail.contrib.frontend_cache configured (or equivalent cache invalidation), and a process running publish_scheduled management command on a schedule. In most deployments, that's a simple cron entry or a Celery periodic task:
# Celery beat configCELERY_BEAT_SCHEDULE = {"publish-scheduled-pages": {"task":"wagtail.contrib.scheduled_publishing.tasks.publish_scheduled","schedule": crontab(minute="*/5"), }, }
Once that's in place, editors can schedule an entire campaign of content — news articles, event pages, landing pages — weeks in advance, confident it'll go live exactly when intended.
You can also set an expiry date — pages automatically unpublish themselves on that date. Useful for time-limited offers, event pages, or any content that should stop being publicly accessible after a known point.
Revision history and comparison
Wagtail stores every saved version of every page automatically. Editors access the full history from the page's action menu — every save, every publish, timestamped and attributed to the user who made it.
Better still: Wagtail has a side-by-side comparison view that highlights exactly what changed between any two revisions. Text additions are highlighted green, deletions in red. This is invaluable for content review: "what exactly changed in the last edit before it went live?" — answered in one click.
Revisions also power the revert feature: if an editor accidentally deletes a section, they can restore any previous version without bothering a developer. This alone eliminates a category of support request we used to see regularly.
Workflow and moderation
Wagtail ships with a built-in workflow system that lets you require content to pass through one or more approval steps before it can be published. A typical configuration:
- Editor creates a draft and submits it for review
- Senior editor is notified; they review and either approve or request changes
- On approval, the page moves to the publish queue (or publishes automatically, depending on configuration)
Setting this up in code takes about 20 lines:
fromwagtail.modelsimportTask, Workflow, WorkflowPage# Create an approval taskapproval_task = GroupApprovalTask.objects.create( name="Senior editor approval") approval_task.groups.add(Group.objects.get(name="Senior Editors"))# Create a workflow with that taskworkflow = Workflow.objects.create(name="Standard review") WorkflowTask.objects.create( workflow=workflow, task=approval_task, sort_order=1, )# Apply to all pages of a typeWorkflowPage.objects.create( workflow=workflow, page=NewsIndexPage.objects.first(), )
Workflow state is surfaced directly in the Wagtail admin — editors see at a glance which pages are in review, which have been rejected with feedback, and which are approved and ready to publish.
Image renditions and focal points
Wagtail's image system generates renditions — resized, cropped, format-converted versions of uploaded images — on demand and caches them. Editors set a focal point on each image: the subject of the photo that should be preserved when the image is cropped to different aspect ratios.
In templates, you request the rendition you need:
{% image page.hero_image fill-1200x630-c100 as hero %}
<img src="{{ hero.url }}" width="{{ hero.width }}" height="{{ hero.height }}" alt="{{ page.hero_image.title }}">
The fill-1200x630-c100 filter crops to exactly 1200×630px, using 100% of the focal area. If the editor set a focal point on the face in the photo, that face stays in frame regardless of crop ratio. No manual resizing, no uploading multiple versions of the same image.
Configuring site search
Wagtail has a built-in search system that works out of the box with the database backend. For production sites, we swap it for the Elasticsearch or OpenSearch backend, which supports full-text search, field boosting, and faceted filtering.
From an editor's perspective, the key lever is controlling which pages and which fields appear in search results — configured in the model's search_fields:
search_fields = Page.search_fields + [
index.SearchField("body", boost=1),
index.SearchField("intro", boost=2),
index.FilterField("is_published"),
index.FilterField("content_type"),
]
The boost values control ranking — a match in the intro counts twice as much as a match in the body. Editors see the effect immediately in the Wagtail admin's global search, which uses the same index.
The biggest editorial quality-of-life improvement on any Wagtail project is making sure these features are actually turned on and explained during onboarding. A 30-minute walkthrough of revision history, scheduled publishing, and workflow with a content team pays for itself on the first content crisis — and there's always a first content crisis.