Backwards Compatibility Breaks in 5.0.1

5.0.1 was just released to fix several security bugs. The Security team tried very hard to mitigate all of the vulnerabilities without any back-compat breaks, but unfortunately there were a few cases where that was not possible.

Security patches are backported to the 3.7 branch, so these BC breaks also apply to versions 4.9.9, 4.8.8, etc.

Form element no longer passes KSES

Prior to 5.0.1, the $allowedposttags array contained an entry for the <form> element and some of its attributes. Because of that, Contributors and Authors could use it in posts.

The element was removed in 5.0.1, except for situations where a plugin has explicitly registered input or select fields via the wp_kses_allowed_html filter. If a Contributor or Author includes <form> in their post, it will be removed when the post is saved. It will also be stripped from arbitrary content that plugins pass to wp_kses_post() or wp_kses_allowed_html( 'post' ).

If a plugin author wants to restore the original behavior, they will need to add form, input or select tags via wp_kses_allowed_html. Please exercise caution when doing so, because this could re-introduce a vulnerability. Make sure only trusted users are allowed to add <form> tags. 

meta_input, file, and GUID inputs are ignored

Prior to 5.0.1, $_POST requests for creating posts could contain values for meta_input, file, and guid. This is no longer true, and values passed for those fields will be ignored.

Plugins should not manually manipulate $_POST, but rather use the appropriate filters, and always validate/sanitize any data coming from an untrusted source.

MIME validation for uploaded files

Prior to 5.0.1, WordPress did not require uploaded files to pass MIME type verification, so files could be uploaded even if the contents didn’t match the file extension. For example, a binary file could be uploaded with a .jpg extension.

This is no longer the case, and the content of uploaded files must now match their extension. Most valid files should be unaffected, but there may be cases when a file needs to be renamed to its correct extension (e.g., an OpenOffice doc going from .pptx to .ppxs).

#5-0, #5-0-1, #dev-notes

JavaScript Packages and Interoperability in 5.0 and Beyond

WordPress 5.0 adds a number of new bundled script handles, including many specific to the new editor, as well as a few vendor dependencies. Given that a plugin or theme may already have registered their own copies of these same dependencies, this post is meant to serve as a reference for maximum interoperability. These are tips which could apply for any new dependency added by WordPress in any version, but are more likely than usual to be encountered given the amount of new JavaScript included starting in 5.0.

WordPress-Specific Scripts

All new WordPress-specific JavaScript code is registered using wp- as a prefix for the script handle. These include the following script handles:

  • wp-a11y
  • wp-annotations
  • wp-api-fetch
  • wp-autop
  • wp-blob
  • wp-block-library
  • wp-block-serialization-default-parser
  • wp-blocks
  • wp-components
  • wp-compose
  • wp-core-data
  • wp-data
  • wp-date
  • wp-deprecated
  • wp-dom-ready
  • wp-dom
  • wp-edit-post
  • wp-editor
  • wp-element
  • wp-escape-html
  • wp-format-library
  • wp-hooks
  • wp-html-entities
  • wp-i18n
  • wp-is-shallow-equal
  • wp-keycodes
  • wp-list-reusable-blocks
  • wp-notices
  • wp-nux
  • wp-plugins
  • wp-polyfill-element-closest
  • wp-polyfill-fetch
  • wp-polyfill-formdata
  • wp-polyfill-node-contains
  • wp-polyfill
  • wp-redux-routine
  • wp-rich-text
  • wp-shortcode
  • wp-token-list
  • wp-url
  • wp-viewport
  • wp-wordcount

Documentation for these packages can be found in the Gutenberg Handbook. They are also available for consumption via npm under the @wordpress scope (excluding polyfills).

Each WordPress package above assigns itself to the global scope under the wp namespace. For example, if your theme or plugin enqueues wp-editor, it will be made available at window.wp.editor in your browser. Conversely, you should also make sure that, for any JavaScript in your plugin which references a wp-namespaced library, the associated script handle is defined as a dependency of your script using the third argument of wp_enqueue_script and wp_register_script.

Vendor Dependencies

The following vendor dependencies have been added in WordPress 5.0:

