Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Breaking Up with SVG-in-JS in 2023 (kurtextrem.de)
57 points by samwillis on July 2, 2023 | hide | past | favorite | 33 comments


> SVGs in JS have a cost and SVGs do not belong into your JS bundle

This article appears to do a reasonable job of weighing the the performance pros and cons to justify such a broad statement. However it fails to consider the use cases beyond plonking static SVG images into a page.

The initial example of "people are embedding SVG icon packages as SVG in JS" seems to be the only type of use case that each abstract justification is being validated in. This advice needs a heavy qualifier of "for static assets", and even then, only when dealing with large individual assets, or large quantities of individual and artificially inseparable SVGs such as icon packages. I'm suspicious this whole argument stems from people blindly "importing" svg bundles into JS without fully understanding what that actually means in the end result of their bundle. It would be more useful to talk about this issue with it's original context, where there are multiple lessons to learn than coerce it into an over generalisation.


While I agree it should be only used where absolutely necessary, another not insignificant benefit to 'inlining' svgs in jsx is that they're then visible immediately on first render, there's no loading time.

This is particularly relevant for icons and buttons in key places in the UI, where it's janky to have them appear even a split second after the rest of the UI.

How can you acheive that using other approaches?


Just include an SVG sprite in the HTML page. Then, like the article says, you can reference each icon with <use href="#icon-name" /> and they will be available far before your js bundle is parsed.


You could also just use data URIs in your CSS.

“How else could you possibly have SVGs render inline other than to include a massively bloated JS representation of a string in your bundle?”

- the React ecosystem, 2023

The answer, as you’ve shown, is “use the platform”. Sometimes I feel like React actively works against that.


I have this idea that as you go up a level of stack parts of the underlying stack get underused, people want to use things at the same level in the stack - for example if you are using React or Vue have everything React or Vue. This makes things work worse because of course you have those underlying levels for a reason, but it makes people feel architecturally purer or something (it is at this point the idea breaks down for me, I have no idea why it is that everything should be in whatever framework you are using, just that this seems to how everyone I ever work with feels on the matter)


I agree, I also think React is a particularly regular victim because of JSX. A JSXed SVG looks just the same as a regular SVG in your editor, if everyone was forced to see the endless chains of React.createElement() they might start to feel uneasy.


[Edited per below comment pointing out my mistake about external CSS loading order] -- this is a decent alternative, thanks!


External css files are render blocking, meaning any content after a <link rel=stylesheet> will not be visible until the css has been loaded.


Using SVG sprites gives benefit of caching, as icons tend to change less frequently than main application code. And be very careful to use real SVG, not bitmaps wrapped in SVG, as it will very quickly bloat your sprite size.


Are there any negative impacts to accessibility with the `<use href="#icon-name" />` sprite approach?


By not using jsx? It would be an order of magnitude less waste to just set innerhtml in such a case, than to waste your users' cpu cycles diffing and reconstituting a giant tree of vdom.


I'd also generalise the question beyond jsx to just 'inline with javascript' - ie just as you say, inject the svg markup into the DOM synchronously using the same javascript that renders the DOM (rather than some deferred source) - it's just most of the time for me that means jsx.


This sounds to me like "You should generate images one pixel at a time from javascript, because they will render immediately. Never use those obsolete <img> tags."


I think the long and short of it is if you need performance, don’t use an SPA. Anything you do in a JS bundle for an SPA is going to be more expensive than just doing a normal HTML page. And that’s fine! But don’t be confused about what you’re trading off against.


> if you need performance, don’t use an SPA

you can make a SPA using vanilla JS, without any bundle using plain es6 module and be perfectly fine. I made one such scaffold to showcase people how to do exactly that: https://mickael-kerjean.github.io/skeleton/example/index.htm...


Okay, but it will be slower to first render than a normal HTML page because you have to download the HTML plus execute the JS vs just download the HTML. Again, that’s fine, but it’s an inescapable part of the trade off.


