Skip to content

Icons: Ship all SVG icons to WordPress core#79102

Open
t-hamano wants to merge 5 commits into
icons/snake-casefrom
icons/expose-all-icons
Open

Icons: Ship all SVG icons to WordPress core#79102
t-hamano wants to merge 5 commits into
icons/snake-casefrom
icons/expose-all-icons

Conversation

@t-hamano

@t-hamano t-hamano commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

What?

Previously, only icons marked as "public" within the icons package were available in the icon registry. This has been achieved through the following mechanism:

  • In manifest.json, add the field "public": true to the icons we want to make public.
  • When generating manifest.php from manifest.json, only icons with "public": true defined are included.
  • Icons are registered in the icon registry based on the generated manifest.php.

This PR removes this restriction, making all icons defined in manifest.json available in the icon registry. However, icons that were previously public are now marked with "show_in_rest": true and are only available via the REST API. This ensures that only the restricted core set of icons remains available in the Icon block as before.

How?

  • Replace "public": true with "showInRest": true in manifest.json.
  • When generating manifest.php, always include the "show_in_rest": true or false field.
  • This PR means that a stricter backward compatibility policy will be applied to a wider range of icons. In other words, once an icon is defined in manifest.json or manifest.php and shipped to the core, it can never be removed. See this document for more details. In the future, I might consider implementing a mechanism to deprecate icons in some way, for example, like this:
    [
    	{
    		"slug": "clock",
    		"label": "Clock",
    		"filePath": "library/align-right.svg"
    	},
    	{
    		"slug": "time",
    		"label": "Time",
    		"deprecated": true,
    		"alias": "clock"
    	}
    ]

Testing Instructions

Icon block

  • Open the post editor and insert an Icon block.
  • Open the Icon Library modal.
  • Confirm that only icons defined as public will be displayed.

Confirm all registered icons

Check the registered icons using the following code.

add_action(
	'init',
	function () {
		$registry = WP_Icons_Registry::get_instance();

		// Output icons that are not exposed by the REST API.
		$icon_1 = $registry->get_registered_icon( 'core/accordion' );
		$icon_2 = $registry->get_registered_icon( 'core/background' );

		echo '<div style="display:flex;flex-wrap:wrap;gap:16px;">';
		echo '<div style="width:24px;">' . $icon_1['content'] . '</div>';
		echo '<div style="width:24px;">' . $icon_2['content'] . '</div>';
		echo '</div>';

		// Output all registered icons.
		$icons = $registry->get_registered_icons();
		echo '<div style="display:flex;flex-wrap:wrap;gap:16px;">';
		foreach ( $icons as $icon ) {
			echo '<div style="width:24px;">' . $icon['content'] . '</div>';
		}
		echo '</div>';
	},
	10
);

Use of AI Tools

Although most of the code was written by Claude, I reviewed all of it myself.

@github-actions github-actions Bot added the [Package] Icons /packages/icons label Jun 11, 2026
@github-actions

Copy link
Copy Markdown

Size Change: 0 B

Total Size: 8.46 MB

compressed-size-action

@t-hamano t-hamano added [Type] Enhancement A suggestion for improvement. [Feature] Icons Related to Icon registration API and Icon REST API labels Jun 11, 2026
t-hamano and others added 4 commits June 11, 2026 15:49
Co-Authored-By: Claude <noreply@anthropic.com>
The show_in_rest filter belonged in the REST controller, not the
registry. Filtering inside get_registered_icons() prevented every
internal caller from retrieving the full set of registered icons,
which contradicts the intent of shipping all icons while exposing
only a subset through REST.

- get_registered_icons() now returns all icons regardless of show_in_rest.
- The REST list and single-item endpoints filter by show_in_rest, so
  non-public icons are no longer leaked via GET /icons/<name>.
- Mirror show_in_rest handling in the WP 7.0 base registry (constructor,
  allowed keys, boolean validation) so the controller filter works even
  when the Gutenberg registry override is not active.
- Preserve show_in_rest when replaying non-core icons during the
  registry swap.

Co-Authored-By: Claude <noreply@anthropic.com>
…ller

The REST filtering was only applied in the WP 7.0 compat controller,
which is guarded by class_exists() and skipped whenever WordPress core
already defines WP_REST_Icons_Controller. In that case the route is
served by WP_REST_Icons_Controller_Gutenberg, which inherited the
unfiltered core get_items() and exposed every registered icon
regardless of show_in_rest.

Override get_items() and get_icon() in the always-loaded Gutenberg
controller so non-public icons are excluded from both the list and the
single-item endpoints independently of the WordPress version. The
registry keeps returning all icons, since server-side rendering looks
up icons by name via get_registered_icon() and must resolve content
even for icons that are not exposed through REST.

Co-Authored-By: Claude <noreply@anthropic.com>
Restore the cosmetic Markdown changes (emphasis style, link format, and
the "Code is Poetry" footer) that were unrelated to this PR, keeping
only the showInRest documentation relevant to the change.