For vendor scripts in particular, there is a chance for conflict if a plugin or theme registers their own copy of the dependency with the same un-prefixed name. There is a greater likelihood if there is a large version mismatch between the registered scripts.

In general, a plugin should seek to avoid naming collisions by following the best practice of “Prefix Everything” as prescribed in the Plugin Handbook. While this can sometimes result in multiple copies of a script being present in a page, it reduces the chance for breakage and grants WordPress the option to use the unprefixed name in the future.

For dependencies which assign themselves to the global window scope, wp_add_inline_script can be used to ensure it is assigned to a global name unique to your plugin or theme. For example, the following snippet demonstrates including React on a page for a plugin without interfering with a core-enqueued copy of React, whether or not it exists:

wp_enqueue_script( 'myplugin-react', '' );
wp_add_inline_script( 'myplugin-react', 'window._originalReact = window.React;', 'before' );
wp_add_inline_script( 'myplugin-react', 'window.mypluginReact = window.React; window.React = window._originalReact;' );

With the above snippet, your intended copy of React will be guaranteed to be available at window.mypluginReact . This pattern can be applied to any vendor dependency to ensure compatibility with libraries added to WordPress in the future.

If your plugin or theme registers a script with using one of the above script handles as its registered name, consider taking one of the following steps:

Depending on whether you plan to continue support for versions of WordPress earlier than 5.0…

  • If you are only intending to target 5.0 and newer, verify compatibility between the versions of the library and simply remove your plugin or theme’s registration of the conflicting script handle.
  • If earlier version support is needed, and assuming your script registration occurs after the default scripts are registered (after wp_default_scripts), check for whether the script handle is already registered , then register or enqueue your copy of the script, optionally deregistering the core-registered script. Note that since core code targets a specific version of the vendor dependency, deregistering the script may have adverse effects on screens where it is used (notably the new editor screen).
    • Alternatively, follow the pattern as described above to assign your plugin’s copy of the library to a unique name.

Lodash Global

