# Wagtail 7.1 release notes

_August 4, 2025_

```{contents}
---
local:
depth: 1
---
```

Check out our upcoming [Wagtail Space 2025](https://wagtail.org/wagtail-space-2025/) event 🚀! Online, ✨ free ✨, October 8-10.

## What's new

### Site setting permissions

The [](../reference/contrib/settings) app now allows permission over site settings to be granted on a per-site basis. This makes it possible to give non-superuser accounts full control over the configuration of an individual site. This feature was developed by Matt Westcott.

### Previews support for generic and site settings

The [](../reference/contrib/settings) app now allows previews to be enabled for generic and site setting models through `PreviewableMixin`. This makes it possible to preview your site while editing the settings. For more details, see [](enabling_previews_for_settings). This feature was developed by Sébastien Corbin and Sage Abdullah.

### Media listings improvements

Image listings now support both grid and list layouts, with a simple toggle for users to switch between the two. The list layout gives you more information about the images, such as the file name, collection, creation date, and usage count. This helps CMS users manage larger collections of images, and makes it possible to display more metadata about images in the future.

The documents listing now also display the usage count for each document, making it easier to see which documents are being used on your site.

Thank you to Joel William for implementing this as part of the [Google Summer of Code program](https://wagtail.org/blog/four-contributors-for-gsoc-2025/), with support from Coen van der Kamp, Sage Abdullah, Thibaud Colas, Ben Enright.

### Collapsible StructBlock for block settings

To make it easier to navigate complex structured content, blocks created with `StructBlock` can now be collapsed and expanded like other structured block types. A common need for this is block settings, where the block supports a lot of optional fields:

```python
class BlockQuote(StructBlock):
    text = TextBlock()
    attribute_name = CharBlock(blank=True, required=False, label="e.g. Mary Berry")
    # Fields within the settings block will be available but hidden by default.
    settings = ThemeSettingsBlock(collapsed=True)
```

For more information, see [](custom_editing_interfaces_for_structblock) and [](streamfield_block_reference). This feature was implemented by Sage Abdullah, with support from the Wagtail UI team.

### Preview and user bar improvements for headless setups

The live preview and user bar features have been refactored and improved to work better in headless setups. The preview panel can now load a cross-domain headless frontend (provided by the wagtail-headless-preview package). The Wagtail user bar has been refactored into a template component, allowing it to be rendered independently and loaded by the headless frontend. Incorporating the user bar into the frontend enables features in the page editor, such as:

-   Live preview scroll restoration
-   Accessibility checks
-   Content metrics (word count, reading time)

For more details, see the documentation on [enabling the user bar](headless_user_bar) and [accessibility checker](headless_accessibility_checker) in headless setups. This feature was developed by Sage Abdullah.

### Documentation for admin UI components

Wagtail’s developer documentation now includes [experimental docs to reuse client-side code](javascript_components) and [UI components](ui_components).
This new documentation helps developers reuse existing components when creating customizations or extensions of the admin interface, so those customizations are simpler to build and maintain.
The **documentation is experimental**, as well as reuse of those UI components. This means a component's API may change in a minor release without going through the deprecation process. If you use these components in your own code, please ensure that you have a testing process in place to catch any breaking changes with each Wagtail upgrade.

This feature was implemented by Sage Abdullah, with support from the Wagtail UI team.

### Keyboard shortcuts improvements

The admin interface now supports more keyboard shortcuts to facilitate common actions: in this release, toggling the main navigation sidebar, and the minimap within the page editor.
It’s also now possible for users to disable all of Wagtail’s custom shortcuts via a dedicated setting in their user profile.

Thank you to Dhruvi Patel for implementing this as part of the [Google Summer of Code program](https://wagtail.org/blog/four-contributors-for-gsoc-2025/), with support from Scott Cranfill, Thibaud Colas, LB (Ben) Johnston.

### Client-side panels API

The panels mechanism used by the page and snippet editing forms now provides an API for client-side code to access the panel structure. This makes it possible to retrieve and manipulate the form contents without navigating the HTML structure of the page, which is not guaranteed to remain stable across releases of Wagtail. For further information, see [](../extending/client_side_panels). This feature was developed by Matt Westcott.

### Other features

 * Add iHeart oEmbed provider (Storm Heg)
 * Add locale-aware `NumberColumn` to display numbers in universal listings (Baptiste Mispelon)
 * Add ability for the header breadcrumbs to save their open/closed state across navigation & refresh (Srishti Jaiswal)
 * Render listing buttons as template components (Sage Abdullah)
 * Define default `GenericRelations` for `RevisionMixin` and `WorkflowMixin`, to avoid issues with deletion cascades (Sage Abdullah)
 * Update Twitter oEmbed provider to recognize x.com links (manu)
 * Document and relocate the `init_new_page` signal (Maciek Baron)
 * Use `requests` to access oEmbed endpoints, for more robust SSL certificate handling (Matt Westcott)
 * Ensure that bulk deletion views respect protected foreign keys (Sage Abdullah)
 * Add minimum length validation for `RichTextBlock` and `RichTextField` (Alec Baron)
 * Allow `SnippetChooserBlock`'s `icon` to take precedence over `SnippetViewSet.icon` (Matt Westcott)
 * Allow searching the users index on custom fields (Paul Craciunoiu, Sage Abdullah)
 * Support `preserve-svg` in Jinja2 image tags (Vishesh Garg)
 * Recognize `preserve-svg` as a filter when calling `Image.get_rendition` directly (Richard Allen)
 * Add support for `preserve-svg` for `Image.get_renditions`, picture, and srcset_image tags (Matt Westcott)
 * Include `TypedTableBlock` content when indexing for search (Charan T M)
 * Preserve query parameters when redirecting from the API `find` view to the `detail` view (Andrew Hosgood)
 * Add 'Edit' button to success message after copying page (Dhruvi Patel)
 * Restrict file dialog in multiple image uploader to the allowed image file types (Mustopha Mubarak O)
 * Raise clear error when non-StreamBlock is used as top-level block in StreamField (Clifford Gama)
 * Add type-to-confirm step when deleting large numbers of pages through bulk actions (Rachel Smith)
 * Add [`NoFutureDateValidator`](date_field_validation) to validate against dates in the future (Talha Rizwan)
 * Extract separate `background_position_x` and `background_position_y` properties from `AbstractRendition.background_position_style` (Chiemezuo Akujobi)
 * Add support for translated string concatenation in the locale selector when switching between a model's language in the admin (Matt Westcott, Ellie Walsh-O'Neill)
 * Switch to check / cross icons for users’ active state in users listing (Sage Abdullah)
 * Add API for extracting preview page content (Sage Abdullah)
 * Add [`form_attrs` support to all StreamField blocks](custom_editing_interfaces_for_structblock) (Sage Abdullah)
 * Improve `label_format` support for more widget types in StreamField (Sage Abdullah)
 * Update project template documentation to [include testing instructions](project_templates_testing) and include starting test file in template (Aditya (megatrron))
 * Add support for `preview_value` and `default` in `Block` meta options as callables for [dynamic previews within StreamField](configuring_block_previews) (Ziyao Yan, Sage Abdullah)
 * Make transliteration of titles to slugs locale-aware, and provide [further customization options](customizing_slug_widget) (LB (Ben) Johnston)
 * Always show block types for collapsed StreamField blocks (Thibaud Colas)

### Bug fixes

 * Handle lazy translation strings as `preview_value` for `RichTextBlock` (Seb Corbin)
 * Fix handling of newline-separated choices in form builder when using non-windows newline characters (Baptiste Mispelon)
 * Ensure `WAGTAILADMIN_LOGIN_URL` is respected when logging out of the admin (Antoine Rodriguez, Ramon de Jezus)
 * Fix behavior of `ViewSet.inject_view_methods` with multiple methods (Gorlik)
 * Preserve query strings in URLs submitted to CloudFront for invalidation (Jigyasu Rajput)
 * Handle non-JSON-safe fields in `exclude_fields_in_copy` (Matt Westcott)
 * Allow upload of AVIF images through image chooser on Firefox (Matt Westcott)
 * Accept any string beginning with 'y' as confirmation for `import_redirects` command (Matt Westcott)
 * Fix error when accessing the submissions listing view with a non-form page (Sage Abdullah)
 * Replace inline styles with CSS classes in HTML files (Srishti Jaiswal)
 * Refactor remaining inline styles to avoid unsafe-inline style-src CSP (Chiemezuo Akujobi)
 * Avoid breaking words in listing table cells unless specifically enabled (Ziyao Yan)
 * Ensure that "all items in listing" option on image / document bulk actions respects user permissions (Alex Morega)
 * Adjust accessibility dialog position based on user bar position (Anees Asghar, Sage Abdullah)
 * Correctly handle ordering API results by mixed ascending and descending order (Rohit Sharma, Mahmoud Nasser)
 * Ensure that draft changes to an editable `first_published_at` field are preserved on reloading (Talha Rizwan)
 * Remove ngram parser on MySQL that prevented autocomplete search from returning results (Vince Salvino)
 * Only enable ManifestStaticFilesStorage in production settings, to aid test running (M. Sumair Khokhar)
 * Update `BooleanColumn` icons so they can be distinguished without relying on color (Sage Abdullah)
 * Do not delete default homepage by ID in home app migration (Matt Westcott)
 * Update the start project template to align with Django's recommendation to have the `django.middleware.security.SecurityMiddleware` first (Brylie Christopher Oxley)
 * Ensure keyboard usage will correctly focus on new comments, including replies, when the side panel is open or closed (Dhruvi Patel)
 * Handle `help_text` kwarg in `FloatBlock` (Nick Smith)
 * Set `aria-invalid` on StreamField widgets input when there is an error (Sage Abdullah)
 * Make sure the label of panel collapse buttons is translatable (Thibaud Colas)
 * Ensure there is suitable padding for large numbers on pagination within universal listings (M. Sumair Khokhar)
 * Use the correct localized format utils in the `human_readable_date` template tag (Seb Corbin)
 * Ensure the editing of translation alias pages correctly shows links to the source page if the alias was created from a draft (Dan Braghis)
 * Support complex `order_by` expressions in database search backends (Seb Corbin)
 * Improve handling of capitalization of InlinePanel labels and headings (Stefan Hammer)

### Documentation

 * Add missing tag library imports to footer template code in tutorial (Dimaco)
 * Improve documentation around securing user-uploaded files (Jake Howard)
 * Introduce search_fields in a dedicated tutorial section instead of the introduction, see [](../tutorial/add_search.md) (Matt Westcott)
 * Add note about PDF XSS to security page (Matt Westcott)
 * Add documentation for how to [group blocks within the StreamField picker](block_grouping) (Gaurav Verma)
 * Clarify the user bar does not have moderation features (Sage Abdullah)
 * Add experimental documentation for admin [UI components and client-side code](admin_ui_reference) (Sage Abdullah)
 * Clean up JSDoc documentation comments, syntax, and update spelling where suitable (LB (Ben) Johnston)
 * Add more examples of all [images settings](wagtailimages_all_settings) and include `avif` in `WAGTAILIMAGES_EXTENSIONS` example (Thibaud Colas)
 * Add an example of using `data-edit-form` and interacting with the editor form and preview panel (Sage Abdullah)

### Maintenance

 * Refactor `get_embed` to remove `finder` argument which was only used for mocking in unit tests (Jigyasu Rajput)
 * Simplify handling of `None` values in `TypedTableBlock` (Jigyasu Rajput)
 * Remove squash.io configuration (Sage Abdullah)
 * Use `utf8mb4` charset and collation for MySQL test database (Sage Abdullah)
 * Add `django.contrib.postgres` to test settings `INSTALLED_APPS` (Sage Abdullah)
 * Add integrity & resolved checksums to package-lock.json (Sylvain Fankhauser)
 * Replace `SlugController` with more generic and reusable `CleanController` (LB (Ben) Johnston)
 * Remove outdated nginx / uWSGI example config files from `/etc` (LB (Ben) Johnston)
 * Use browser built-in `EventTarget` instead of `events.EventEmitter` from Webpack (Sage Abdullah)
 * Split code from `telepath/widgets.js` into separate files (Matt Westcott)
 * Update development UI guidelines to adopt best practices for image handling within the admin interface (Thibaud Colas)
 * Bump minimum Willow version to v1.11.0 to use Pillow's built-in AVIF support (Sage Abdullah)
 * Add overridable blocks in comments and checks side panel templates (Sage Abdullah)
 * Rewrite Tabs component frontend to use Stimulus and TypeScript (Sai Srikar Dumpeti, LB (Ben) Johnston, Sage Abdullah)
 * Upgrade django-tasks to 0.8.x (Jake Howard)
 * Refresh accessibility targets in contributing docs (Thibaud Colas)

## Upgrade considerations - changes affecting all projects

### Changes to `label_format` behavior in `StructBlock`

The `label_format` option for `StructBlock` has been changed to always show the block's original `label` when the block is collapsed, with the `label_format` string shown after it as a summary instead of replacing the `label`. This means that if you had previously set `label_format` to include (or be the same as) the `label`, you would likely want to remove the original `label`'s text from the `label_format` string.

## Upgrade considerations - changes affecting Wagtail customizations

### Deprecation of `PageListingButton`, `SnippetListingButton`, and `UserListingButton`

The `PageListingButton`, `SnippetListingButton`, and `UserListingButton` classes have been deprecated in favour of the generic `ListingButton` and `Button` classes.

The `PageListingButton` class was previously documented as part of the [`register_page_listing_buttons`](register_page_listing_buttons) hook. If you used this hook to add buttons to the page listing, you should now replace `wagtail.admin.widgets.PageListingButton` with `wagtail.admin.widgets.ListingButton`. Using the `PageListingButton` class will continue to work for now, but will raise a deprecation warning.

The `SnippetListingButton` and `UserListingButton` classes were previously documented as part of the [`register_snippet_listing_buttons`](register_snippet_listing_buttons) and [`register_user_listing_buttons`](register_user_listing_buttons) hooks, respectively. If you used these hooks to add buttons to the snippet or user listing, you should now replace `wagtail.snippets.widgets.SnippetListingButton` and `wagtail.users.widgets.UserListingButton` with one of the following:

-   Use `wagtail.admin.widgets.Button` if you want the button to appear as an item inside the "More" actions dropdown in the listing.
-   Use `wagtail.admin.widgets.ListingButton` if you want the button to appear as a top-level button in the listing.

Using the `SnippetListingButton` or `UserListingButton` classes will continue to work for now, but will raise a deprecation warning.

The `PageListingButton`, `SnippetListingButton`, and `UserListingButton` classes will be removed in a future release.

### Using `ListingButton` in `IndexView.get_list_more_buttons` now renders the button as a top-level button

If you have overridden the undocumented `get_list_more_buttons` method on a generic `IndexView` subclass (e.g. for a `ModelViewSet`) and used the `ListingButton` class to add buttons to the "More" actions dropdown, these buttons will now be rendered as top-level buttons in the listing. If you want the buttons to appear inside the "More" actions dropdown, you should use the `Button` class instead.

### `init_new_page` moved to `wagtail.signals`

The [](init_new_page_signal) signal, previously defined in `wagtail.admin.signals`, has now been moved to `wagtail.signals`. Any import lines referencing the old location need to be updated.

### `AccessibilityItem` now accepts an `in_editor` argument

The `AccessibilityItem` user bar item now accepts an {attr}`~wagtail.admin.userbar.AccessibilityItem.in_editor` argument, which is set to `True` when it is instantiated within the page editor. If you [customized the accessibility checker](built_in_accessibility_checker), you should update your code to pass this argument when creating an instance of your `AccessibilityItem` subclass.

```diff
 @hooks.register('construct_wagtail_userbar')
 def replace_userbar_accessibility_item(request, items, page):
     items[:] = [
-        CustomAccessibilityItem()
+        CustomAccessibilityItem(in_editor=item.in_editor)
         if isinstance(item, AccessibilityItem) else item
         for item in items
     ]
```

If you do not update your code, the `AccessibilityItem` will continue to work, but the `in_editor` argument will always be `False`. Wagtail does not currently use this argument, but it may do so in the future. It may also be utilized by users for [cross-domain headless setups](headless_accessibility_checker).

### `wagtail.telepath` and `wagtail.widget_adapters` moved to `wagtail.admin.telepath`

The `wagtail.telepath` module has now been moved to `wagtail.admin.telepath`, and `wagtail.widget_adapters` has been moved to `wagtail.admin.telepath.widgets`. Any import lines referencing the old locations need to be updated.

## Upgrade considerations - changes to undocumented internals

### List view for image listings

The `wagtailimages` listing view templates have been adapted to support both list and grid layout. Those adaptations should be replicated for any overrides or reuse of those templates.

### Removal of `wagtailadmin/js/telepath/telepath.js`

The JavaScript file `wagtailadmin/js/telepath/telepath.js` is no longer required, as Telepath initialization is now handled within `wagtailadmin/js/core.js`. Any templates used within the Wagtail admin should remove imports of this file; any front-end code using Telepath outside of the Wagtail admin should use the `telepath.js` supplied with [the `telepath` package](https://wagtail.github.io/telepath/) or [`telepath-unpack`](https://www.npmjs.com/package/telepath-unpack).

### Rewrite of Tabs component frontend

The frontend implementation of the Tabs component (used in `TabbedInterface` and elsewhere) has been rewritten using Stimulus.

Any existing usage of the component via the `data-tabs` attribute will no longer work. Here is a summary of the new approach.

These are subject to change and are still not officially documented, please add a thumbs up or comment to [the feature request](https://github.com/wagtail/wagtail/issues/8408) if you would like a formalised Python or JavaScript API for the Tabs component.

| Item      | Usage                                     | Old                                                                 | New                                                                                                                                                                            |
| --------- | ----------------------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Attribute | Root tab container div                    | `data-tabs`                                                         | `data-controller="w-tabs"`                                                                                                                                                     |
| Attribute | Tab trigger (tab)                         | `role="tab"` only                                                   | `role="tab"` & `data-w-tabs-target="trigger" data-action="w-tabs#select:prevent"`                                                                                              |
| Attribute | Non-tab ad-hoc triggers (as a link)       | `href="#tab-label-id" data-trigger`                                 | `href="#the-tab-id" data-w-tabs-target="trigger" data-action="w-tabs#select:prevent" data-w-tabs-focus-param="true"` (focus is optional, but recommended)                      |
| Attribute | Non-tab ad-hoc triggers (as a button)     | N/A - was not supported                                             | `type="button" data-w-tabs-target="trigger" data-action="w-tabs#select" data-w-tabs-id-param="the-tab-id" data-w-tabs-focus-param="true"` (focus is optional, but recommended) |
| Attribute | Opt in/out of URL sync, on tabs container | `data-tabs-disable-url` (opt out)                                   | `data-action="popstate@window->w-tabs#select" data-w-tabs-use-location-value="true"` (opt in)                                                                                  |
| Attribute | Add a class when a panel becomes active   | `data-tabs-animate` (hard-coded 'animate-in' class)                 | `data-w-tabs-active-class="animate-in"`                                                                                                                                        |
| Attribute | Tab panel (content)                       | `role="tabpanel"` only                                              | `role="tabpanel"` & `data-w-tabs-target="panel"`                                                                                                                               |
| Attribute | Tablist container keyboard control        | `role="tablist"`                                                    | `role="tablist"` & `data-action="keydown.right->w-tabs#selectNext keydown.left->w-tabs#selectPrevious keydown.home->w-tabs#selectFirst keydown.end->w-tabs#selectLast"`        |
| Event     | A tab is selected                         | `'switch'` (dispatched on the `role=tablist`, does not bubble)      | `'w-tabs:selected'` (dispatched on the triggers for the tab, does bubble)                                                                                                      |
| Event     | A change to the active panel              | `'wagtail:tab-changed'` (dispatched on `document`, does not bubble) | `'w-tabs:changed'` (dispatched on the tabs container, does bubble)                                                                                                             |

For a full HTML example, install the [](styleguide) and see the tabs examples there.
