[EuiIcon] Rewrite icon assets to explicit dynamic imports#9342
Merged
weronikaolejniczak merged 5 commits intoelastic:mainfrom Feb 3, 2026
Merged
Conversation
f711b4e to
fb46a90
Compare
fb46a90 to
6a0b6dc
Compare
cf358d0 to
44cc4d9
Compare
Collaborator
💚 Build SucceededHistory
|
Collaborator
💚 Build Succeeded
History
|
dfvalero
approved these changes
Feb 3, 2026
Contributor
dfvalero
left a comment
There was a problem hiding this comment.
I can't express how happy I am to see this getting fixed. Really appreciate it!
tkajtoch
approved these changes
Feb 3, 2026
Member
tkajtoch
left a comment
There was a problem hiding this comment.
I tested various usecases locally and can confirm the solution works as reliably as the previous one while bringing support for non-webpack bundlers. Code changes look good. LGTM
|
AWESOME ! How can integrate into Vite now? |
Contributor
Author
|
Hey @adhishthite 👋🏻 it should be released next week (likely |
|
Thanks!
…On Tue, Feb 3, 2026 at 7:46 PM Weronika Olejniczak ***@***.***> wrote:
*weronikaolejniczak* left a comment (elastic/eui#9342)
<#9342 (comment)>
Hey @adhishthite <https://github.com/adhishthite> 👋🏻 it should be
released next week (likely v112.3.0). All you have to do is remove your
appendIconComponentCache usage and all icons should load dynamically. LMK
if it doesn't work!
—
Reply to this email directly, view it on GitHub
<#9342 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AHSMKJSNTUJY7CE6NB6G3ED4KCUUXAVCNFSM6AAAAACTKCOEGCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTQNBRGU4TKOBUGQ>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
Contributor
|
Amazing job on this Weronika!!! 🥹 You guys are doing such great work! |
mgadewoll
added a commit
to elastic/kibana
that referenced
this pull request
Feb 10, 2026
## Dependency updates - `@elastic/eui`: v112.2.0 ⏩ v112.3.0 - `@elastic/eslint-plugin-eui`: v2.7.0 ⏩ v2.8.0 --- ## Package updates ### `@elastic/eui` [v112.3.0](https://github.com/elastic/eui/releases/tag/v112.3.0) - Added new `server` icon. ([#9355](elastic/eui#9355)) - Added `className` support to `EuiMarkdownEditor`'s `toolbarProps` for custom toolbar styling ([#9349](elastic/eui#9349)) - Updated `EuiFilePicker` to use the `upload` icon to better indicate uploads. ([#9351](elastic/eui#9351)) - Exported the flyout system store singleton and added an event observer for emitting close session events ([#9347](elastic/eui#9347)) - Updated `EuiIcon` to use standard dynamic imports for icon assets, enabling native support for modern bundlers (Rollup, Parcel) and improving initial load performance ([#9342](elastic/eui#9342)) **Bug fixes** - Fixed a potential crash in the flyout system: due to asynchronous state updates and React's batching behavior, it was possible to experience a crash when closing a managed flyout. ([#9356](elastic/eui#9356)) ### `@elastic/eslint-plugin-eui` v2.8.0 - Added new `icon-accessibility-rules` rule. ([#9357](elastic/eui#9357)) - Added new `badge-accessibility-rules` rule. ([#9354](elastic/eui#9354))
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
icon_map.tsto use dynamic imports instead of string values. 84b73d1icon_map.tsloaders directly inpackages/eui/src/components/icon/icon.tsx. 84b73d1Note
For consumers, please try removing your patches and
appendIconComponentCacheusages once the change gets released (ETA v112.3.0). If you find any issues, please open a new bug report.Why are we making this change?
Resolves #5463
Resolves #5305
Relates to #8860
The gist of EUI icon approach
We hold all SVG files in:
packages/eui/src/components/icon/svgs. We use svgr inpackages/eui/scripts/compile-icons.js(that's executed in our building pipeline) to transform the SVG files into React components. We add.tsxfiles intopackages/eui/src/components/icon/assetsfolder and inpackages/eui/src/components/icon/icon_map.tswe reference the paths. Later, inEuiIconsource:packages/eui/src/components/icon/icon.tsxwe reference this map:eui/packages/eui/src/components/icon/icon.tsx
Lines 202 to 208 in b715720
and access the
assets/folder paths usingiconTypepassed to theEuiIconcomponent, importing the path dynamically and rendering the React components.Problem context
For a long time, the way icons were loaded was by using one dynamic import with path string concatenation, as you can see above, and letting Webpack know to chunk it with magic comments. This is convenient because we can load them all with one import. Likely because Kibana uses Webpack and EUI was predominantly written for Kibana use.
The biggest downside to this approach is the vendor lock-in. As soon as modern bundlers started becoming standard, now tools like Vite, NextJS successfully burying CRA and Webpack-based tooling, this became an issue. In parallel, bundlers like Parcel started supporting dynamic imports. Modern bundlers need to see explicit dynamic imports to be able to load the modules.
Solution
The solution is very simple. Instead of writing path strings in
icon_map.ts:we use dynamic imports:
that should allow all bundlers to statically see all needed modules, and chunk them accordingly.
Advantages
The bonus to this approach as opposed to
appendIconComponentCacheis:Disadvantages
The only downside of on-demand loading is a tiny delay when an icon appears but we have an in-built loading state (fade in) to prevent layout shifts. Once the icon is loaded, it is cached for the rest of the session.
Performance audits
Storybook (Webpack but no preloading)
Measured on the EuiButton Playground story.
Kibana (Webpack)
Followed Testing in Kibana wiki. Resolved
@elastic/euitopackage.tgzgenerated by runningbuild-packon this branch. Tested before and after.CLS was repeatedly lower by
~0.076but it's fluctuating. Speed Index was reported1slower but I'm not confident it's saying much considering Kibana's size. What would warrant any worry would be the decrease in JS Requests (i.e. icons are possibly incorrectly chunked).The bottom line is, there's no regression for the main consumer.
Icons are chunked correctly. Since we removed the magic comments, the names are just hashes.
AutoOps (Vite/Rollup)
Followed AutoOps README.md. Resolved
@elastic/euitopackage.tgzgenerated by runningbuild-packon this branch. Tested before and after.This effectively demonstrates the dynamically loaded icons (as opposed to preloaded ones).
Additional details
#5463 (comment)
This is simply not the case. The relative path dynamic imports work identically to the current string concatenation
"./assets/" + type. When the project is built, no matter the target (es,lib,optimize/es,optimize/lib), the build preserves the directory structure. So the sourceicon_map.jsis right next toassets/folder. Because they move together to the destination folder, the relative path remains valid for all 4 build targets and local dev.Missing extensions
All modern bundlers are capable of resolving the extension. There is no need to provide them directly in our source code. If we did that, it would have to be
.jsand that would lead to references not being resolved on our side and TypeScript overrides / muting comments.Impact to users
🟢 Webpack-based projects keep resolving and chunking the icons as needed. Projects using modern bundlers like Rollup, Parcel don't require
appendIconComponentCacheusage and all icons are loaded runtime as used, not all preloaded.QA
Specific checklist
appendIconComponentCachein a boilerplate Parcel projectyarn workspace @elastic/eui build-packnpm create parcel react-client test-eui-parcel(see docs)appendIconComponentCachein a boilerplate Vite project (Rollup)yarn workspace @elastic/eui build-packnpm create vite@latest test-eui-vite -- --template react-ts(see docs)appendIconComponentCachein AutoOps project (thanks @dfvalero 🙌🏻)I went through these myself, feel free to double-check.
General checklist
@defaultif default values are missing) and playground toggles