By default, Lodash will assign itself on the global scope using the _ name. Since WordPress will continue to bundle Underscore for the foreseeable future (see #43733), the core-registered Lodash adopts an approach similar to the enqueuing of jQuery to avoid naming conflicts. It does so using Lodash’s _.noConflict, assigning Lodash to the global scope as window.lodash

For this reason, if your plugin or theme uses Lodash as a dependency, you should make sure to not cause _.noConflict to be called multiple times in conflict with the core-registered script.

  • When using the copy of Lodash registered by core, your code should reference it by the window.lodash global.
  • When enqueuing your own copy of Lodash, you should only call Lodash’s _.noConflict under the condition that window.lodash is not already assigned and that the core-registered Lodash will not be included on the page.

In general, the _ global should be used only to reference Underscore.

React and wp-element

When extending the new editor interface, you should use the new wp-element script handle, rather than React directly. The element package is a thin abstraction over the React interface, and implements most all of the same functionality. Targeting wp-element will ensure the greatest compatibility for your plugin as new versions of WordPress are released.

Refer to the wp-element package documentation and React website for more information. 

#5-0, #dev-notes, #javascript

The REST API in WordPress 5.0

WordPress 5.0 contains several additions to the REST API that help provide the new block based editor with all the data it needs. Let’s talk about them!

New Core Endpoints

Several new endpoints have been added to help supply the new block based editor with the data it needs. They all have been built on the existing REST API infrastructure, so the same action and filter hooks throughout can be used to modify requests and responses.


An Autosaves endpoint has been added and registered for all post types except attachment (but including custom post types). The endpoint utilizes the WP_REST_Autosaves_Controller class which extends WP_REST_Revisions_Controller. Registering the endpoint for all post types, and not just those with revisions, was an intentional design decision because all post types need the ability to autosave whether or not revisions are enabled.

Only id, title, post_content and excerpt are saved on this endpoint, but a full, updated post object is returned in the response. It also returns the preview_link, which drives the Post Preview button in the new editor. 

Note: This endpoint requires requests to be authenticated.

Ticket #43316


In order to facilitate searching across multiple post types, a new WP_REST_Search_Controller class was added which registers a /wp/v2/search endpoint. This endpoint is used by the link inserter in the new editor. 

Search types are handled by extending WP_REST_Search_Handler. The default search type is WP_REST_Post_Search_Handler, but this can be filtered by plugins or a theme by using the wp_rest_search_handlers filter.

Ticket #39965

Block Type

A new hidden post type of wp_block has been added for storing reusable blocks. Information about the post type is available at wp/v2/types/wp_block, including the labels and the capabilities required in order to read and create reusable blocks.

Ticket #45098


The individual reusable blocks can be retrieved from wp/v2/blocks. This uses the standard post controller to return post objects containing the rendered content and associated data of each reusable block. 

Note: This endpoint requires requests to be authenticated.

Ticket #45098

Block Renderer

All dynamic blocks with server-side rendering, such as the latest comments or archives, can be accessed at wp/v2/block-renderer/<name>. Names have to be structured as namespace/block-name, where namespace is the name of your plugin or theme. An example core endpoint would be wp/v2/block-renderer/core/archives.

All registered attributes for the block being rendered are accepted as GET parameters. These are validated against the schema matching what was declared in register_block_type().

Note: This endpoint requires requests to be authenticated.

Ticket #45098


A minimal wp/v2/themes themes endpoint has been introduced to inform the new block-based editor of the features that the active theme supports. This allows the correct panels, features, and UI elements to be shown or hidden for the user.

This will currently only return data about the active theme. This endpoint must also be accompanied by the status parameter with a value of active. For example: wp/v2/themes?status=active

The current data included in the response is:

  • A list of post formats supported.
  • Post thumbnail support details.
  • Whether responsive embeds are supported.

In the future the themes endpoint can be expanded, but this was the minimum required information for the new block editor to have all the data it needs to work correctly.

Note: This endpoint requires requests to be authenticated.

Ticket #45016

Additional Changes of Note

In addition to the new endpoints, there are a number of changes to REST API responses developers should be aware of.

  • All custom taxonomies must have show_in_rest set to true in order to appear in the new editor.
  • A new function wp_is_json_request() has been added to determine if the request is expecting a JSON response, and if so, silence PHP warnings and errors. [43730]
  • In order for clients to present permalink previews, the REST API must share the computed results of get_sample_permalink(). These values are now exposed as permalink_template and generated_slug for public, viewable post types, but only for context=edit. [43720]
  • Any fields registered with register_rest_field() now respect the ?_fields= filter parameter in the request URL. [43736]
  • Users with read_private_posts capability can now query for private posts on the posts endpoint. [43694]
  • The unfiltered_html capability is now declared using JSON Hyper Schema targetSchema. [43682]
  • block_version value is added to the post object to denote whether post contains blocks and of what version. At this point there is only one version, but if the block format has backwards compatibility breaking changes in the future, we can increment this value as needed. [43770]
  • New rest_after_* action hooks have been added that fire after all write operations have completed. [42864]

View all REST API tickets in the 5.0 release milestone »

#5-0, #dev-notes

Media 5.0 Guide

The block editor in WordPress 5.0 brings a much needed refresh to how we interact with media in the editor. 5.0 has provided new features designed to make complex content easier to create, and is built in a way that is now truly “what you see is what you get”! There are some big changes to where things are, new methods of accessing `classic` functionality, as well as new features you may not even know about. Let’s highlight a few of those!

Where is the add media button?

If you are looking for the add media button for any reason, this can be found in the classic block TinyMCE toolbar labeled “add media”. This button will trigger the media modal.

Gallery Block:

We have improved the Gallery experience with a new block that gives users a truly real time interactive Gallery.

If you need to utilize dynamic galleries that are based on post ID, or if you need to use the `post_gallery` filter, this can still be achieved using the `add media` button located in the classic block TinyMCE toolbar.

Wide Alignment Theme Support:

Some blocks, such as the image block, have the ability to define a “wide” or “full” alignment in the new editor. This is accomplished by adding the `.alignwide` or `.alignfull` classes, respectively, to the image’s wrapper element. A theme can opt-in for this feature by calling: 

add_theme_support( 'align-wide' );

For more information on Wide Alignment, or if you would like to explore the potential markup of images in this context, the documentation has more details.

#5-0 #dev-notes


Block Editor Support in Existing Default Themes

The default WordPress themes are a great way for even the most novice of site owners to quickly get up and running with a WordPress site that speaks their unique message. WordPress 5.0 will ship with the brand new Twenty Nineteen theme that fully showcases everything the new block-based editor has to offer. But, what if you aren’t ready to abandon your site’s current default theme?

New versions of all default WordPress themes will ship at the same time as WordPress 5.0 with full block editor support. For each theme:

  • Front-end and editor styles have been added for the new block-based editor.
  • Editor color palettes have been added based on the theme’s existing color settings. To best mesh with its accent color picker, Twenty Seventeen uses the block based editor’s default color palette.
  • In Twenty Thirteen, support has been added for wide alignments — they will be applied when no sidebar is present.

If default themes are not updated, they will still work with WordPress 5.0, but the editor may not accurately reflect how the post or page will look to users. As always, you are strongly encouraged to always run the latest version of all themes. Below are the versions of each default theme that add full block editor support:

  • Twenty Seventeen: 1.8
  • Twenty Sixteen: 1.6
  • Twenty Fifteen: 2.1
  • Twenty Fourteen: 2.3
  • Twenty Thirteen: 2.5
  • Twenty Twelve: 2.6
  • Twenty Eleven: 2.9
  • Twenty Ten: 2.6

#5-0, #dev-notes

New Post Type Labels in 5.0

It’s been a while since new post type labels have been introduced in WordPress.

In WordPress 5.0, five additional labels have been made available for custom post types. These get passed in via the labels argument when using register_post_type(). The following labels are new:

  • item_published — The label used in the editor notice after publishing a post. Default “Post published.” / “Page published.”
  • item_published_privately — The label used in the editor notice after publishing a private post. Default “Post published privately.” / “Page published privately.”
  • item_reverted_to_draft — The label used in the editor notice after reverting a post to draft. Default “Post reverted to draft.” / “Page reverted to draft.”
  • item_scheduled — The label used in the editor notice after scheduling a post to be published at a later date. Default “Post scheduled.” / “Page scheduled.”
  • item_updated — The label used in the editor notice after updating a post. Default “Post updated.” / “Page updated.”

Please note the trailing period for all of these strings.

#5-0, #dev-notes, #i18n

5.0: JavaScript language packs are here 🎉

News from the JS package inclusion focus: JavaScript language packs are here! Thanks to AWESOME work by @herregroen, @ocean90, @swissspidy, @nerrad, @atimmer and @schlessera we can now translate strings in JavaScript files and distribute them via This functionality will soon be expanded to also work for plugins and themes. This is a major milestone for JavaScript development in WordPress and completes the JavaScript package inclusion focus.

How does it work?

In short, to make it work, you need to

  • use the @wordpress/i18n package for making strings in your JavaScript translatable.
  • let WordPress know that a script has translations by calling wp_set_script_translations( 'my-handle', 'my-domain' ) after you register a script.

Read more about it in the JS i18n devnote.

Can I have a look at the tickets involved?

Sure thing! Here’s an overview of all the work we’ve done in the last few weeks to get to this point:

  1. WP-CLI: Make sure wp i18n CLI command includes JavaScript translations when generating Potfiles. Relevant PRs:
  2. Meta: generate translations on for core, themes and plugins. Ticket:
  3. Meta: generate and serve JS language packs from for core, themes and plugins. Ticket:
  4. Core: Logic to load JS translations. Ticket:
  5. Core: API to register JS translations for a script.

What’s next?

With JS language packs we have concluded the package inclusion focus. In the remaining time we will keep focus on two things:

  • Keep the packages up-to-date until 5.0 ships.
  • @herregroen will shift focus towards generating the core JS code documentation on Devhub ( and making it searchable. Since this pretty much only involves meta, this is not a release blocker. But with the heaps of JavaScript that 5.0 adds to WordPress, it would be nice to have its documentation searchable and discoverable through Devhub. At the same time it would also make WordPress’ legacy JavaScript (of which a lot was documented in the last years in the JS documentation initiative) more accessible to the broader community. Work on this had already started as part of the JS docs initiative.

#5-0, #dev-notes, #i18n

New! JavaScript i18n support in WordPress 5.0

For years, internationalization (i18n) has been a thing that has been pretty well supported in WordPress when it comes to PHP development. For PHP, WordPress already provides all the tools necessary to make it as easy as possible to localize WordPress core, themes and plugins to any language. Today we are bringing the same capabilities to JavaScript development for WordPress.

How does it work?

When registering your scripts you can add wp-i18n as a dependency to allow you to add translatable strings as you would in PHP:

wp_register_script( 'my-handle', plugins_url( '/js/my-file.js', MY_PLUGIN ), array( 'wp-i18n' ) );

Inside your scripts you will then be able to use wp-18n as follows:

const { __, _x, _n, _nx } = wp.i18n;

__( '__', 'my-domain' );
_x( '_x', '_x_context', 'my-domain' );
_n( '_n_single', '_n_plural', number, 'my-domain' );
_nx( '_nx_single', '_nx_plural', number, '_nx_context', 'my-domain' );

These functions mirror their PHP counterparts and can be used in exactly the same manner. 

The final step is to tell WordPress your script contains translations and of which domain, this is to allow WordPress to selectively load only the necessary translations to ensure everything is as fast as can be:

wp_set_script_translations( 'my-handle', 'my-domain' );

Make sure to also specify the Text Domain for your translations in the header of your plugin file. Otherwise the translations will not be picked up by

Advanced usage

Right now it’s already possible to ship your own translations using the load_textdomain function and passing your own MO file. This is also possible using wp_set_script_translations which accepts an optional third path argument that allows you to tell WordPress to first look elsewhere for translations:

wp_set_script_translations( 'my-handle', 'my-domain', plugin_dir_path( MY_PLUGIN ) . 'languages' );

If passed WordPress will first check if a file in the format of ${domain}-${locale}-${handle}.json exists in the given path and use it as the source of translations if so. Alternatively it will also first check the given path for the md5 filename before defaulting to the WordPress languages directory.

If you want to ship your own translation files these should be in the JED 1.x ( .json ) format. GlotPress is able to create these along with other tools such as po2json. Ideally these files should only contain translations that occur within their respective JS files. With po2json you can generate these files as follows:

po2json translation.po translation.json -f jed

This will generate a JSON in the following format:

  "translation-revision-date": "+0000",
  "generator":                 "GlotPress/2.3.0-alpha",
  "domain":                    "messages",
  "locale_data":               {
    "messages": {
      "":                                                             {
        "domain":       "messages",
        "plural-forms": "n != 1",
        "lang":         "en-gb"
      "This file is too big. Files must be less than %d KB in size.": [
        "This file is too big. Files must be less than %d KB in size."
      "%d Theme Update":                                              [
        "%d Theme Update",
        "%d Theme Updates"
      "password strength\u0004Medium":                                [
      "taxonomy singular name\u0004Category":                         [
      "post type general name\u0004Pages":                            [

Behind the screens

When you upload your plugin or theme to all JS files will automatically be parsed the same as is already being done for PHP files. Any detected translations will be added to to allow the community to cooperate to ensure WordPress, plugins and themes are available in as many languages as possible.

In order to parse all JS files the i18n-command for wp-cli is used. This replaces makepot.php to not only allow picking up translations in JS files but also to audit strings, parse strings only of a specific text domain and even pick up a few strings that weren’t detected by makepot.php. This command is freely available and open-source as makepot.php was and it’s recommended that anyone using makepot.php transition over to this much improved replacement.

Based on these parsed translations Language Packs are generated. Traditionally these used to only contain PO and MO files, one pair for each locale. In order to selectively load only the necessary translations regardless of whether it’s used or not a few more files are being added, one JSON file for every JS file that contains translations per locale.

When parsing JS files for translations we don’t know which handle is used to register that file so we’ve had to use an alternate mechanism to find the translations belonging to each file. To do this we’re using the md5 of the relative path of each file. This is appended to the usual name of ${domain}-${locale} in the form of ${domain}-${locale}-${md5}.json.

When you set script translations for a handle WordPress will automatically figure out the relative md5 hash of your source file, check to see if a translations file exists and if so ensure that it’s loaded into wp.i18n before your script runs.

Plugin and theme support

Translation and Language packs support for plugins and themes that are hosted on the repo is expected in the upcoming weeks. The patches are ready and waiting for commit. Plugin and theme authors are encouraged to start using wp-i18n in their JavaScript projects.


This API wouldn’t have been possible without the long standing efforts of @ocean90, @swissspidy, @nerrad, @atimmer, @schlessera and more recently @herregroen. Thanks a ton for the incredible work you’ve all put into making this a reality! Another thanks to @herregroen for providing the necessary input for this devnote.

#5-0, #dev-notes, #i18n

Meta Box Compatibility Flags

The new block editor has the ability to load meta boxes, to help plugins transition to the new Gutenberg platform. This compatibility functionality works fairly seamlessly with basic meta boxes, but more complex uses may require modification, particularly those that make heavy use of DOM-dependent JavaScript.

To help ensure end users have the best possible experience, there are two flags you can set when adding a meta box, which WordPress will use when determining when to load the block editor, or to fall back to the classic editor.



This flag lets you set whether the meta box works in the block editor or not. Setting it to true signifies that the you’ve confirmed that the meta box works in the block editor, setting it to false signifies that it doesn’t. If the flag isn’t set, then WordPress 5.0 will assume that the meta box does work in the block editor.


This flag lets you set whether the meta box is intended to be used in the block editor, or if it has been replaced by block-based functionality. Setting it to true signifies that this meta box should only be loaded in the classic editor interface, and the block editor should not display it. Setting it to false signifies that this meta box is intended to be loaded in the block editor. If the flag isn’t set, then WordPress 5.0 will assume that the meta box is intended to be loaded in the block editor.


These flags are used by passing them in the $args parameter of add_meta_box(), like so:

	__( 'My Meta Box', 'textdomain' ),
		'__block_editor_compatible_meta_box' => true,
		'__back_compat_meta_box'             => false,

Fallback Warnings

WordPress 5.0 also contains a developer preview of a fallback warning. If you set WP_DEBUG to true, then create a meta box with __block_editor_compatible_meta_box set to false, and __back_compat_meta_box either undefined, or set to false as well, this warning will show in the classic editor.

Screen shot of a demo meta box showing the fallback warning.

This warning is currently intended to be a developer preview, hence why it’s hidden behind the WP_DEBUG flag, and requires a particular combination of the compatibility flags to be set. The wording and appearance of the warning will likely change in future WordPress versions, but it’s ultimately intended to help end users identify which plugin is responsible for a meta box.

If you register your meta box with the __back_compat_meta_box set to true, then this warning will never be displayed with your meta box, now, or in future WordPress releases.

Future Changes

Future versions of WordPress may change how they behave when these flags are set. For example, WordPress 5.0 will silently fall back to the classic interface when an incompatible meta box is encountered. Classic editor code will likely be moved to the Classic Editor plugin in the future, however, so those future WordPress versions would require the Classic Editor plugin to be installed before it could automatically fall back. Any such changes would be posted well in advance, to ensure there’s plenty of warning for testing changes.

#5-0, #dev-notes

Block Editor Filters

In WordPress 5.0, the block editor is the default editor for all post types that both:

  • When registered (or using add_post_type_support()) declare that they support the editorfeature.
  • When registered, set their show_in_rest setting to true (the default is false).

In the Gutenberg plugin, there are two filters available to determine if a particular post, or a particular post type should use the block editor. gutenberg_can_edit_post, and gutenberg_can_edit_post_type, respectively.

In WordPress 5.0, the equivalent filters are use_block_editor_for_post, and use_block_editor_for_post_type. You can use them like so:

function my_post_filter( $use_block_editor, $post ) {
	$author = get_userdata( $post->post_author );
	if ( 'pento' === $author->user_login ) {
		return (bool) random_int( 0, 1 );

	return $use_block_editor;
add_filter( 'use_block_editor_for_post', 'my_post_filter', 10, 2 );

function my_post_type_filter( $use_block_editor, $post_type ) {
	if ( 'my_mystical_post_type' === $post_type ) {
		return false;

	return $use_block_editor;
add_filter( 'use_block_editor_for_post_type', 'my_post_type_filter', 10, 2 );
#5-0 #dev-notes