Icons: Ship all SVG icons to WordPress core - #79102
Conversation
The generated `manifest.php` and the PHP icon registry exposed the icon path under a camelCase `filePath` key, which is inconsistent with PHP/WP array-key conventions. Rename it to `file_path` across the manifest PHP generator, both registry classes, and the test docblock. The `manifest.json` source intentionally keeps `filePath` (JS convention). Because the Gutenberg override inherits `get_content()` from the base class, also override `get_content()` in `WP_Icons_Registry_Gutenberg` so content is read from the `file_path` property even when the base class comes from WordPress core (which may still use `filePath`), preventing a key mismatch that would silently break icon content retrieval. Co-Authored-By: Claude <noreply@anthropic.com>
|
Size Change: 0 B Total Size: 7.51 MB |
… in registry The previous commit renamed the icon path key to snake_case file_path across both the generated manifest.php and its generator. The manifest mirrors the JS manifest.json, which uses camelCase filePath, so the generated PHP manifest should keep filePath for consistency with its source. Revert the generator and manifest.php back to filePath. The PHP registry still exposes the path as file_path (PHP/WP array-key convention), so register_collection now reads the camelCase filePath from the manifest and converts it to file_path when registering. This confines the filePath-to-file_path conversion to the registry boundary. Co-Authored-By: Claude <noreply@anthropic.com>
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>
7db08d2 to
9fdaf8b
Compare
|
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. |
No, if it hasn't been displayed in the icon block before, it won't be displayed as before.
|
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
|
Thanks for the clarification, I missed the detail 🙏 |
|
Flaky tests detected in 617f209. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/27330510023
|
|
Hey, @t-hamano! I'm worried by this change. We introduced the 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 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'; |
There was a problem hiding this comment.
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'] ), |
There was a problem hiding this comment.
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'] ) ) { |
| } | ||
|
|
||
| if ( isset( $icon_properties['show_in_rest'] ) && ! is_bool( $icon_properties['show_in_rest'] ) ) { | ||
| _doing_it_wrong( |
| * @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 ) { |
There was a problem hiding this comment.
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'] ) ) { |
There was a problem hiding this comment.
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' ), |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Note that spacing will need to be aligned here.
|
Thanks for the review! Let me explain the intention behind this PR in a bit more detail.
The Furthermore, the reason I changed
I agree. I think we really need to scrutinize and remove icons that are not worth releasing to developers. |
Right, I see the desire for symmetry, though it's apples and oranges: While a registered meta key hidden from REST is indeed "visible" to third parties via PHP, the semantics of meta mean that any third-party access will be guarded (is the key registered? are there meta values for this key and this object ID?). Furthermore, Core itself rarely registers meta/settings of its own, and when it does it's usually understood to be an internal matter. All things combined, we can reasonably expect third parties to either not interact with meta that they haven't registered themselves, or be exceedingly defensive when doing so. In contrast, icons are a resource/asset meant to be found and used. If any third party can enjoy icons by simply calling
Pinging @jasmussen and @tyxla too: With our original introduction of the
Since the motivation for this PR and |
|
Thanks for the ping, thansk for the review Miguel and thanks for the code Aki. For me, what is important is that we can energise and use WordPress icons. It's a good set, we might soon add stroke-based scaling effects, and following up on that, a consistency pass, deprecate some icons. In fact I expect to redraw a fair bit of icons too. Aside enabling their usage to support an admin refresh, I would like to help make this set beautiful, and both easy and compelling to use. I'm quite excited for all of that, and feel like we are only a couple of decisions away from unblocking good energy here. In saying that, I'm also clearly delineating what's my area of expertise, the design and the systematic approach for engaging with it. Which is to say, inline SVGs appears to be the most solid approach adopted broadly by the industry (with GitHub Octicons being a prime example of a great set that moved away from And I can say with some confidence that I'd like to deprecate additional icons (such as some of the filled versions), and update others (such as insertBefore). For example if we deprecate plusCircleFilled, we can provide some form of deprecation by pointing to plusCircle instead, whether through a permanent or temporary additional export under the same name, and mark it as a breaking change in the README. But I can't say whether that same method for deprecating icons can work the same for a PHP API as it currently does for the React API. And in both cases, I can't say for either, whether making all these icons "public" or not, is a good idea, like how we did in #75526. But I will say that the primary motivation with that reduction in the set was that, both then and as now, we knew we were going to clean things up further, and we didn't want to expand the API surface to the Icons block—public content on webpages. In that sense, I've always appreciated at least limiting icons in that block, which I understand remains in place regardless of the registry. So I'll keep investing energy in icons, their design, updates, a few deprecations. And I've put on my todo list to create a list, like I did in #75526, of which icons I would denote as "at risk" at least as far as needing visual updates or possibly coming on the chopping block: I can have one ready tomorrow. What we do with such a list, I will trust into your capable hands. |
|
Following up, and let me know if there's a better place to post these. Here's a list of icons, and their status, rather emotionally felt and extremely subjectively assessed with all the shortcomings that entails:
I want to emphasise the human fault possible in all this. These are my instinct. I'll elaborate, but I share them mainly to help any technical decisions happen, as far as how we might handle deprecations or updates in the future, as the API surface increases.
Let me know if this tells us anything about next steps, or is useful otherwise. I don't necessarily attach a timeline to any of this: my first order of business is to enable us to use at least some of our WordPress icons in places we currently use Dashicons, second order of business is to convert the whole set to be stroke-based. Edit: see this comment about pullquote. |
|
Thanks for the detailed answer, Joen! It all sounds reasonable, though I should be clear that I'm no position to consider the fate of the icons themselves. :) I would ask one thing, which is to attach your categorisation to this thread as text (i.e. as lists of slugs), so that we can easily apply/repro the classification in development.
Sounds good. In this set contained within the Mostly Stable set? |
Should be, yes. Perhaps lock, unlock, and cog, are ones that have broad utility as well, but we can negotiate details if it comes to that. Here are the same icons, in text. Note, these are based on the SVG filenames, which in order to be jsx names I think you need to convert them to camelcase. I.e. Mostly stable
Could be improved
Needs improvement
Needs thought/stroke-based
Deprecation candidates
Edit: see this comment about pullquote. |
I agree that we should seek simplicity here, and a |
|
An important correction to this list, the pullquote icon should not be a deprecation candidate. I had mistakenly thought the Pullquote block itself had been deprecated, it appears to be back, and so of course the Pullquote icon should remain. |
|
Thank you all for your wonderful suggestions. My final thought is to postpone this PR to 7.2. The icons still have room for improvement, and I think we should discuss more carefully which ones should be released. For now, it might be better to focus on improving the existing icon registry and shipping the custom icon registration API. The custom icon registration API is likely the most requested feature by consumers. |
Thanks for all the energy. Just to be sure I understand correctly: does #78332 depend on this PR in order to land in 7.1, or do you propose both are postponed to 7.2? I don't know that I have strong opinions either way, just making sure I understand. |
|
Let me explain my intentions regarding this PR a bit further. If the API for custom icon registration becomes available in #77260, consumers will be able to register icons with code like the following: add_action( 'init', function () {
wp_register_icon_collection( 'my-icons', array(
'label' => 'My Icons',
) );
wp_register_icon( 'my-icons/star', array(
'label' => 'Star',
'content' => '<svg></svg>',
) );
}, 20 );What if consumers want to control whether their blocks should be displayed in the Icon block? add_action( 'init', function () {
wp_register_icon_collection( 'my-icons', array(
'label' => 'My Icons',
) );
// Since this icon is intended to be used within the content,
// make it available in the Icon block.
wp_register_icon( 'my-icons/star', array(
'label' => 'Star',
'content' => '<svg></svg>',
'show_in_rest' => true,
) );
// This icon is used on the server-side or for the dashboard.
wp_register_icon( 'my-icons/star', array(
'label' => 'Gear',
'content' => '<svg></svg>',
) );
}, 20 );I feels like we need some option to control whether an icon should be used in post content, perhaps via a key like "public", "show_in_rest", or something similar. |
Sounds right. And, for future readers, we seem to agree on a "ternary" |
|
Thank you, everyone. For now, it seems best to decide on the icons to be published with only the public fields mentioned in #79451 (comment). Let's close this PR for now. |

file_pathkey in icon registry #79100What?
Previously, only icons marked as "public" within the
iconspackage were available in the icon registry. This has been achieved through the following mechanism:manifest.json, add the field"public": trueto the icons we want to make public.manifest.phpfrommanifest.json, only icons with"public": truedefined are included.manifest.php.This PR removes this restriction, making all icons defined in
manifest.jsonavailable in the icon registry. However, icons that were previously public are now marked with"show_in_rest": trueand 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?
"public": truewith"showInRest": trueinmanifest.json.manifest.php, always include the"show_in_rest": true or falsefield.manifest.jsonormanifest.phpand 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
Confirm all registered icons
Check the registered icons using the following code.
Use of AI Tools
Although most of the code was written by Claude, I reviewed all of it myself.