Hi @gilbarbara — reviving the second point of #167, with an implementation this time.
The cost
cacheRequests caches the SVG text, but nothing caches the result of converting it. useInlineSVG runs getNode() (a DOMParser parse) plus a DOM → React conversion on every mount, including on a cache hit — the lazy reducer initializer re-parses cached content. So the cost scales with the number of mounts, not with the number of distinct SVGs, and surfaces that mount icons constantly by design (virtualized tables, log lines, trees) pay it over and over.
Measured in Grafana, in a table panel with 300 rows and a handful of repeated icons: 600+ parses during ~25s of scrolling, resizing and hovering. Scrolling up and down mounted ~500 icons — all of them already in the request cache, all of them re-parsed.
The proposal
A cacheElements prop, off by default, that keeps the converted React element and reuses it. Same measurement afterwards: 4728 icon mounts → 2 conversions (one per distinct icon the session ever showed).
Design notes, since the size of the change was the concern last time (it is ~70 lines of src):
- Entries are keyed by the content and by every prop that shapes the output: the
preProcessor result, title (with undefined — keep an existing <title> — kept distinct from null — remove it), description, and for uniquifyIDs the hash and baseURL. Instances with random hashes therefore never share an entry; ones that pass the same uniqueHash do.
- Module-level LRU capped at 100 elements, with a
clearElementCache() export.
- Nothing changes when the prop is not passed: no reads, no writes, no growth.
dist stays inside the existing 10 kB size limit (7.83 kB CJS / 7.29 kB ESM).
For context on the consumer side, this is the workaround we are thinking about adding in Grafana: grafana/grafana#132209 — we would much rather not, and use a library option instead.
Hi @gilbarbara — reviving the second point of #167, with an implementation this time.
The cost
cacheRequestscaches the SVG text, but nothing caches the result of converting it.useInlineSVGrunsgetNode()(aDOMParserparse) plus a DOM → React conversion on every mount, including on a cache hit — the lazy reducer initializer re-parses cached content. So the cost scales with the number of mounts, not with the number of distinct SVGs, and surfaces that mount icons constantly by design (virtualized tables, log lines, trees) pay it over and over.Measured in Grafana, in a table panel with 300 rows and a handful of repeated icons: 600+ parses during ~25s of scrolling, resizing and hovering. Scrolling up and down mounted ~500 icons — all of them already in the request cache, all of them re-parsed.
The proposal
A
cacheElementsprop, off by default, that keeps the converted React element and reuses it. Same measurement afterwards: 4728 icon mounts → 2 conversions (one per distinct icon the session ever showed).Design notes, since the size of the change was the concern last time (it is ~70 lines of
src):preProcessorresult,title(withundefined— keep an existing<title>— kept distinct fromnull— remove it),description, and foruniquifyIDsthe hash andbaseURL. Instances with random hashes therefore never share an entry; ones that pass the sameuniqueHashdo.clearElementCache()export.diststays inside the existing 10 kB size limit (7.83 kB CJS / 7.29 kB ESM).For context on the consumer side, this is the workaround we are thinking about adding in Grafana: grafana/grafana#132209 — we would much rather not, and use a library option instead.