Make WordPress Core

Welcome to the official blog of the core development team for the WordPress open source project.

Here, we make WordPress core. Follow our progress with general updates, status reports, and the occasional code debate.

We’d love for you to help out.

Looking to file a bug?

It’s easy to create a ticket on our bug tracker.

Want to contribute?

Get started quickly. We have some tickets marked as good first bugs for new contributors. There’s more on our reports page, like patches needing testing.

We also have a detailed handbook for contributors, complete with tutorials.

Weekly meetings

We use Slack for real-time communication. As contributors live all over the world, there are discussions happening at all hours of the day.

We have a project meeting every Wednesday at 20:00 UTC in the #core channel on Slack. (Find out more about Slack.)

You can find meeting agendas on this blog. You’re welcome to join us or listen in.

Recent Updates Toggle Comment Threads | Keyboard Shortcuts

  • Rian Rietveld 2:04 am on July 31, 2015 Permalink |
    Tags: , ,   

    Headings in Admin screens change in WordPress 4.3 

    Are you a theme, plugin or framework developer for WordPress? Take note: the heading structure in the Admin screens will change in WordPress 4.3.

    From H2 to H1

    Currently, in WordPress 4.2 and before, the main heading in admin screens is an <h2>. However, if you want to have a correct, semantic heading structure, a page should an <h1>, but only one, which describes what the page is about.

    People using assistive technology use the <h1> to identify a page and quickly know where they are. Further, proper HTML5 dictates that an <h1> should be the initial heading.

    Therefore, in WordPress 4.3 the headings of all admin screens have been changed from <h2> to <h1> (see #31650).

    Related CSS changes

    If your theme or plugin still uses <h2> for admin screens, don’t worry; styles for <h2> are still supported in the admin CSS so that, visually, nothing changes during the update to 4.3. However, a new class was introduced to properly style <h1>s, page-title-action. Here’s an example of the new class in use:

    <h1>Posts <a href="[..]/wp-admin/post-new.php" class="page-title-action">Add New</a></h1>

    The old class .wrap .add-new-h2 is still supported but has been labeled deprecated. It has been replaced by: .wrap .page-title-action.

    More changes ahead

    After the release of WordPress 4.3, the accessibility team will continue making changes to heading so that they are semantically correct. Current <h3>s will become <h2>s, <h4>s will become <h3>s, and so on.

    Check your code

    Does your plugin, theme, or framework have admin screens? Check the heading structure. Change the main heading from an <h2> into an <h1>. If you’re feeling generous, check the rest of your heading structure to ensure it’s semantic.

    Making these semantic changes will ensure your plugin or theme is in sync with WordPress and that people using a screen reader can understand your admin screens better.

    If you have any questions, ask them in the comment section below, or contact the WordPress accessibility team in the #accessibility channel in WordPress Slack.

  • Samuel Sidler 8:50 pm on July 30, 2015 Permalink |
    Tags: , 4.2.4   

    WordPress 4.2.4 Release Candidate 1 

    tl;dr WordPress 4.2.4 RC1 is available (download) for testing and fixes an issue with inline scripts.

    A change in WordPress 4.2.3 had the unintentional side effect of breaking some inline scripts when the CDATA block is used (see #33106). For example, consider the intended content here:

    <script>// <![CDATA[
    // ]]>

    In 4.2.2, this content is left as is and _my_function() fires as expected. In 4.2.3, the content is manipulated as such:

    <script>// <![CDATA[ _my_function('data'); // ]]></script>

    This results in the script being commented out by the // and it will not fire. A workaround for this is to use /* for commenting.

    <script> /* <![CDATA[ */ _my_function('data'); /* ]]> */ </script>

    However, this workaround should not be necessary. As a result, we intend on releasing WordPress 4.2.4 to fix this issue.

    Additionally, WordPress 4.2.3 caused issues when using shortcodes within angle brackets (see #33116). For example, this shortcode usage worked in 4.2.2 but did not work in 4.2.3:


    While we do not recommend this use of shortcodes and strongly encourage plugin developers to move away from this use of shortcodes, the breakage was unintentional and we have restored the behavior in WordPress 4.2.4 RC1.

    Please download and test WordPress 4.2.4 RC1 and report any issues to core trac or to this post.

  • Dominik Schilling (ocean90) 8:38 pm on July 30, 2015 Permalink |
    Tags: , ,   

    get_transient() is now more strict in 4.3 

    WordPress 4.3 includes a change to get_transient(). As reported in #23881 and #30380, get_transient() had a check for get_option( $transient_timeout ) < time().

    Because get_option() can return false and false is always < time() 😖, get_transient() could delete transient timeout options in an unexpected way or cause two more unnecessary queries.

    WordPress 4.3 now checks the return value first before comparing with the current time. This means that WordPress will no longer delete broken transients via get_transient() if you have only deleted the timeout option. See [33110].

    If you have to delete a transient manually, please make sure that you’re deleting _transient_timeout_$transient and '_transient_' . $transient. (Hint: Transients can be stored in an external object cache too.)

    See also: the Transients API Codex page.

  • Dominik Schilling (ocean90) 5:41 pm on July 30, 2015 Permalink |
    Tags: , ,   

    Legacy Theme Preview Removed in 4.3 

    This release we removed the old code for the Legacy Theme Preview, which hasn’t been maintained since the introduction of the customizer in WordPress 3.5. Recently, we noticed that the theme preview no longer works when using a static front page. We kept the old theme preview for no-JS and some browsers that were less capable. But since browsers are doing a better job today we don’t need to continue fixing/shipping this legacy code. That’s why we removed it. See [33492].

  • Weston Ruter 10:45 pm on July 29, 2015 Permalink |
    Tags: , , , ,   

    Fast previewing changes to Menus in the Customizer 

    The foundation of the Customizer is built on the concept of the “setting”, an abstract representation of anything in WordPress: options, theme mods, posts, terms, and so on. Settings are linked to controls which are responsible for manipulating them. When a setting is changed it gets pushed into the preview window so that the modification can be reviewed before being published live.

    There are two ways (transports) for how a setting can be pushed into the preview: refresh and postMessage. The refresh transport does a full page refresh with the modified settings applied, and so it relies on PHP to apply the setting changes. The refresh transport is designed to be compatible with all themes, but for heavy sites refresh can be excruciating slow and taxing on the server. For this reason, the postMessage transport was introduced for previewing changes. This transport uses JavaScript to apply the changes instantly in the preview without any Ajax requests or PHP calls. This is great, but themes have to opt-in to postMessage transport for settings because they must also include JavaScript code which duplicates the logic which is already being executed on the server by PHP. The postMessage transport violates the DRY principle.

    With that background out of the way…

    Partial Refresh of Menus

    In WordPress 4.3 menu management is being added to the Customizer, extending the types of objects that the Customizer can manipulate to include nav_menu taxonomy terms and nav_menu_item posts. We wanted this menu management experience to be fast so it had to avoid the slow full-page refresh transport. Nevertheless, the postMessage transport wasn’t possible to use either since we wouldn’t be able to duplicate all of the PHP menu walker logic and theme/plugin extensions for rendering menus purely in JavaScript. We also wanted to enable fast previewing of menu changes by default. So we implemented a postMessage/refresh hybrid approach which uses postMessage to sync the menus settings to the preview, and then the preview does an Ajax request to render just the contents of the menu container, and this Ajax response is then inserted into the DOM to replace the previous menu container. The technical name for this approach we have been calling “partial refresh”, but you can call it “fast preview”.

    When the Customizer determines it can’t do a partial refresh, it falls back to performing a full page refresh. Conditions for why a menu change will get a full page refresh include:

    • if the menu was just assigned to a location for the first time or removed from being assigned to a location, since the theme may have some changes to the layout that it may need to perform;
    • or if wp_nav_menu() is called with with echo being false, since the theme may be doing some string replacement in this case which we can’t replicate in the partial-refresh Ajax request;
    • or if wp_nav_menu() is called with with a non-JSON serializable fallback_cb, such as a closure or a object method—this arg has to be empty or a string;
    • or if wp_nav_menu() is called with with a walker object instance, as opposed to a class name string;
    • or if wp_nav_menu() is called without a theme_location arg and the menu arg is not an term ID or a menu object;
    • or if the menu selected in a Custom Menu widget is changed, since partial refresh for widgets is not yet supported (however, see feature plugin)—subsequent changes to the menu will result in fast partial refreshes if the above conditions

    When a menu is being updated via partial refresh, the menu container will receive a classname customize-partial-refreshing, which by default changes the opacity to 25%.

    Theme Support

    The fast preview for menu changes is being enabled by default so that theme authors don’t have to opt-in to the functionality like they do for instant preview (postMessage transport). In general this should be fine. However, if the theme includes some JavaScript which dynamically manipulates the rendered menu container, such as adding event handlers for expanding/collapsing submenus, then the theme would need to include some JS to re-apply the modifications once the menu is partial-refreshed, as the old DOM for the menu container gets replaced.

    The Twenty Fifteen theme actually uses JS to expand/collapse submenu items, so it needed a patch in 4.3 to re-setup a partial-refreshed main nav menu. In addition to firing the menu container DOM setup logic once at jQuery.ready(), it now invokes the functionality whenever the menu is partial-refreshed. See the initMainNavigation() JS function.

    So in general, if your theme needs to dynamically initialize menus with JavaScript, the pattern for the initialization code should be:

    jQuery(function($) {
    	function initMainNavigation( container ) {
    		/* set up container... */
    	initMainNavigation( $( '.main-navigation' ) );
    	$( document ).on( 'customize-preview-menu-refreshed', function( e, params ) {
    		if ( 'primary' === params.wpNavMenuArgs.theme_location ) {
    			initMainNavigation( params.newContainer );
    			/* optionally sync a previous menu state from params.oldContainer... */

    The params being passed to the event handler consists of the following properties:

    • newContainer: jQuery object containing the new menu container element retrieved from Ajax; this is what you would manipulate to initialize.
    • oldContainer: the previous jQuery object holding the element for the replaced menu container; this is useful if there is any state in the old menu that should persist in the new menu, such as which submenus are expanded (as in Twenty Fifteen).
    • wpNavMenuArgs: The array of arguments passed to wp_nav_menu() in the template, such as template_location.
    • instanceNumber: The index for which wp_nav_menu() call being updated.

    The Future

    Partial refresh is an “experimental transport” introduced here specifically for menus. In 4.4 we hope to generalize the framework so that any setting can be setup for fast preview, starting with widgets (see #27355). There is a Customize Partial Refresh feature plugin that implements widget partial refresh in a beta state. This partial refresh functionality I’ve also identified as being critical for Customizer Transactions.

    • nicholas_io 2:53 am on July 30, 2015 Permalink | Log in to Reply

      This is awesome. But what happens if the menu doesn’t have a container (container arg set to false)? It will fallback to a full-page refresh?

      • Weston Ruter 6:16 pm on July 30, 2015 Permalink | Log in to Reply

        @nicholas_io: Good question! I wasn’t sure actually, but I tried it out and it works fine. The critical logic here is found in WP_Customize_Nav_Menus::filter_wp_nav_menu().

        You can see that it attaches the required CSS class names to the class attribute it finds. When the container arg is false then the root ul element is used, and it will have a class name if the normal walker is used, at least. I tried partial refresh with set to false, and it continued to work as expected. As long as the items_wrap continues to have the default value of <ul id="%1$s" class="%2$s">%3$s</ul> then it will work.

        We should harden this, however, to not just find the first class attribute, but look at the first (root) element and amend an existing class attribute or insert one if it doesn’t exist already.

      • Weston Ruter 6:40 pm on July 30, 2015 Permalink | Log in to Reply

        Here’s a patch for what I have in mind: https://github.com/xwp/wordpress-develop/pull/110/files

        • nicholas_io 1:28 am on July 31, 2015 Permalink | Log in to Reply

          It’s a good patch for me, the original approach could lead to problems in some edge cases (and from what I understand with this patch we could still have partial refresh even if the user alter items_wrap and remove the class attribute.)

  • Ella Iseulde Van Dorpe 9:26 pm on July 29, 2015 Permalink |
    Tags: , ,   

    Old Distraction Free Writing Code Removed in 4.3 

    This release we removed all old DFW code, which hasn’t been used in core since 4.1. We left it in core for two releases so plugin authors had the time to update. If it is essential to your plugin, the files in 4.2 can still be reused and improved. See [32677].

  • Konstantin Obenland 5:04 pm on July 29, 2015 Permalink |
    Tags: ,   

    Dev Chat Agenda for July 29 

    Here’s the agenda for today’s Dev Chat in the #core channel on Slack.

    We’re getting ready for RC1 so lets discuss blocker, RC guidelines, and everything else related to RC.

    Time/Date: July 29 2015 20:00 UTC:

    1. RC Notes
    2. Feature Updates
    3. Component Updates
    4. Open Floor
  • Mark Jaquith 11:54 pm on July 28, 2015 Permalink |
    Tags: , ,   

    Passwords in 4.3: Strong by Default 

    One of the development efforts in the WordPress 4.3 cycle was improving the way that passwords are chosen and changed. Before, people had to start from scratch when choosing a password. They were presented with an empty box, and had to use a really terrible tool for generating secure passwords: the human brain.

    Here’s how things look now, as we approach 4.3’s release candidate…

    Screen Shot

    And when you click that button…

    Screen Shot

    You start out with a strong password. And if you like, you can just accept that. Most modern browsers will offer to remember it for you (as well as password managers like 1Password and LastPass). Or, you could go old school and write it on a sticky note. Hey: anything is better than choosing “letmein”!

    You can, of course, click into the field and edit it. But now the password strength meter is better integrated with the field. Things start to look dire as you go your own way.

    Screen Shot

    That red seems to signal danger. And hey, look, below. This password is SO BAD that WordPress wants to make extra sure you know you’re doing something monstrously foolhardy.

    If you’re in a public location, you can hide your password, to prevent people from peeking over your shoulder.

    Screen Shot

    This new interface is also integrated into the Add New User screen. By default, we won’t even reveal the password. We’ll just send the user a reset link.

    Screen Shot

    But if you’re in a non-email environment or would like to pass this password to the user in a secure method such as iMessage, Signal, or TextSecure, you can reveal it…

    Screen Shot

    The new interface can also be found on the password reset screen and the WordPress install screen. They all start you out with a long, random, unguessable password. Although WordPress isn’t stopping you from choosing terrible passwords, the default in 4.3 is that you get secure passwords, and making them less secure takes a bit of work.

    In addition to this new UI, we have also stopped e-mailing passwords, so valid passwords aren’t going to sit in your e-mail inbox, waiting for some future e-mail hacker to gain access. Password reset links now expire in 24 hours by default. And when your password or e-mail changes, we send you an e-mail (in the case of e-mail, to your old address), so if someone hijacks your browser session and changes those critical items, at least you’ll be aware that it happened, and you can take action. You can disable these e-mails via the send_pass_change_email and send_email_change_email filters (just have them return false).

    Huge thanks to everyone who contributed code, testing, UI, and thoughtful feedback on this feature!

    • Pippin Williamson 11:59 pm on July 28, 2015 Permalink | Log in to Reply


    • Dylan Ryan 3:19 am on July 29, 2015 Permalink | Log in to Reply


    • Gary Jones 7:34 am on July 29, 2015 Permalink | Log in to Reply

      Excellent stuff. All of this is a real step forward. Thank you to all those who contributed.

      Is the code modular enough that it can be applied to the front-end (e.g. via a function call in a template), as part of a visitor sign-up / checkout with a registration process?

      • spivurno 10:55 am on July 29, 2015 Permalink | Log in to Reply

        Also interested in this answer.

      • Inggo 1:14 pm on July 29, 2015 Permalink | Log in to Reply

        Similarly interested in this as well.

      • John James Jacoby 2:05 pm on July 29, 2015 Permalink | Log in to Reply

        In my opinion, no; the code is not currently written to make it easy to pull into a template part.

        That said, the HTML and accompanying CSS & JS is very simple, and could be encapsulated relatively easily in a future release.

        Look for BuddyPress and bbPress to go this route soon, and contribute upstream where possible.

    • Gustavo Bordoni 7:52 am on July 29, 2015 Permalink | Log in to Reply

      What an Awesome update! Real good to see this to become a reality.

    • TobiasBg 7:57 am on July 29, 2015 Permalink | Log in to Reply

      Can I suggest to rename the filter `send_pass_change_email` to `send_password_change_email`?

    • Mario Peshev 8:40 am on July 29, 2015 Permalink | Log in to Reply

      That’s a great update, can’t wait to see it in the next major core update.

    • Minion 8:51 am on July 29, 2015 Permalink | Log in to Reply

      “stopped e-mailing passwords…”. I like that! So say if new user created, will the option “Send this password to the new user by email.” also contain link that expires in 24hours?

    • Collizo4sky 1:08 pm on July 29, 2015 Permalink | Log in to Reply

      Can’t wait to see this live. Great update.

    • John James Jacoby 2:11 pm on July 29, 2015 Permalink | Log in to Reply

      This is one of those small changes that will make a huge impact on the security of the web. Some users will be confused. Some admins will need to explain to their users why this is now a requirement. Someone will make a plugin to revert this back to the pre-4.3 experience. What matters is everyone will be safer and more secure from the start and going forward.

      Nice work, team!

    • Paradigm5h1f7 6:38 pm on July 29, 2015 Permalink | Log in to Reply

      I’m hoping that people will actually use this. From this – ” WordPress isn’t stopping you from choosing terrible passwords” – I assume that the worst offenders will continue using their one-password-for-everything… why not make it so the user cannot accept anything less than a strong password? Can the user still choose “letmein” ??

      Also wondering if there can be a way to recommend users get a trusted password manager. Unfortunately, although “Most modern browsers will offer to remember it for you…” – they also store the passwords in plain text so any malware can grab them. This is a long standing issue that Chrome refuses to fix…

      Just some thoughts. All in all, it’s a good step in the right direction, but maybe preaching to the choir too much. It does look good though, and reinforces that the onus is on users to be secure in their password management.

  • Konstantin Obenland 10:00 pm on July 27, 2015 Permalink |
    Tags: , , ,   

    Site Icon 

    WordPress 4.3 adds the ability for site owners to manage their site’s favicon on desktop and mobile. Site Icons work out of the box, are theme independent, and don’t require theme support. There are four sizes that WordPress supports by default:

    • 32x32px favicon.
    • 180x180px app icon for iOS up to the iPhone 6+.
    • 192x192px Android/Chrome app icon.
    • 270x270px medium-sized tile for Windows.

    The public API is very simple:

    • wp_site_icon() displays all available favicons and app icons.
    • get_site_icon_url() returns the url to the current site’s icon, or the default passed to it.
    • site_icon_url() displays an escaped version of the url to the current site’s icon.
    • has_site_icon() returns whether the current site has an icon set up or not.

    At this point we discourage theme authors from using site icons as logos in the front-end. It’s unexpected behavior for users since it doesn’t fit the API’s purpose.

    Custom Site Icons

    Plugin authors can add custom sizes to the site icon feature. To do this, filter the array of available sizes on the admin side and the array of meta tags on the front-end, like so:

    function prefix_custom_site_icon_size( $sizes ) {
       $sizes[] = 64;
       return $sizes;
    add_filter( 'site_icon_image_sizes', 'prefix_custom_site_icon_size' );
    function prefix_custom_site_icon_tag( $meta_tags ) {
       $meta_tags[] = sprintf( '<link rel="icon" href="%s" sizes="64x64" />', esc_url( get_site_icon_url( null, 64 ) ) );
       return $meta_tags;
    add_filter( 'site_icon_meta_tags', 'prefix_custom_site_icon_tag' );

    Customizer Controls

    Nick Halsey published an article about the new Customizer controls that were introduced for Site Icon. The new controls make it trivial for any theme or plugin author to add custom features that require cropped images. For example, a logo manager. 😉

    • Rouven Hurling 6:26 am on July 29, 2015 Permalink | Log in to Reply

      Is it possible to disable Custom Site Icons?
      I already implemented something like it which uses the realfavicongenerator.net API, although I’ll probably modify it to use the new Customizer Controls.

  • Nick Halsey 9:29 pm on July 27, 2015 Permalink |
    Tags: , ,   

    Changes to Customizer Panels and Sections in 4.3 

    WordPress 4.3 contains some important changes to the Panels and Sections APIs. These won’t impact basic usage – add_section() and add_panel(), for example – but could potentially have compatibility issues for any custom panels or sections that override default methods. If you have themes or plugins that create custom sections or panels, please be sure to test with the latest 4.3 beta. Additionally, we’ve built out the API to support dynamically-added and more JS-driven panels and sections in 4.3. Details below.

    Changes to the Default Panel & Section UI

    Ticket #31336 introduced a new user experience for core Customizer sections, and made some adjustments to panels in the process. Sections in general now slide in sideways (like panels) rather than expanding open vertically in the “accordion” style. This change adds a heading within section containers that contains context for the user’s location within the Customizer hierarchy of sections/panels as well as the navigation back out of the panel.

    If you have a custom section that overrides WP_Customize_Section::render(), you will probably need to add the new section header markup that has been added to the base section. Without at least the back button, users could potentially become stuck within an open section because of this core change. Additionally, the panel-back button has moved from a single button in the header (over the Customizer close button) to being contained within the panel header, similarly to the section header. If you’re overriding WP_Customize_Panel::render_content() in a custom panel, you’ll probably need to add the panel back button there. For panels and sections, if you’re overriding the onChangeExpanded method in JavaScript, you would need to implement the core changes there as well, although odds are your implementation is custom enough that the changes to the default objects won’t affect you. Because custom section and panel implementations will vary widely, your best bet is to look at the revised code in trunk and adjust your code accordingly (see wp-includes/class-wp-customize-section.php). However, for basic sections using a UI close to the default one, adding the following to your render_template() method within ul.accordion-section-content is likely to work (note this is a JS template; can be converted to a PHP template fairly easily):

    <li class="customize-section-description-container">
    	<div class="customize-section-title">
    		<button class="customize-section-back" tabindex="-1">
    			<span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
    			<span class="customize-action">
    				{{{ data.customizeAction }}}
    			{{ data.title }}
    	<# if ( data.description ) { #>
    		<div class="description customize-section-description">
    			{{{ data.description }}}
    	<# } #>

    Note that custom sections and panels are generally designed to allow different UI implementations than the default behavior, so in many cases the changes to the default objects shouldn’t break your custom versions.

    JavaScript-templated Panels and Sections

    Building on the work done in 4.1 for controls, #30737 introduces JavaScript templates for panels and sections. The built-in core panel and section objects now use JS templates to render all instances by default. The PHP-rendered object API is still present and fully supported, but developers are recommended to strongly consider using JavaScript templates for control, section, and panel UI instead. These templates allow objects to be more easily created dynamically and improve performance by loading an object’s data (in JSON format) and a single template for the HTML structure rather than the complete fully-rendered HTML structure for repetitive (PHP-generated) UI.

    Menus in the Customizer drove the completion of this API addition, and it facilitates the dynamic creation of Customizer sections and controls when creating new menus. Further plans in this area include using JS templates for the base WP_Customize_Control object by default ( #30738) and building out the API for dynamically-created controls by implementing container templates similar to what panels have ( #30741).

    The remainder of this post will outline the various API additions that facilitate this, with code examples. Please review the initial post about JS templates and sections, as the new API for sections and panels is conceptually identical to what was introduced for controls in 4.1, taking a few more steps beyond it.

    Registered Panel and Section Types

    Similarly to the concept of registered control types introduced in 4.1, WP_Customize_Manager now supports registered section and panel types. For those that haven’t previously worked with custom sections or panels, they’re just like custom controls. You can override different pieces of the base object as needed to create custom UI. A great example in core is WP_Customize_New_Menu_Section, which is displayed as a button that toggles display of its controls inline, rather than a sliding panel.

    Registering a section or panel type tells the Customizer to print one copy of the object’s JS template to the page, so that any object with this type can be rendered dynamically. Instances added in PHP will be automatically inserted into the DOM after being rendered from the JS template when ready, and the template can also be used when adding an instance dynamically in JS (for example, when adding a new section when a new menu is created).

    Registering panel and section types works just like it does for controls:

    add_action( 'customize_register', 'prefix_customize_register' );
    function prefix_customize_register( $wp_customize ) {
      // Define a custom section class, WP_Customize_Custom_Section.
      // Register the class so that its JS template is available in the Customizer.
      $wp_customize->register_section_type( 'WP_Customize_Custom_Section' );


    Sending PHP Data to JavaScript

    Customizer panel and section data has been passed to the relevant JS models since 4.1, but you’re much more likely to need to send data down when working with JS templates. Anything that you would want access to in render_content() in PHP will need to be exported to JavaScript to be accessible in your section or panel template. Since JS templates are now used by default for sections and panels, most of the core class variables for these objects are passed already. To add custom ones, override WP_Customize_Panel::json()/WP_Customize_Section::json() in your custom subclass. In most cases, you’ll want to merge with the return value of the parent class’s json() method also, to ensure that all core variables are exported as well. Here’s an example from the core nav menu section class:

     * Get section parameters for JS.
     * @since 4.3.0
     * @access public
     * @return array Exported parameters.
    public function json() {
    	$exported = parent::json();
    	$exported['menu_id'] = intval( preg_replace( '/^nav_menu\[(\d+)\]/', '$1', $this->id ) );
    	return $exported;

    Using JS/Underscore Templates

    Once you’ve registered your custom section or panel class as a section or panel type and exported any custom class variables, you can create the template that will render the section or panel UI. This works slightly differently for sections and panels, because panels have a distinct container and content that can be overridden separately as needed.


    For sections, you’ll override WP_Customize_Section::render_template() instead of WP_Customize_Section::render(). The render function is now empty in the base core control, so there’s no need to override it if you’re using a custom template. If you are using the existing PHP API, the JS template will not be used if content exists after running the render() function. The default section’s template looks like this in 4.3:

     * An Underscore (JS) template for rendering this section.
     * Class variables for this section class are available in the `data` JS object;
     * export custom variables by overriding WP_Customize_Section::json().
     * @since 4.3.0
     * @access protected
     * @see WP_Customize_Section::print_template()
    protected function render_template() {
    <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
    	<h3 class="accordion-section-title" tabindex="0">
    		{{ data.title }}
    		<span class="screen-reader-text"><?php _e( 'Press return or enter to open' ); ?></span>
    	<ul class="accordion-section-content">
    		<li class="customize-section-description-container">
    			<div class="customize-section-title">
    				<button class="customize-section-back" tabindex="-1">
    					<span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
    					<span class="customize-action">
    						{{{ data.customizeAction }}}
    					{{ data.title }}
    			<# if ( data.description ) { #>
    				<div class="description customize-section-description">
    					{{{ data.description }}}
    			<# } #>

    As you can see, Underscore-style templates are very similar to PHP. As more and more of WordPress core becomes JavaScript-driven, these templates are becoming increasingly more common. Besides the Customizer, sample template code in core can be found in media, revisions, the theme browser, and even in the Twenty Fifteen theme, where a JS template is used to both save the color scheme data and instantly preview color scheme changes in the Customizer. The best way to learn how these templates work is to study similar code in core.


    Panels have two distinct pieces to their UI: the container, which is displayed in a list with other panels and sections at the top level of the Customizer UI, and the content, which is the context that the Customizer enters when the panel is opened. Note that this distinction is due to the philosophical design of Panels to be distinct contexts, rather than simply being a way to group related sections together. This is also based on the distinction between the container and the content in control objects. However, the distinction between panels and sections in terms of UI is less visually clear in WordPress 4.3.

    Panel containers are rendered with the render_template() function, and the content template is inserted into the container in JS by looking for .accordion-sub-container. It will rarely be necessary to override the container template in a custom panel, as the custom UI will typically go in the content of the panel. The default panel template looks like this:

     * An Underscore (JS) template for rendering this panel's container.
     * Class variables for this panel class are available in the `data` JS object;
     * export custom variables by overriding WP_Customize_Panel::json().
     * @see WP_Customize_Panel::print_template()
     * @since 4.3.0
     * @access protected
    protected function render_template() {
    	<li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
    		<h3 class="accordion-section-title" tabindex="0">
    			{{ data.title }}
    			<span class="screen-reader-text"><?php _e( 'Press return or enter to open this panel' ); ?></span>
    		<ul class="accordion-sub-container control-panel-content"></ul>

    Panel contents are rendered with the content_template() function, similarly to the way control templates work. In the future, this part of the API will be completed with the addition of container templates for controls, see #30741. The default panel content template in 4.3 looks like this:

     * An Underscore (JS) template for this panel's content (but not its container).
     * Class variables for this panel class are available in the `data` JS object;
     * export custom variables by overriding WP_Customize_Panel::json().
     * @see WP_Customize_Panel::print_template()
     * @since 4.3.0
     * @access protected
    protected function content_template() {
    	<li class="panel-meta customize-info accordion-section <# if ( ! data.description ) { #> cannot-expand<# } #>">
    		<button class="customize-panel-back" tabindex="-1"><span class="screen-reader-text"><?php _e( 'Back' ); ?></span></button>
    		<div class="accordion-section-title">
    			<span class="preview-notice"><?php
    				/* translators: %s is the site/panel title in the Customizer */
    				echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">{{ data.title }}</strong>' );
    			<button class="customize-help-toggle dashicons dashicons-editor-help" tabindex="0" aria-expanded="false"><span class="screen-reader-text"><?php _e( 'Help' ); ?></span></button>
    		<# if ( data.description ) { #>
    			<div class="description customize-panel-description">
    				{{{ data.description }}}
    		<# } #>

    In core, the nav menus panel, which adds screen options for the panel, is a good example of a panel with a custom content template.

compose new post
next post/next comment
previous post/previous comment
show/hide comments
go to top
go to login
show/hide help
shift + esc
Skip to toolbar