Co-Authored-By: Claude <noreply@anthropic.com>
@jasmussen

Copy link
Copy Markdown
Contributor

Thanks for all the help with icons.

Just to be sure, does this PR make the icons public in the Icons block? I'd prefer if we still show only a limited subset there, because once they are available to that block, they are much harder to deprecate, which is something being discussed in context of the fresh energy there is in the icon space.

@t-hamano

Copy link
Copy Markdown
Contributor Author

does this PR make the icons public in the Icons block?

No, if it hasn't been displayed in the icon block before, it won't be displayed as before.

However, icons that were previously public are now marked with "show_in_rest": true and are only available via the REST API. This ensures that only the restricted core set of icons remains available in the Icon block as before.

@t-hamano t-hamano marked this pull request as ready for review June 11, 2026 07:17
@t-hamano t-hamano requested a review from spacedmonkey as a code owner June 11, 2026 07:17
@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: t-hamano <wildworks@git.wordpress.org>
Co-authored-by: tyxla <tyxla@git.wordpress.org>
Co-authored-by: jasmussen <joen@git.wordpress.org>
Co-authored-by: mcsf <mcsf@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@jasmussen

Copy link
Copy Markdown
Contributor

Thanks for the clarification, I missed the detail 🙏

@github-actions

Copy link
Copy Markdown

Flaky tests detected in 617f209.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27330510023
📝 Reported issues:

@t-hamano t-hamano self-assigned this Jun 11, 2026
@mcsf

mcsf commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Hey, @t-hamano!

I'm worried by this change. We introduced the public field precisely because we weren't confident about many of the icons in the core library. So we hid the non-public icons from any consumers interacting with the icons registry (whether directly or via the REST endpoint), but we kept them for our own uses within the client application. This distinction was easy to make because the latter was based on explicit JS imports and was "safeguarded" by the bundled nature of the Icons package.

But now I don't understand the reasoning behind hiding them in the controller while showing them in the registry. The registry is accessible by any third party. And so will wp_get_icon as proposed in #78332.

IMO, this change entails a change in "policy" around the core icon library, but as far as I can tell this fact isn't made explicit. If we're to proceed, shouldn't we audit the library to make sure we actually want to "promote" all the icons to registry-accessible?

const key = formatPHPKey( item.slug, maxKeyLength );
const label = escapePHPString( item.label );
const filePath = escapePHPString( item.filePath );
const showInRest = item.showInRest ? 'true' : 'false';

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Noting that the icon manifest obviously grew a lot with this change. That's likely fine on its own, but I'm also considering the changes in #79100 where we're now essentially hydrating the content for every icon whose content is on the filesystem. That might mean hundreds of disk I/O operations; is it possible this will lead to a performance regression?

I'm not sure what the solution is, but maybe it makes sense to consider lazy hydrating the icon content, or maybe introducing some filtering so we don't call get_content() on icons that we won't really need yet.

'file_path' => $icons_directory . $icon_data['filePath'],
'label' => $icon_data['label'],
'file_path' => $icons_directory . $icon_data['filePath'],
'show_in_rest' => ! empty( $icon_data['showInRest'] ),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add test for the show_in_rest REST API exclusion behavior?

public function get_icon( $name ) {
$icon = parent::get_icon( $name );

if ( ! is_wp_error( $icon ) && empty( $icon['show_in_rest'] ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also needs a test IMO.

}

if ( isset( $icon_properties['show_in_rest'] ) && ! is_bool( $icon_properties['show_in_rest'] ) ) {
_doing_it_wrong(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test for this too?

* @param WP_REST_Request $request Full details about the request.
* @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
*/
public function get_items( $request ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be fully reimplementing the parent class method. Should we try to reuse it as much as possible and just sprinkle the show_in_rest filter logic on top?

public function get_icon( $name ) {
$icon = parent::get_icon( $name );

if ( ! is_wp_error( $icon ) && empty( $icon['show_in_rest'] ) ) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, what's the scenario to this path of the code? Won't parent::get_icon() already return a 404 for non-REST icons?

return `${ key } => array(
'label' => _x( '${ label }', 'icon label', 'gutenberg' ),
'filePath' => '${ filePath }',
'label' => _x( '${ label }', 'icon label', 'gutenberg' ),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When are we actually going to surface the translations for icons that we don't show in the REST API? It seems a bit wasteful to localize all those strings and add hundreds of new non-translated strings to core if we're never going to surface them to end users.

* If not provided, the content will be retrieved from the `file_path` if set.
* If both `content` and `file_path` are not set, the icon will not be registered.
* @type string $file_path Optional. The full path to the file containing the icon content.
* @type bool $show_in_rest Optional. Whether the icon is exposed through the REST API.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that spacing will need to be aligned here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] Icons Related to Icon registration API and Icon REST API [Package] Icons /packages/icons [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants