Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: <head> bubbling #221

Open
lxsmnsyc opened this issue Mar 21, 2023 · 4 comments
Open

Feature: <head> bubbling #221

lxsmnsyc opened this issue Mar 21, 2023 · 4 comments

Comments

@lxsmnsyc
Copy link
Collaborator

lxsmnsyc commented Mar 21, 2023

  • Allows <head> to be declared across components but be rendered in a single declaration, essentially "bubbling" to a single element that is by rendering <head> to the <html> opening (or appended as a prefix) when the sync output is done.
  • <head>'s content is deferred on the server to allow bubbling to occur.
  • <head> is also now isomorphic, which means it can work on the client. The mechanism I'm thinking is by utilizing hydration key ranges (like what we do with components and fragments currently). This should be more efficient than what we are doing for https://github.com/solidjs/solid-meta
  • Deprecates meta libraries since the mechanism is now already built-in.
  • Potentially add head types (e.g. <head type="pre"> to allow users to decide where to insert the <head>'s content.
// server
function ssrHead(props: HeadProps): void;

// client
function insertHead(props: HeadProps): void;

Extra:

  • Should this allow modifying head's attributes? (in the server it's a no-brainer, but I'm thinking about the client)
@ryansolid
Copy link
Owner

I'm super interested in what you are thinking here. If this is viable, this is pretty high priority to me.

@lxsmnsyc
Copy link
Collaborator Author

I think this is 100% viable, we just need to know some edge cases first that may potentially break this. For instance, I'm thinking right now what would happen if a <head> suspends

@ryansolid
Copy link
Owner

Maybe I can help you think through that. Can you describe what the mechanism is, and how it differs from what we are doing with @solidjs/meta. We've talked a bit in Discord but I think it would be great to have that here.

@lxsmnsyc
Copy link
Collaborator Author

lxsmnsyc commented Mar 28, 2023

The behavior I'm thinking right now is that <head> renders much like <Assets> that its children only evaluates after the sync render. All <head> declarations are then merged into one to form a single <head>. The resulting <head> is then appended after the <html>'s opening tag (if it exists) or prefixed in the SSR markup.

@solidjs/meta

Currently, what we do with @solidjs/meta is that we use special meta-related components (i.e. Title), attach a special kind of key (data-sm), push them to a meta array, then in hydration, we match the elements with their respective hydration keys. I think this is okay, although this doesn't scale very well when there's a lot of meta tags to render, specially when each Meta component is accompanied with a data-sm and data-hk.

For <head>, the ideal solution is instead to use hydration key ranges (like what we do for components/fragments) wherein we just look up the matching opening and closing hydration comments. This way, there's only one key for every <head> and it's not part of each element, at least with that thing we don't have to worry much about hydration matching.

Comparison (@solidjs/meta vs new <head>):

<title data-sm="<HYDRATION KEY>" data-hk="<HYDRATION KEY>">Hello World</title>
<meta data-sm="<HYDRATION KEY>" data-hk="<HYDRATION KEY>" name="description" content="This is a description">
<!--<HYDRATION KEY>-->
<title>Hello World</title>
<meta name="description" content="This is a description">
<!--/<HYDRATION KEY>-->

On client-side, we can utilize insert to append <head>'s children after the comment anchors

Differences with <Assets>

My biggest issue with <Assets> is its reliance on existing <head>, on top of that, it's no-op if <head> doesn't exist (related #202)

When <head> is used, it guarantees that the resulting markup will have a <head> element, but maybe the problem here is what if it's unideal add <head>. (I'll mention it in the challenges section below).

The new <head> also has type="pre"|"post" to allow users to decide where to inject the children, something useAssets/<Assets> lacks.

Challenges

Suspense and suspending <head>

This might be tricky for the following code:

<Suspense>
  <head>
    <title{titleData()}</title>
  </head>
</Suspense>

in this case, titleData is a Resource data. The obstacle here is whether or not it is possible to bubble from the deferred children to its original Suspense ancestor.

Is <head> worth replacing?

Initially I was thinking if <head> should be replaced or should we just introduce <Head> or <<rxcore>:head> instead, so that the current <head>'s behavior stays the same while <Head> can do the bubbling behavior

What happens if the SSR markup isn't actually a document

This is another challenge. Suppose to say that the target output is for islands, how would we bubble up outside the markup? With @solidjs/meta it's certainly possible since we have the meta array.

Edit:
Based on what I've tested, a <head> inside another <head> or other elements (like <body>) will be removed but its children will be moved to its ancestor. This is a browser correction process so I guess this challenge is a minor issue.

Note
🚧 This message is going to be edited over time 🚧

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants