Replies: 4 comments 8 replies
-
If I understood correctly, this aligns with my proposal here regarding meta tags: solidjs/solid-meta#39 But, it would be built into solid, and libraries can customize the deduping behavior by using This seems like a great approach to me. For many people (including myself), the core behavior would be more than enough if we don't mind setting explicit keys. Then, Solid Meta & co. can get smart and do more "out of the box" and opinionated deduping. And it's also quite easy for devs to create their own heuristics by wrapping It's a great win because it abstracts the hard part (making deduping work in all contexts, client/server/etc) while providing enough freedom to users to implement advanced heuristics with a good enough default. Nice work Ryan! |
Beta Was this translation helpful? Give feedback.
-
Wow, this is a great writeup and research into the different approaches. Just to explain the React approach here, as design principle, we try to avoid forcing a common abstraction on users that has unresolved pitfalls like the ones you mention. That's why React supports deduping for link, script, and style tags in cases where there are no pitfalls, but not for title. Instead, we encourage libraries and frameworks to choose their own tradeoffs, and users can choose different libraries with different tradeoffs. That's why the two React frameworks listed have support for deduping the title, but in different ways which suit them, and React doesn't force them into doing it one way with unresolved pitfalls. That's a choice we make and live with, but for Solid or other libraries you may have different design principles that work better for your users. I don't think one way is better, it's just a different set of tradeoffs. |
Beta Was this translation helpful? Give feedback.
-
Do you think it’d be possible to use the mentioned https://github.com/unjs/unhead library as base for the use head composable or is there anything speaking against it? |
Beta Was this translation helpful? Give feedback.
-
This is looking very promising 🥰! One question about
|
Beta Was this translation helpful? Give feedback.
-
Summary
In this RFC I'm looking for feedback on the potential of a core handling of head tags. Things like
<title>
,<metadata>
,<link>
,<script>
and<style>
. This proposal aims to be not only a useful feature for core but to make the development of 3rd party libraries easier when handling things like streaming SSR.Background
Current Concerns
I admit this is an area (like CSS) where I haven't wanted to touch with a 10 foot pole because no one seems to agree and everyone seems to get really opinionated and passionate about this stuff. However, I haven't been happy with how Solid's
useAssets
primitive works for head tags for a couple of reasons.The 3rd point is especially annoying. If someone tries to add a head tag lower down in a Suspense boundary and we've already flushed it's when that boundary hydrates in the client that we finally see the update. But it is more than that. If you write code to "correct" on hydration then latency delays can cause work to be undone and redone. For example if you have code splitting the server might know that the final title is the one inside the lazy component not the one outside. But if there is a delay loading that lazy code when shell runs first it might revert the title momentarily before applying the correct title again.
So while one can take a position that all metadata should be resolved before flushing it also means always blocking which I don't think is a good place to be. That isn't always a reasonable request. You could have to wait on the longest to load piece of data before showing the user anything. You could argue that deduping is the problem but showing some less descriptive title before the fully formed one is probably still beneficial.
Research
I did research on the head solutions used in React 19, Svelte, Next, Nuxt 2, Nuxt 3(unhead), Remix, React Helmet (Async), to understand how these libraries work and realized that few of them supported streaming properly. Solid Meta's approach while less than ideal is still adequate in this field.
The biggest point of contention is the approach to deduping. While this is not conclusive I hope it gives you an idea of how these solutions perspectives are different.
svelte:unique
Analysis
Let's start with a summary of results from my perspective. Every library supports deduping
title
except React 19. Grouped solutions (mostly route section based) are more comfortable deduping metadata and don't need explicit keys to do so. This is likely because they use a layered approach. So uniqueness at the same level doesn't conflict and can be replaced at a lower level. React Helmet has this characteristic too which is why it doesn't use explicit keys. You can have multiple open graph meta tags with the same name on the same level even if it overrides with other head entries.React 19 is really the anomaly of this bunch and probably requires further exploration to understand the intent. Every library has some means to dedupe what you would want to except it. One thing to consider is that all deduping strategies that aren't purely hierarchical are fundamentally flawed. This means that while Router based solutions can provide good guarentees others cannot at all. All it takes is something being rendered anywhere in the tree and it wins. Order mattering on rendering is the least predictable approach possible and when dealing with singletons very hard to track. Arguably even harder than saying there should only be one on the page and not following that perfectly.
That being said in the past decade I've rarely heard anyone complain about this feature for things like title, yet demand a solution when one doesn't exist for it. So while flawed it is basically universally accepted. Is that sufficient criteria here for inclusion?
Proposal
So there are a lot of ways to take this. But in order to play nicely with streaming the way we'd want it needs to be core. Userland short of intercept the streamed chunks has no good tools here. But assuming someone wants to be able to dedupe it means we need to have the means built in otherwise this won't all work.
So for this I suggest a 2 prong approach. One that appeals to the general developer and one that appeals to library authors. This approach is heavily inspired by React 19 and along with our current experience with Solid Meta.
useHead
We introduce a new core primitive into
solid-js/web
to handle head management of tags. It will take a single tag description and manage it in the head both for client and server.This API optionally can be given a key which will be used to dedupe entries, last one wins. The
key
property can be a getter so it can be made reactive and change so it is possible to do some pretty complicated updates.Mechanically this will register all tags in a list that can be updated and deduped as changes occur keeping the head in sync in the client. During SSR it will collect the same way and at first flush render these tags into the head (in order link/style, meta, others, script). Subsequent flushes will diff the flushed value against the new changes and stream updates to the client from the server. On hydration we will collect the tags but not render or update anything. Only subsequent client changes will update the DOM.
If streaming is cancelled (like someone navigates the page) we will bail out of future server updates.
useHead
is intended to be used internally in things like Solid Meta etc or other libraries so they can come up with their own keys and stricter deduping logic and own custom interfaces for more control. This gives a lot of power and full access to streamable APIs without digging into internals.JSX tags
Certain JSX tags will automatically be turned into
useHead
calls. This only works with native tags and not with<Dynamic>
components and elements that don't have their defining attributes for inclusion inside a spread since the mechanism is based on compilation not runtime.<title>
- This tag will be added to the head and given a special key to ensure only one ends up in the document. The exception is if it is found under known SVG elements as<title>
is a legitimate svg element or elements withitemprop
attribute. Don't overuse this with siblings and keep it top level in your route sections preferably to avoid inconsistent behavior.<meta>
- This tag will be added to the head except for when it hasitemprop
attribute. No deduping.<link>
- This tag will be added to the head except for when it hasitemprop
attribute. Links with the samehref
will be deduped.<style>
- This tag will be added to the head when it has ahref
attribute and noitemprop
attribute. Styles with the samehref
will be deduped.<script>
- This tag will be added to the head when it has asrc
attribute andasync
attribute. Scripts with the samesrc
attribute will be deduped.Possible Additions
precedence
or equivalent.Some means to prioritize other than document order. Usually render order is sufficient because of the hierarchical nature of things. But this could be worth exploring.
This seems like a powerful feature if used properly.
Impact
Other than the JSX transform aspect this has minimal impact as it doesn't impact existing code and is completely treeshakable behavior in the client. I've attempted to keep the implementation minimal and it looks to be even smaller than current Solid meta so I'm thinking ballpark of 500 bytes minified and gzipped in the client.
I'm proposing that this be turned on by default in the next minor with the ability to turn it off via compiler option as to allow codebases that need to be updated or can't support this feature to not be impacted. Since meta data is intrinsically part of your application I don't expect this to impact many 3rd party libraries.
As always appreciate feedback very much.
Beta Was this translation helpful? Give feedback.
All reactions