You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, react-head utilizes data-rh attributes in order to manage the tags that it creates. By default, react-head avoids touching tags that it doesn't know about (that don't have data-rh attributes). This is so that static tags that are part of a static HTML template are not touched.
There's been a few issues (both in this project and react-helmet) from folks who are interested in:
While these two items are certainly separate, they are closely related and we might be able to kill two birds with one stone depending on our approach.
Potential Solutions
Option 1: Manually created whitelist definition
One solution, as proposed in #84 is to define an explicit whitelist prop:
/* on server */constheadTags=[];<HeadProviderheadTags={headTags}whitelist={['title','[name="description"]','[property^="og:"]'}><App/></HeadProvider>/* on client */<HeadProviderwhitelist={['title','[name="description"]','[property^="og:"]'}><divid="app"><Title>Title of page</Title>
// ...
</div></HeadProvider>
This solution has the following benefits:
Easily implementable
Completely opt-in
While this works, it has the following drawbacks:
Creating that whitelist prop isn't straight-forward (you have to be familiar with uncommon CSS selectors, [foo^=bar])
You have to provide this same selector set to the server and the client (more manual overhead)
It doesn't explicitly solve for Problem 2 stated above (although it could depending on implementation).
Implementation Details
The implementation can be explored in the WIP PR by @CanRau
Option 2: Auto-Whitelist
Expanding on Option 1, we could auto-generate the necessary "selector set" as you go (opt-in to this behavior with autoWhitelist prop on HeadProvider):
/* server */constheadTags=[];<HeadProviderheadTags={headTags}autoWhitelist><App>
// ... within any component, head tags are rendered as normal
<Metaname="blah-long-name"content="lots of content here"/><Title>Title of page</Title></App></HeadProvider>/* on client */<HeadProvider><App/></HeadProvider>
Note that autoWhitelist prop isn't very expressive/meaningful without context. Maybe we can come up with a better name? raw? clean? Any other ideas?
Implementation Details
On the server, as we render head tags, if any of them include a whitelist prop, we build up the selector set necessary to identify which tags were inserted by react-head (basically the selector set that was manually created in Option 1 above). We return this as a renderable DOM element as part of the headTags[] array with some unique ID that we grab from the DOM and provide as context during client rendering:
constheadTags=[// normal head tags'<meta name="blah" content="foo" />','<title>Title of page</title>',// new: the generated whitelist selector set, to be injected during SSR// we'd basically JSON.parse() this content on the client and use// as part of the querySelectorAll: `<script id="react-head-whitelist" type="text/template"> [ 'meta[name="blah-long-name"][content="lots of content here"]', 'title' ] </script> `]
CONs:
Sort of bloats the server rendered payload (we'd basically duplicate all tags twice). There's some optimizations we could do here, e.g. select just the first few characters of each attribute instead of the full thing: meta[name^="blah"][content^="lots"]
PROs:
No additional setup required (as @CanRausuggested, the API doesn't need to change much!)
Completely opt-in so it's non-breaking (current behavior would be the default). Maybe we can flip this with a future major release.
Depending on how we generate the whitelist selector set, we could also solve for Problem 2 (e.g. in the above example the selector for the title tag is just title, instead of also including the full text content). Alternatively we could force opt-in to mangling by requiring an additional specific prop, <title mangle>Title of page</title> but not crazy about that idea.
Option 3: Individually opt-in to whitelist
As a tweak to Option 2, we could individually require opt-in, if we wanted to support whitelisting only individual elements, we could support an explicit whitelist prop on each head tag instead of an autoWhitelist prop on the HeadProvider.
<titlewhitelist>Title of Page</title>
In this scenario you'd have a mix of data-rh and #react-head-whitelist selectors.
Not sure how popular this option would be and it complicates the implementation slightly so I'd almost rather have it be all or nothing. Thoughts?
PRO:
Opt-in
Selective SSR bloat vs all
CON:
Complicates implementation
Potentially confuses the mental model for users
Option 4: The Nuclear Option
The above assume that folks don't want react-head to manage tags that it hasn't explicitly rendered. There is a "nuclear" option of allowing folks to opt-in to having react-head mangle all tags:
/* server */constheadTags=[];<HeadProviderheadTags={headTags}manageAllTags><App>
// ... within any component, head tags are rendered as normal
<Metaname="blah-long-name"content="lots of content here"/><Title>Title of page</Title></App></HeadProvider>/* on client */<HeadProvidermanageAllTags><App/></HeadProvider>
If manageAllTags is set, we can avoid rendering data-rh attributes and just have a very greedy selector on the client, something like: head > title, head > meta ... which would select all tags in <head /> whether or not they were created by react-head. This would work but would require that users express all their tags in the React tree and not just rely on static HTML templates. This is okay if we make this opt-in and explain thoroughly in the documentation.
PROs:
Simple to implement
Simple mental model
No SSR payload bloat
CONs:
All or nothing
May be surprising for folks who don't read the docs :)
Problem
Currently, react-head utilizes
data-rhattributes in order to manage the tags that it creates. By default, react-head avoids touching tags that it doesn't know about (that don't havedata-rhattributes). This is so that static tags that are part of a static HTML template are not touched.There's been a few issues (both in this project and react-helmet) from folks who are interested in:
While these two items are certainly separate, they are closely related and we might be able to kill two birds with one stone depending on our approach.
Potential Solutions
Option 1: Manually created whitelist definition
One solution, as proposed in #84 is to define an explicit whitelist prop:
This solution has the following benefits:
While this works, it has the following drawbacks:
[foo^=bar])Implementation Details
The implementation can be explored in the WIP PR by @CanRau
Option 2: Auto-Whitelist
Expanding on Option 1, we could auto-generate the necessary "selector set" as you go (opt-in to this behavior with
autoWhitelistprop onHeadProvider):Implementation Details
On the server, as we render head tags, if any of them include a
whitelistprop, we build up the selector set necessary to identify which tags were inserted by react-head (basically the selector set that was manually created in Option 1 above). We return this as a renderable DOM element as part of theheadTags[]array with some unique ID that we grab from the DOM and provide as context during client rendering:CONs:
meta[name^="blah"][content^="lots"]PROs:
title, instead of also including the full text content). Alternatively we could force opt-in to mangling by requiring an additional specific prop,<title mangle>Title of page</title>but not crazy about that idea.Option 3: Individually opt-in to whitelist
As a tweak to Option 2, we could individually require opt-in, if we wanted to support whitelisting only individual elements, we could support an explicit
whitelistprop on each head tag instead of anautoWhitelistprop on theHeadProvider.In this scenario you'd have a mix of
data-rhand#react-head-whitelistselectors.Not sure how popular this option would be and it complicates the implementation slightly so I'd almost rather have it be all or nothing. Thoughts?
PRO:
CON:
Option 4: The Nuclear Option
The above assume that folks don't want react-head to manage tags that it hasn't explicitly rendered. There is a "nuclear" option of allowing folks to opt-in to having react-head mangle all tags:
If
manageAllTagsis set, we can avoid renderingdata-rhattributes and just have a very greedy selector on the client, something like:head > title, head > meta... which would select all tags in<head />whether or not they were created by react-head. This would work but would require that users express all their tags in the React tree and not just rely on static HTML templates. This is okay if we make this opt-in and explain thoroughly in the documentation.PROs:
CONs: