svelte
svelte copied to clipboard
[feature req]: awareness of tags in `<head>` to prevent duping
Describe the problem
Say I have some component which adds elements to <svelte:head>
. It doesn't matter what those elements are, just that they're either generated from export let
inputs or are entirely static.
<script>
export let title;
</script>
<svelte:head>
<title>{title}</title>
<meta name="description" content="Static">
<svelte:head/>
Let's call this element LowEffortSEO.svelte
and use it as such;
<script>
import LoSEO from "./LowEffortSEO.svelte";
</script>
<LoSEO title="My Title" />
<LoSEO title="My Title" />
<LoSEO title="Changed My Mind" />
This will generate both of the titles and both of the meta tags;
<head>
<title>My Title</title>
<meta name="description" content="Static">
<title>My Title</title>
<meta name="description" content="Static">
<title>Changed My Mind</title>
<meta name="description" content="Static">
<head/>
Ignoring this stupid practice for the sake of demonstration, the content of <head>
contains duplicates; twice <title>My Title</title>
, and thrice <meta name="description" content="Static">
.
If we're prerendering, this is excess data we're sending to the client which is entirely functionless, if we're dynamically injecting this at runtime, we're telling the browser "hey something new just popped up and you need to deal with it", only for the resulting effect to be the same.
I'm currently building out some components. I can't make any assumptions about the DOM they're in, only that they must work when there's anything there or nothing there. This means that if I depend on something external to the site, say an icon lib or a font, I have to do it within a component. It's pretty nice on the consumer of components if things "just work" without any additional setup. There's no bug hunting, less documentation to go through, and they can get straight to the point of building their website without having to deal with adjacent tasks caused by the components.
Describe the proposed solution
Currently, if you want to do this you need to maintain your own internal graph of components and head elements, abusing stores and actions and getting fairly messy.
The solution I'm thinking of involves some sort of middle man between calls in <svelte:head>
and calls to the DOM.
This middle man would have several challenges to overcome. Taking the example from earlier, what happens when we bind the title
of the second LoSEO
to a user input? How do we make sure that we maintain the resulting <title>
tag for the first LoSEO
when the second is telling us "hey, this has changed and you need to replace it"?
Working with literal data is a bit easier, but also a lot less common. If you're the author of both your site and your components, then why put literal <svelte:head>
data in anything that would appear more than once at a time?
This middle man would have to maintain some dependency structure, possible a key-value store where each key is the element's data (excluding svelte-generated classnames) and each value is a set of component instance references who require this element to exist. Once that set is empty, the element should be destroyed. Component instances are removed from the set when an update causes the desired element to be different from the keyed element, or when the component is outright destroyed.
This would require a total separation between components and their <svelte:head>
, so calls such as append(document.head, meta0);
would be replaced by something like sveltehead.append(meta0)
, which would run through handling the graph, then run the appropriate append
call if necessary.
Alternatives considered
Svelte users should handle this themselves
This leaves us where we are, anyone wanting to do this has to implement pretty much all of this feature request, and then stop using <svelte:head>
if they can't figure out the action magic for it.
Only do this for non-dynamic elements
I'd be okay with this, although by implementing that you're either already most of the way to the full solution, or you've made compiler output more complicated.
Don't handle this, it's fine
It's not fine. Duplicate elements can be a hit to performance, especially on slow connections or slow devices, even in good conditions you can reach a point where excess HTML is visibly slowing your application.
Here's some mediocre code to demonstrate what the proposed sveltehead
may do. I've made notable errors such as being incredibly slow with the __cleanup()
function, I don't expect the code in this gist to ever see the light of day but it serves as an example of how this feature might work. At the very least is a messy and rough prototype to refine and improve for a real implementation.
Importance
nice to have
Another use case is loading 3rd party styles for a component on demand. Something like
<svelte:head>
<link
rel="stylesheet"
href="https://...stylesheet.css"
crossorigin="anonymous"
/>
</svelte:head>
will duplicate the link
element in the head when you use your component multiple times on a page.
And the network panel says the stylesheet is loaded twice as opposed to when the link
is in app.html
@fcrozatier this was something that I ran into while I was experimenting with duplicated <head>
elements, I had originally wrote out a section on ad hoc stylesheet loading, but decided to discard it as it was just reiterating the SEO stuff (which is a bit easier to follow along with than what I had for <link />
).
I did make a note about link elements, but I'm glad you highlighted it as it's a bit tucked away in the original post.
[...context etc] This means that if I depend on something external to the site, say an icon lib or a font, I have to do it within a component.