On deleting a page, image, document or snippet, the confirmation screen now provides a summary of where the object is used, allowing users to see the effect that deletion will have elsewhere on the site. This also prevents objects from being deleted in cases where deletion would be blocked by an `on_delete=PROTECT` constraint. This feature was developed by Sage Abdullah.
The image library can now be configured to allow uploading SVG images. These are handled by the `{% image %}` template tag as normal, with some limitations on image operations - for full details, see [](svg_images). This feature was developed by Joshua Munn, and sponsored by [YouGov](https://yougov.com/).
Support for adding custom validation logic to StreamField blocks has been formalised and simplified. For most purposes, raising a `ValidationError` from the block's `clean` method is now sufficient; more complex behaviours (such as attaching errors to a specific child block) are possible through block-specific subclasses of `ValidationError`. For more details, see [](streamfield_validation). This feature was developed by Matt Westcott.
Wagtail’s icon set is now fully updated, customisable, and extendable. Built-in icons are now based on the latest [FontAwesome](https://fontawesome.com/) visuals, with capabilities to both customise existing icons as well as add new ones. In particular, this includes:
* A new `{% icon %}` icon template tag to reuse icons in custom templates.
* A `register_icons` hook to register new icons and override existing ones.
For more details, see our new [icons documentation](icons).
This has been made possible thanks to a multi-year refactoring effort to migrate all icons to SVG. Thank you to all contributors who participated in this effort: Coen van der Kamp, LB (Ben) Johnston, Dan Braghis, Daniel Kirkham, Sage Abdullah, Thibaud Colas, Scott Cranfill, Storm Heg, Steve Steinwand, Jérôme Lebleu, Abayomi Victory.
Those improvements were implemented by Albina Starykova as part of an [Outreachy internship](https://wagtail.org/blog/introducing-wagtails-new-accessibility-checker/), with support from mentors Thibaud Colas, Sage Abdullah, and Joshua Munn.
Wagtail’s admin interface now supports dark mode. The new dark theme can be enabled in account preferences, as well as configuring permanent usage of the light theme, or following system preferences.
We hope this new theme will bring accessibility improvements for users who prefer light text on dark backgrounds, and energy usage efficiency improvements for users of OLED monitors. This feature was developed by Thibaud Colas, with designs from Ben Enright.
For more details, see [](wagtailsnippets_custom_admin_views).
Developed by Sage Abdullah, these features were implemented as part of [RFC 85: Snippets parity with ModelAdmin](https://github.com/wagtail/rfcs/pull/85). We will start the deprecation process of the ModelAdmin contrib package in the next feature release and publish it as a separate package for users who wish to continue using it. The ModelAdmin package will be removed in Wagtail 6.0.
* Ensure selected collection is kept when navigating from documents or images listings to add multiple views & upon upload (Aman Pandey, Bojan Mihelac)
* Remove confusing `SettingsPanel` reference in the page editing `TabbedInterface` example as `SettingsPanel` no longer shows anything as of 4.1 (Kenny Wolf, Julian Bigler)
The following features deprecated in Wagtail 3.0 have been fully removed. See [Wagtail 3.0 release notes](/releases/3.0) for details on these changes, including how to remove usage of these features:
* The modules `wagtail.core`, `wagtail.tests`, `wagtail.admin.edit_handlers` and `wagtail.contrib.forms.edit_handlers` are removed.
* The field panel classes `StreamFieldPanel`, `RichTextFieldPanel`, `ImageChooserPanel`, `DocumentChooserPanel` and `SnippetChooserPanel` are removed.
* The `ModelAdmin.get_form_fields_exclude` method is no longer passed a `request` argument.
* The `ModelAdmin.get_edit_handler` method is no longer passed a `request` or `instance` argument.
* The `widget_overrides`, `required_fields`, `required_formsets`, `bind_to`, `render_as_object` and `render_as_field` methods on `Panel` (previously `EditHandler`) are removed.
The following features deprecated in Wagtail 4.0 have been fully removed. See [Wagtail 4.0 release notes](/releases/4.0) for details on these changes, including how to remove usage of these features:
* The `wagtail.contrib.settings.models.BaseSetting` class is removed.
* The `Page.get_latest_revision_as_page` method is removed.
* The `page` and `page_id` properties and `as_page_object` method on `Revision` are removed.
* The JavaScript functions `createPageChooser`, `createSnippetChooser`, `createDocumentChooser` and `createImageChooser` are removed.
* The `wagtail.contrib.modeladmin.menus.SubMenu` class is removed.
* Subclasses of `wagtail.contrib.modeladmin.helpers.AdminURLHelper` are now required to accept a `base_url_path` keyword argument on the constructor.
* The `wagtail.admin.widgets.chooser.AdminChooser` class is removed.
* The `wagtail.snippets.views.snippets.get_snippet_edit_handler` function is removed.
### Elasticsearch backend no longer performs partial matching on `search`
The `search` method on pages, images and documents, and on the backend object returned by `wagtail.search.backends.get_search_backend()`, no longer performs partial word matching when the Elasticsearch backend is in use. Previously, a search query such as `Page.objects.search("cat")` would return results containing the word "caterpillar", while `Page.objects.search("cat", partial_match=False)` would only return results for the exact word "cat". The `search` method now always performs exact word matches, and the `partial_match` argument has no effect. This change makes the Elasticsearch backend consistent with the database-backed full-text search backends.
To revert to the previous partial word matching behaviour, use the `autocomplete` method instead - for example, `Page.objects.autocomplete("cat")`. It may also be necessary to add an [](wagtailsearch_index_autocompletefield) entry for the relevant fields on the model's `search_fields` definition, as the old `SearchField("some_field", partial_match=True)` format is no longer supported.
The `partial_match` argument on `search` and `SearchField` is now deprecated, and should be removed from your code; it will be dropped entirely in Wagtail 6.
### ReferenceIndex no longer tracks models used outside of Wagtail
When introduced in Wagtail 4.1, the `ReferenceIndex` model recorded references across all of a project's models by default. The default set of models being indexed has now been changed to only those used within the Wagtail admin, specifically:
* all Page types
* Images
* Documents
* models registered as Snippets
* models registered with ModelAdmin
This change will remove the impact of the indexing on non-Wagtail apps and models.
If you have models that still require reference indexing, and which are not registered as snippets or with ModelAdmin, you will need to explicitly register them within your app's `AppConfig.ready()` method. See [Reference Index](registering_a_model_for_indexing) for further details.
The use of `wagtail_reference_index_ignore` to prevent indexing of models is unchanged, but in many cases it may no longer be necessary.
It is recommended that the `rebuild_references_index` management command is run after the upgrade to remove any unnecessary records.
The undocumented `Page.get_static_site_paths` method (which returns a generator of URL paths for use by static site generator packages) has been removed. Packages relying on this functionality should provide their own fallback implementation.
All imports will need to be updated and migrations will need to be run via a management command, some imports will still work with a warning until a future version.
The `search_garbage_collect` command used to remove old stored search queries and daily hits has been moved to [`searchpromotions_garbage_collect`](searchpromotions_garbage_collect).
If there are custom styles in place for the `ModelAdmin`'s header content or more complex template overrides in use, there are a few changes for the following classes to be aware of.
The slug field JavaScript behaviour was previously attached to any field with an ID of `id_slug`, this has now changed to be any field with the appropriate Stimulus data attributes.
If using a custom edit handler or set of panels for page models, the correct widget will now need to be used for these data attributes to be included. This widget will use the `WAGTAIL_ALLOW_UNICODE_SLUGS` Django setting.
```python
from wagtail.admin.widgets.slug import SlugInput
# ... other imports
class MyPage(Page):
promote_panels = [
FieldPanel("slug", widget=SlugInput),
# ... other panels
]
```
Additionally, the slug behaviour can be attached to any field easily by including the following attributes in HTML or via Django's widget `attrs`.
The mechanism for synchronising the slug field with the page title has changed, and is no longer hard-coded to activate on fields named 'title'. Notably, this change affects page panel definitions that use `FieldPanel("title")` directly (rather than the convention of extending `Page.content_panels`), as well as non-page models such as snippets.
To assist in upgrading these definitions, Wagtail 5.0.2 provides a new [](title_field_panel) class to be used in place of `FieldPanel("title")`. For example:
```python
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
# ...
content_panels = [
MultiFieldPanel([
FieldPanel("title"),
FieldPanel("subtitle"),
]),
]
```
should become:
```python
from wagtail.admin.panels import FieldPanel, MultiFieldPanel, TitleFieldPanel
# ...
content_panels = [
MultiFieldPanel([
TitleFieldPanel("title"),
FieldPanel("subtitle"),
]),
]
```
If you have made deeper customisations to this behaviour, or are unable to upgrade to Wagtail 5.0.2 or above, please read on as you may need to make some changes to adopt the new approach.
The title field will sync its value with the slug field on Pages if the Page is not published and the slug has not been manually changed. This JavaScript behaviour previously attached to any field with an ID of `id_title`; this has now changed to be any field with the appropriate Stimulus data attributes.
There is a new Stimulus controller `w-sync` which allows any field to change one or more other fields when its value changes, the other field in this case will be the slug field (`w-slug`) with the id `id_slug`.
If you need to hook into this behaviour, the new approach will now correctly dispatch `change` events on the slug field. Alternatively, you can modify the data attributes on the fields to adjust this behaviour.
To adjust the target field (the one to be updated), you cam modify `"data-w-sync-target-value"`, the default being `"body:not(.page-is-live) [data-edit-form] #id_slug"` (find the field with id `id_slug` when the page is not live).
To adjust what triggers the initial check (to see if the fields should be in sync), or the trigger the sync, you can use the Stimulus `data-action` attributes.
Above we have adjusted these attributes to add a 'change' event listener to trigger the sync and also adjusted to look for a field with `some_other_slug` instead.
### Auto height/size text area widget now relies on data attributes
If you are using the `wagtail.admin.widgets.AdminAutoHeightTextInput` only, this change will have no impact when upgrading. However, if you are relying on the global `autosize` function at `window.autosize` on the client, this will no longer work.
It is recommended that the `AdminAutoHeightTextInput` widget be used instead. You can also adopt the `data-controller` attribute and this will now function as before. Alternatively, you can simply add the required Stimulus data controller attribute as shown below.
The `button-longrunning` class usage has been updated to use the newly adopted Stimulus approach, the previous data attributes will be deprecated in a future release.
If using the old approach, ensure any HTML templates are updated to the new approach before the next major release.
Stimulus [targets](https://stimulus.hotwired.dev/reference/targets) and [actions](https://stimulus.hotwired.dev/reference/actions) can be leveraged to revise the behaviour via data attributes.
*`<button ... data-w-progress-duration-value="500" ...>` - custom duration can be declared on the element
*`<button ... class="custom-button" data-w-progress-active-class="custom-button--busy" ...>` - custom 'active' class to replace the default `button-longrunning-active` (must be a single string without spaces)
*`<button ... ><strong data-w-progress-target="label">{% trans 'Create' %}</strong></button>` - any element can be the button label (not just `em`)
*`<button ... data-action="w-progress#activate focus->w-progress#activate" ...>` - any event can be used to trigger the in progress behaviour
*`<button ... data-action="w-progress#activate:once" ...>` - only trigger the progress behaviour once
*`<button ... data-action="readystatechange@document->w-progress#activate:once" data-w-progress-duration-value="5000" disabled ...>` - disabled on load (once JS starts) and becomes enabled after 5s duration
### JavaScript `window.addMessages` replaced with event dispatching
The undocumented `window.addMessage` function is no longer available and will throw an error if called, if similar functionality is required use DOM Event dispatching instead as follows.
The client-side handling of StreamField validation errors has been updated. The JavaScript classes `StreamBlockValidationError`, `ListBlockValidationError`, `StructBlockValidationError` and `TypedTableBlockValidationError` have been removed, and the corresponding Python classes can no longer be serialised using Telepath. Instead, the `setError` methods on client-side block objects now accept a plain JSON representation of the error, obtained from the `as_json_data` method on the Python class. Custom JavaScript code that works with these objects must be updated accordingly.
Additionally, the Python `StreamBlockValidationError`, `ListBlockValidationError`, `StructBlockValidationError` and `TypedTableBlockValidationError` classes no longer provide a `params` dict with `block_errors` and `non_block_errors` items; these are now available as the attributes `block_errors` and `non_block_errors` on the exception itself (or `cell_errors` and `non_block_errors` in the case of `TypedTableBlockValidationError`).
The ability to remove multiple snippet instances from the `DeleteView` and the undocumented `wagtailsnippets_{app_label}_{model_name}:delete-multiple` URL pattern have been removed. The view's functionality has been replaced by the delete action of the bulk actions feature introduced in Wagtail 4.0.
The delete bulk action view now also calls the `{before,after}_delete_snippet` hooks, in addition to the `{before,after}_bulk_action` hooks.
If you have customised the `IndexView` and/or `DeleteView` views in a `SnippetViewSet` subclass, make sure that the `delete_multiple_url_name` attribute is renamed to `delete_url_name`.
The template name for the index view of a snippet model has changed from `wagtailsnippets/snippets/type_index.html` and `wagtailsnippets/snippets/results.html` to `wagtailsnippets/snippets/index.html` and `wagtailsnippets/snippets/index_results.html`. In addition, the model index view that lists the snippet types now looks for the template `wagtailsnippets/snippets/model_index.html` before resorting to the generic index template. If you have customised these templates, make sure to update them accordingly.
The Wagtail icon font has been deprecated and will be removed in a future release, as it is now unused in Wagtail itself. There are no changes to make for any icons usage via dedicated APIs such as `icon` class properties. Any direct icon font usage needs to be converted to SVG icons instead, as documented in our [icons overview](icons).
To check whether your project uses the icon font, check for occurrences of:
* Loading of the `wagtail.woff` font file.
* Usage of `font-family: wagtail` in CSS.
*`icon-<name>` CSS classes outside of SVG elements.
### Deprecated icons
The following icons are unused in Wagtail itself and will be removed in a future release. If you are using any of these icons, please replace them with an alternative (see our full [list of icons](icons)), or re-add the icon to your own project.
### Snippets `get_admin_url_namespace()` and `get_admin_base_path()` moved to `SnippetViewSet`
The undocumented `get_admin_url_namespace()` and `get_admin_base_path()` methods that were set on snippet models at runtime have been moved to the {class}`~wagtail.snippets.views.snippets.SnippetViewSet` class. If you use these methods, you could access them via {meth}`SnippetModel.snippet_viewset.get_admin_url_namespace() <wagtail.snippets.views.snippets.SnippetViewSet.get_admin_url_namespace>` and {meth}`SnippetModel.snippet_viewset.get_admin_base_path() <wagtail.snippets.views.snippets.SnippetViewSet.get_admin_base_path>`, respectively.
### Snippets `get_usage()` and `usage_url()` methods removed
The undocumented `get_usage()` and `usage_url()` methods that were set on snippet models at runtime have been removed. Calls to the `get_usage()` method can be replaced with `wagtail.models.ReferenceIndex.get_grouped_references_to(object)`. The `usage_url()` method does not have a direct replacement, but the URL name can be retrieved via {meth}`SnippetModel.snippet_viewset.get_url_name("usage") <wagtail.admin.viewsets.base.ViewSet.get_url_name>`, which can be used to construct the URL with {func}`~django.urls.reverse`.