Doesn't have to be, it's not very popular these days but we used to talked about progressive enhancement, the idea being you should prerender your thing in HTML and spice it up with JS, this is also covered in the POC and is unfortunatly something that got lost over time in favor of "modern framework" and the new meta everything fad


Progressive enhancement is, by definition, not a SPA.


The inline-in-html trick looks very nice indeed! We have a low-ish amount of different svg icons (as JSX) used in many places (potentially 1000s). This should help reduce the DOM size quite significantly at no downside (that I can see). Thanks!


Is this another ‘optimize away this extra 5ms step on the worst phone in existence’ article? I need some rough numbers before I’ll pay any attention to something like this.


I don’t think there’s anything wrong in pointing out that X is a better practise than Y, even if only by a small amount. As the tweet in the article points out, it’s really easy to multiply that tiny benefit by 1000 icons before you know what you’re doing. Why not start off on the best foot?

I don’t want to sound too negative (but I’m about to): this attitude in web development really bugs me. Shaving 5ms off render time on a crappy Android device should be a desirable goal, not something you have to be cajoled into doing. You should be motivated by that! But instead the industry is all about “DX”: is that change going to complicate my precious build script? Then out the window it goes.

We have an industry that prioritises developer experience over user experience and I think that has a role to play in the shittiness of the web these days.


That’s not really my point. All of these icons will cost you 5ms at worst. Should we make that tradeoff in the first place? The article present this as something you must do, or else you don’t care about your users. But as someone else points out elsewhere, including the icons in your code means no lazy loading, so everything is either instantly there or not at all. That’s a much bigger user benefit than a minimally faster bundle load.

I just don’t see a convincing (concrete) argument in this article.


> But as someone else points out elsewhere, including the icons in your code means no lazy loading

And if you check the replies you’ll see examples of other, more efficient ways to do the exact same thing.

It speaks to a bad level of myopia in the React community that when presented with the challenge of “put statically rendered markup in HTML” the accepted solution is “take that markup, transform it into a bloated JS representation, transform it back to markup during server side render while also shipping the bloated representation in the client JS, then unnecessarily hydrate that representation on page load” and no one sees any problem.


... getting the job done is usually the first priority. wasted CPU cycles are comparatively cheap. just as experienced devs, who can write more optimized code are expensive.

of course if what the HTMX cult is chanting is true, then the best way to get the job done and save cycles is to skip as much of the frontend dev stuff as possible. (though this would probably lead to the problem of how to hire a dev versed in the occult.)


You are making things harder than they are. I ship the representation to the client once (as a JS string) and then render it there as many times as I want. No SSR involved.


> Shaving 5ms off render time on a crappy Android device should be a desirable goal, not something you have to be cajoled into doing. You should be motivated by that!

Here’s the thing: I would love to worry about 5ms optimizations, but my time is limited and there’s hundreds of 100ms optimizations I can do first. Those haven’t got done because, again, my time is limited and we had plenty of infinite-ms optimizations to do first by making various things possible to do at all.


Yeah there is a suspicious lack of performance numbers in the article


No mention of fragment identifiers and the <view> tag for SVG sprites? Simplifies the markup quite a bit.

https://developer.mozilla.org/en-US/docs/Web/SVG/Element/vie...


CSS-in-JS is a far more pervasive issue IMO. SVG-in-JS has a definite performance cost, but it's not an anti-pattern.


We do this at my job. Do you have any good articles that describe clearly why this is bad?

On the surface I assume it’s because you need to load all the js, create the dom, and then apply css in that order instead of just having a separate file for css loaded quickly at the beginning and letting the dom load in after.

But is there a benefit to css in js where only the css used in the components your rendering is loaded? Like an automatic code splitting for css?


Using the svg as mask image in css is presented to have the disadvantage of two separate requests (one for css and the other for the svg content). That can be overcome by inlining the svg directly in the css file.


If people are doing that a lot, why not optimize browsers to handle this better?


I don't think browsers should optimise for whatever the react cult happens to like this year because, as history has shown, they preach one thing now, then change their opinion months or years later




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: