Selective Refresh in the Customizer
Note: This proposal was merged to core in [36586]. Download the latest nightly build and give it a try!
The Customizer is our framework for live previewing changes. However, settings edited in the Customizer get sent to the preview via the refresh transport by default, meaning that in order for a setting change to be applied, the entire preview has to reload: this refresh can be very slow and is anything but “live”. To remedy this poor user experience of a laggy preview, the Customizer allows settings to opt-in to using a postMessage transport which relies on JavaScript to preview the change instantly without doing any server-side communication at all. This provides for a great user experience.
There is a major issue with settings using the postMessage transport, however: any logic used in PHP to render the setting in the template has to be duplicated in JavaScript. This means that the postMessage previewing logic violates the DRY (Don’t Repeat Yourself) principle, and so it is really only suitable for simple text or style changes that involve almost no logic (e.g. site title or background color). Even in this case, it is often not possible to fully re-implement the PHP logic in JS, since as in the example of #33738 there may be an unknown number of PHP filters that get applied to the setting’s value (such as the site title) when rendering the content on the server.
What the Customizer needs is a middle-ground between refreshing the entire preview to apply changes with PHP and between applying the changes exclusively with JavaScript. The selective refresh is the solution proposed in #27355, and this post is about documenting the feature, as it was approved for core merge in 4.5. Selective refresh also appears on the proposed Customizer roadmap.
Partial preview refreshing was first explored when Customizer widget management was being developed for the 3.9 release. It was added to the Widget Customizer plugin and described in a Make Core post. But it was decided to remove the feature in favor of having a better generalized framework for partial refreshes in a future release (which is now). In 4.3, nav menus were added to the Customizer and with this the first core implementation of selective refresh, as described in Fast previewing changes to Menus in the Customizer. (For more background, see Implementing Selective Refresh in the Customizer.)
With the shipped example of selective refresh of nav menus in the Customizer, and an initial implementation of selective refresh for widgets, work progressed to generalize a selective framework that would support the complicated examples of nav menus and widgets, but also to support simpler use cases such as letting the server render the updated site title so that wptexturize and other filters can apply. The generalized framework has been implemented in the Customize Partial Refresh feature plugin, which also re-implements selective refresh of nav menus and introduces selective refresh of sidebars and widgets.
To see a simple example of selective refresh, see the Site Title Smilies plugin showing wptexturize and emoji applying to site title updates in the preview:
Note in this example that the standard postMessage updating of the site title in the header is still in place and so the change appears immediately. It gets followed, however, by the content being rendered by the partial on the server. This allows for JS to apply a quick low-fidelity preview for a setting change until the server can respond with the high-fidelity preview.
Selective Refresh Architecture
This plugin introduces a selective refresh framework which centers around the concept of the partial. A partial is conceptually very similar to a Customizer control. Both controls and partials are associated with one or more settings. Whereas a control appears in the pane, the partial lives in the preview. The fields in a control update automatically to reflect the values of its associated settings, and a partial refreshes in the preview when one of its settings is changed. The name “partial” is derived from get_template_part(), which is a natural function to use in a partial’s render_callback.
In addition to a partial being associated with one or more settings, a partial is also registered with a jQuery selector which identifies the partial’s locations or “placements” on the page, where the rendered content appears. For example, a partial can be registered for the site title to update the header as well as the document title via:
function my_register_blogname_partials( WP_Customize_Manager $wp_customize ) {
// Abort if selective refresh is not available.
if ( ! isset( $wp_customize->selective_refresh ) ) {
return;
}
$wp_customize->selective_refresh->add_partial( 'header_site_title', array(
'selector' => '.site-title a',
'settings' => array( 'blogname' ),
'render_callback' => function() {
return get_bloginfo( 'name', 'display' );
},
) );
$wp_customize->selective_refresh->add_partial( 'document_title', array(
'selector' => 'head > title',
'settings' => array( 'blogname' ),
'render_callback' => 'wp_get_document_title',
) );
}
add_action( 'customize_register', 'my_register_blogname_partials' );
When a setting (like blogname here) is modified, each registered partial is asked on the client whether it isRelatedSetting(). If that method for a partial returns true, then the partial’s refresh() method is invoked and a request is queued up to render the partial via the requestPartial() function. Calls to this function are debounced and multiple simultaneous partials being changed will be batched together in a single Ajax request to the server.
When the server receives a request to render partials, it will receive the list of partials as well as all of the modified settings in the Customizer. Partials need not be all registered up front: as settings support the customize_dynamic_setting_args and customize_dynamic_setting_class filters, so too partials support the customize_dynamic_partial_args and customize_dynamic_partial_class filters. These filters allow partials to be added on demand to support the partials being requested to render.
If there is no registered partial for the request, or if the partial’s render_callback returns false, then false will be returned in the response as the content for that partial, and the default fallback() behavior in the client-side Partial object is to requestFullRefresh(). On the other hand, if the request recognizes the partial and the render_callback returns content, then this content is passed along to the Partial instance in the client and its renderContent() method then follows through to update the document with the new content.
As noted previously, the location in the document where a partial renders its update is called a placement. A selector may match any number of elements, and so too a partial may update multiple placements. A partial’s placements in the document may each have different sets of context data associated with them. This context data is included in the partials request and is passed along to the render_callback so that the content can vary as needed, for example a widget appearing in two different sidebars would have different sidebar_id context data, which would then determine the before_widget and after_widget args used.
Partial placements may also be nested inside other placements. For example, a Custom Menu widget is rendered as a placement which then contains another placement for a nav menu. The nav menu placement is updated independently from the parent widget placement. This being said (and this is advanced usage), a selector does not need to be supplied when registering a partial: alternatively to a top-down selector, the placement for a partial can be declared from the bottom-up by adding a data-customize-partial-id attribute to any container element. Whenever a partial is re-rendered, any nested partials will be automatically recognized. (As noted above, each placement can have a different associated context, and this is indicated by JSON object in a data-customize-partial-placement-context HTML attribute.)
Aside: The architecture of the Infinity Scroll module in Jetpack mirrors Selective Refresh framework in several ways.
Linking Partials to Controls
Since each partial is associated with at least one setting, shift-clicking on the placement for a partial will attempt to focus on the related control in the pane. This was already implemented for widgets since 3.9 and recently for nav menu items as well, but now the functionality is available to partials generally.
Selective Refresh of Widgets
Selective refresh for nav menus was enabled by default in 4.3. While some themes needed to add theme support for any dynamic aspects (such as the expanding submenus in Twenty Fifteen), generally nav menus seem to be fairly static and can be replaced in the document without needing any JS logic to be re-applied. Widgets, on the other hand, have much more variation in what they can display, since they can in fact display anything. For any widget that uses JavaScript to initialize some dynamic elements, such as a map or gallery slideshow, simply replacing the widget’s content with new content from the server will not work since the dynamic elements will not be re-initialized. Additionally, the sidebars (widget areas) in which the widgets are displayed may also have dynamic aspects associated with them, such as the Twenty Thirteen sidebar which displays widgets in a grid using Masonry. For this theme, whenever a widget is changed/added/removed/reordered, the sidebar needs to be reflowed.
In order to allow themes to reflow sidebars and widgets to reinitialize their contents after a refresh, the selective refresh framework introduces widget-updated and sidebar-updated events. Additionally, since re-ordering widgets in a sidebar is instant by just re-ordering the elements in the DOM, some widgets with dynamically-generated iframes (such as a Twitter widget) may need to also be rebuilt, and for this reason there is a partial-content-moved event.
For the above reasons, I believe it will be much more common for widgets (and sidebars) to need special support for selective refresh, and so I think that at least for 4.5 the selective refresh should be opt-in for widgets (and perhaps in themes for sidebars). See theme support for Twenty Thirteen and plugin support for a few widgets in Jetpack (which won’t be part of the merge). At last week’s Core dev meeting, it was suggested that we add the opt-in for widget selective refresh at RC.
Update: See follow-up post on Implementing Selective Refresh Support for Widgets.
Selective Refresh API
What follows is a list of aspects of the API that should be more commonly used. For full documentation, see the inline docs in the plugin source.
PHP
I suggest that Selective Refresh be added as a new Customizer component to be accessed via $wp_customize->selective_refresh which is where the methods for partials(), add_partial(), get_partial(), remove_partial(), and others would be accessed.
New classes:
WP_Customize_Selective_RefreshWP_Customize_Partial
New filters:
customize_partial_rendercustomize_partial_render_{$partial->id}customize_dynamic_partial_argscustomize_dynamic_partial_classcustomize_render_partials_response
New actions:
customize_render_partials_beforecustomize_render_partials_after
JS
Namespace: wp.customize.selectiveRefresh
New classes:
wp.customize.selectiveRefresh.Partialwp.customize.selectiveRefresh.Placementwp.customize.navMenusPreview.NavMenuInstancePartialwp.customize.widgetsPreview.WidgetPartialwp.customize.widgetsPreview.SidebarPartial
New properties:
wp.customize.selectiveRefresh.parital(collection)wp.customize.selectiveRefresh.partialConstructor
Events (triggered on wp.customize.selectiveRefresh):
render-partials-responsepartial-content-renderedpartial-placement-movedwidget-updatedsidebar-updated
The Future
If we can eliminate full-page refreshes from being the norm for the Customizer, we can start to introduce controls inline with the preview. If the entire preview does not reload, then the inline controls won’t get destroyed by the refresh with each change. For example, consider a widget control floating immediately next to the widget in the sidebar it is editing. With selective refresh, it will then also be possible to eliminate the Customizer altogether. The Customizer could be available to anyone who is logged in, with the controls being bootstrapped on demand when a user wants to edit a given element. There would be no need to navigate way from a page on the frontend to enter a unique Customizer state: the Customizer would come to the user. Any controls not relevant to being inline could remain in the Customizer pane, but it could slide in only as needed instead of appearing by default. That is to say, selective refresh makes the Customizer a much better framework for implementing frontend editing.
Feedback
There is just over a week until the first beta for 4.5. I’d like to get any final feedback on the feature as soon as possible before committing it to trunk. The plugin is on GitHub and the plugin directory. Feedback should be left on #27355. Note that the plugin depends on 4.5-alpha-35776 to run.


chatmandesign 3:13 pm on February 16, 2016 Permalink | Log in to Reply
Why does this need its own API? Shouldn’t it just be done using the REST API? That would also make it easy to register AJAX-refreshable regions on the front end. This seems like a lot of duplicated effort.
Weston Ruter 4:41 pm on February 16, 2016 Permalink | Log in to Reply
If your theme is making use of the REST API and it is using the WP-API JS Backbone client, then you can also use the Customizer to preview changes to those resources in a proof of concept Customize REST Resources plugin (see Bridging the Customizer and the WP REST API).
If your theme is not already using the REST API then there is nothing about the REST API that will just automatically handle selective refresh in the Customizer preview. While the Customize REST Resources plugin does inject the Customizer state into Ajax requests to the REST API, general Ajax requests cannot get the Customizer state injected into them if they are GET requests because the Customizer requires POST. Customizer transactions (#30937) will facilitate previewing changes to any Ajax requests.
In any case, the underlying need for selective refresh in the Customizer is for settings (resources) to be associated with arbitrary regions in the preview and to refresh those regions when an associated setting changes. There is nothing about the REST API that handles this for us.
Davide 'Folletto' Casali 4:29 pm on February 16, 2016 Permalink | Log in to Reply
I think this vision is conflating two different things, namely:
1. the design of the UI shows up
2. the direct manipulation of content
This is very important, because “floating controls” and “inline controls” look nice and cool, but they scale very very little. If you check the history of interfaces you’ll notice that every “floating” element over time ends up being a sidebar (or some kind of static control). The reasons are subtle, but numerous: cognitively everything always appear in the same space, there’s a clear separation content and editing, and the sidebar doesn’t cover existing parts of the content itself.
I have the feeling that since we have the sidebar right now we want to eschew that for other UIs just because they feel “new”. I agree with that feeling, and there are many examples out there that use floating components. But you will also notice that the floating components are either very simple or very annoying to use.
That’s why the most effective UIs out there use direct manipulation properly (which gets enabled by the selective refresh discussed here) but also have a static sidebar on the side. See all the pro and consumer tools out there: Keynote, Sketch, Word… even Photoshop, the king of floating UIs, since a few years ago docked everything to the sides! 🙂
This to say also: nothing in having a sidebar limits the kind of direct manipulation and WYSIWYG editing we can add to the page. Nothing. 🙂
I’d like to avoid spending time going floating to then revert back right after because it won’t scale. 😉
So my suggestion here would be:
1. Let’s deep dive in both direct manipulation and direct editing (i.e. typing the text on the page, dropping images, etc)
2. Let’s also put any required UI in the sidebar, as years of work of the best direct manipulations tools out there teach us
Weston Ruter 4:45 pm on February 16, 2016 Permalink | Log in to Reply
Good points. I was waving my hands about the future there, not saying how exactly it will look like, but just saying what would be made possible. Whether the editing of elements is done via a control in a sidebar, or via a floating control, or via WYSWIG direct manipulation—all of these would be possible, and they could be implemented in plugins if not in core.
Davide 'Folletto' Casali 5:40 pm on February 16, 2016 Permalink | Log in to Reply
Ok thanks! I think it was worth regardless to make this conversation to surface. 🙂
sidati 7:39 pm on March 5, 2016 Permalink | Log in to Reply
Hello @westonruter,
Is there a way to get the new value of the edited control/setting inside the
render_callback?? it will be very helpful cause sometimes therender_callbackmay depending on the new value 😉Weston Ruter 7:42 pm on March 5, 2016 Permalink | Log in to Reply
@sidati The new value will be available by default because the
preview()method would have previously been called on the related settings. In other words, if you have a partial that is associated with ablognamesetting, and you make a change to this setting, then when the partial is rendered you will get the modified value if you doget_option( 'blogname' )as opposed to the actual value stored in the database. This is the fundamental component to selective refresh working, as it isn’t useful at all if therender_callbackalways outputs content using the values as stored in the DB. (As then it would always return the same output, unless the setting gets saved.)sidati 8:23 pm on March 5, 2016 Permalink | Log in to Reply
I got it 🙂
sidati 8:24 pm on March 5, 2016 Permalink | Log in to Reply
By the way, the `partial-content-rendered` get triggered 6 times each time, is that normal ??
Weston Ruter 8:57 pm on March 5, 2016 Permalink | Log in to Reply
@sidati maybe or maybe not. How many partials do you have registered? How many elements do their selectors match, all combined? Feel free to ping me on Slack and we can look into it more deeply to see if there is a defect.
Ahmad Awais 1:54 am on March 11, 2016 Permalink | Log in to Reply
Hey, @westonruter!
The `partial` only needs to be attached to a particular setting and not control? That shift-click approach would get added automatically?
Weston Ruter 6:56 am on March 11, 2016 Permalink | Log in to Reply
@mrahmadawais That’s right. Partials are connected to settings. Controls are also connected to settings. A partial is connected to a control by virtue of being connected to the same setting(s).
Ahmad Awais 2:15 am on March 12, 2016 Permalink | Log in to Reply
Got it. Thanks.
rmaiaco 8:11 pm on March 28, 2016 Permalink | Log in to Reply
Hi @westonruter!
first, congratulations for the selective refresh. The best feature of wordpress 4.5 for me. I have a question: when ‘selector’ is not found the page is reloaded. Is possible prevent this?
Example: My previewer control ‘X’ is active and your selector is ‘body #comment’. However im in homepage and the div “#comment” not exist. So, full reload is happend.
best regards.
Weston Ruter 8:21 pm on March 28, 2016 Permalink | Log in to Reply
@rmaiaco Thanks! And yes, you can disable the fallback behavior of doing a full-page refresh when the selector is not found. In the
$argspassed to$wp_customize->selective_refresh->add_partial()(or theWP_Customize_Partialclass constructor), just include'fallback_refresh' => false. By default the$fallback_refreshparam istrue, but you can override it like so.rmaiaco 8:36 pm on March 28, 2016 Permalink | Log in to Reply
Perfect @westonruter!
Help me a lot.
The XWP gain a new fan 🙂
Thanks!
rmaiaco 6:49 pm on April 11, 2016 Permalink | Log in to Reply
Hi @westonruter,
A question: I noticed that the elements that are associated with a javascript function do not work after the div recharge. I have to reload the js file again?
Ex: A menu inside in partial reloaded div not working.
Best regards.
Weston Ruter 9:53 pm on April 11, 2016 Permalink | Log in to Reply
@rmaiaco Good question. This is what the
partial-content-renderedevent is for. So what you need to do is listen for these changes, and then re-apply your JS logic to the container for the partial. Take a look at the Contact Info widget in Jetpack and how it puts logic into asetupContactMapsfunction which runs at init and also every time a widget partial is refreshed: https://github.com/xwp/wp-jetpack/blob/feature/selective-refresh-widget-support/modules/widgets/contact-info/contact-info-map.jsThat example includes some code specific to widgets, but you can simplify for your use.
rmaiaco 5:33 pm on April 13, 2016 Permalink | Log in to Reply
Great explanation, thanks @westonruter!
Eric Daams 1:31 am on April 13, 2016 Permalink | Log in to Reply
Hi @westonruter,
I assume that if you’re aiming for PHP 5.2 compatibility, the `render_callback` function should not be an anonymous function as in your `header_site_title` example?
Cheers,
Eric
Weston Ruter 8:01 am on April 13, 2016 Permalink | Log in to Reply
@ericdaams Yes. This is just an example which you could use in a plugin or theme. Using an anonymous function is not required by any means. It just makes the example easier to read.
jiggaman 9:16 pm on April 25, 2016 Permalink | Log in to Reply
Getting what I believe is a selective refresh bug in the 4.5 that didn’t exist before…using an older theme that has been working great. There are some pre-set menu locations: primary menu being one of them. Primary menu is also special in that that when the site is re-sized it has a mobile dropdown replacement. When a menu is created that is applied to primary menu, the refresh seems to be in a loop. Disabling the menu for primary menu gets it out of the loop.
jiggaman 11:02 pm on April 25, 2016 Permalink | Log in to Reply
Found the issue. There was a plugin that uses the following code:
function extra_css() {
echo ”;
echo ‘#customize-control-tt_default_body, #customize-control-tt_default_heading_1, #customize-control-tt_default_heading_2, #customize-control-tt_default_heading_3, #customize-control-tt_default_heading_4, #customize-control-tt_default_heading_5, #customize-control-tt_default_heading_6 { display: none; }’;
echo ”;
}
add_action(‘customize_register’, ‘extra_css’);
Commenting out the echo statements made the issue go away with the non-stop refreshing. This did not occur prior to 4.5. So maybe I have uncovered a small bug.
jiggaman 11:03 pm on April 25, 2016 Permalink | Log in to Reply
( The word style tag was filtered out of my last comment’s code example )
rmaiaco 12:03 pm on May 2, 2016 Permalink | Log in to Reply
Hi @westonruter!
Hi weston,
I adjusted all widgets in my theme to support the partial refresh. However i noticed two points:
01) The selectiveRefresh bind occur many times when the widget field controller is changed. I tried use the stopPropagation but not worked. My question: the event bubbling is normal? Should not occur only once?
02) In the theme control i can set the selector to receive the rendered content through ‘selector’.i don’t found this option in widget construct. So, how i can set the selector? I use the ‘sidebar-updated’ bind and ‘the placement.sidebarId’ to compare and select my div. However created duplicated widget with the refreshed content. Example:
Div content = ‘x’ —-> Changed content = ‘a’ —-> Rendered Content = ‘x’,’a’.
My code to remove duplicated widgets:
You can help me?
Best regards!
Ricardo Maia.
Weston Ruter 5:19 pm on May 2, 2016 Permalink | Log in to Reply
@rmaiaco Do you have partials nested in other partials? When a partial contains other partials, and this this parent partial is updated, it will trigger
partial-content-renderedevents on all nested partials so they can rebuild any dynamic aspects.You cannot use
stopPropagationor any of these DOM event methods since the events are not DOM events. There is no bubbling of events per se, other than this behavior of re-triggering thepartial-content-renderedevents for nested partials.As for widget partials, widgets should have wrapper elements according to the
before_widgetandafter_widgetsidebar args, and in particular the widget ID should be theidattribute of this wrapper element, along with the normalclassattribute for widgets. Doing selective refresh with non-standard widget wrapper elements has not been tested and would likely take some research to fully support.rmaiaco 6:35 pm on May 2, 2016 Permalink | Log in to Reply
@westonruter,
Eureka!
Both the sidebar widget as they were with the option customize_selective_refresh = true. I set partial only widget and worked!
You help me a lot!
Thanks!
rmaiaco 5:58 pm on May 16, 2016 Permalink | Log in to Reply
Hey @westonruter,
Me again 🙂
how i can invoke the selective refresh from javascript only?
Currently i use the “wp.customize.previewer.refresh()” after a ajax function. But i want refresh only partial.
It’s possible? Something like wp.customize.selectiveRefresh(‘ID OF PARTIAL’);
P.S:Thank you for helping me with questions that have appeared. I don’t found advanced materials about selective refresh.
Weston Ruter 10:24 pm on May 16, 2016 Permalink | Log in to Reply
@rmaiaco Why manually refresh? In practice since partials should be associated with settings, they should get refreshed when an associated setting is changed.
rmaiaco 11:15 pm on May 16, 2016 Permalink | Log in to Reply
Curiosity @westonruter. I added the settings of a plugin to Customize Nav Section and the fields are called by ajax. When they are changed the new values are saved by ajax too. However to show the change for user i need the reload full page using ‘wp.customize.previewer.refresh()’. If i could reload only partial would be awesome and best to the ux.
Thanks for your answer.
Weston Ruter 11:19 pm on May 16, 2016 Permalink | Log in to Reply
@rmaiaco You can refresh a partial manually via
wp.customize.selectiveRefresh.partial( partialId ).refresh()(where you supply the requiredpartialId). Do note that this has to be run inside the Customizer preview window.rmaiaco 11:28 pm on May 16, 2016 Permalink | Log in to Reply
Great @westonruter!
Clean and elegant solution for me.
Help me a lot. thanks!