react-helmet
react-helmet copied to clipboard
False positive "You may be attempting to nest <Helmet>..." warning
I have a translation component, something in the lines of this:
const T = ({ translate, children, component = 'span', ...props }) =>
React.createElement(component, omit(props, ['translations', 'language', 'dispatch']), translate(children))
and since I cannot use this with Helmet directly:
<Helmet>
<title><T>some.translation.string</T></title>
</Helmet>
I'm using it like this:
<Helmet>
<T component="title">some.translation.string</T>
</Helmet>
It works but I get a warning in the console which I believe is not right:
You may be attempting to nest <Helmet> components within each other, which is not allowed. Refer to our API for more information.
I think this is the culprit: https://github.com/nfl/react-helmet/blob/6a3d3bf2a4ee6db7045ef27ea2bb1aee906146e3/src/Helmet.js#L186
While I second that this is a bug (I presume it is, since I had the same issue with https://github.com/lingui/js-lingui), you might get away with extracting the "translation" outside helmet for the time being.
Example from my project:
export default class PageContent extends React.Component {
render() {
const HeadModifier = withI18n()(({i18n}) => {
const translatedTitle = i18n._(this.props.title);
return (
<Helmet>
<title>My awesome project - {translatedTitle}</title>
</Helmet>
)
})
return (
<ContentBox>
<HeadModifier/>
{this.props.children}
</ContentBox>
)
}
}
PageContent.propTypes = {
title: PropTypes.string.isRequired
};
And then use it like...
<PageContent title="some.translation.string"/>
Same issue, when I use a component to render title, description
I'm interested to know why nesting of React components in Helmet is not supported. Is it because, with the current implementation, it would not be possible to notify Helmet when nested components are updated and complexity would have to increase?
Any updates on this?
I think this would be useful, one use case would be loading favicons from their own component.
Say you are supporting 14 different favicons to target a wide range of browsers & devices (see here) it would be cleaner to have them all wrapped up in their own component. (This would make it easy to reuse in other projects too.)
import React from 'react'
import appleIcon57x57 from '../../../static/favicons/apple-icon-57x57.png'
import appleIcon60x60 from '../../../static/favicons/apple-icon-60x60.png'
import appleIcon72x72 from '../../../static/favicons/apple-icon-72x72.png'
import appleIcon76x76 from '../../../static/favicons/apple-icon-76x76.png'
import appleIcon114x114 from '../../../static/favicons/apple-icon-114x114.png'
import appleIcon120x120 from '../../../static/favicons/apple-icon-120x120.png'
import appleIcon144x144 from '../../../static/favicons/apple-icon-144x144.png'
import appleIcon152x152 from '../../../static/favicons/apple-icon-152x152.png'
import appleIcon180x180 from '../../../static/favicons/apple-icon-180x180.png'
import androidIcon192x192 from '../../../static/favicons/android-icon-192x192.png'
import favicon32x32 from '../../../static/favicons/favicon-32x32.png'
import favicon96x96 from '../../../static/favicons/favicon-96x96.png'
import favicon16x16 from '../../../static/favicons/favicon-16x16.png'
import msIcon144x144 from '../../../static/favicons/ms-icon-144x144.png'
const icons = [
{
rel: 'apple-touch-icon',
sizes: '57x57',
href: appleIcon57x57,
},
{
rel: 'apple-touch-icon',
sizes: '60x60',
href: appleIcon60x60,
},
{
rel: 'apple-touch-icon',
sizes: '72x72',
href: appleIcon72x72,
},
{
rel: 'apple-touch-icon',
sizes: '76x76',
href: appleIcon76x76,
},
{
rel: 'apple-touch-icon',
sizes: '114x114',
href: appleIcon114x114,
},
{
rel: 'apple-touch-icon',
sizes: '120x120',
href: appleIcon120x120,
},
{
rel: 'apple-touch-icon',
sizes: '144x144',
href: appleIcon144x144,
},
{
rel: 'apple-touch-icon',
sizes: '152x152',
href: appleIcon152x152,
},
{
rel: 'apple-touch-icon',
sizes: '180x180',
href: appleIcon180x180,
},
{
rel: 'icon',
type: 'image/png',
sizes: '192x192',
href: androidIcon192x192,
},
{
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: favicon32x32,
},
{
rel: 'icon',
type: 'image/png',
sizes: '96x96',
href: favicon96x96,
},
{
rel: 'icon',
type: 'image/png',
sizes: '16x16',
href: favicon16x16,
},
]
const Favicons = props => (
<React.Fragment>
{icons.map((icon, id) => <link key={`icon-${id}`} {...icon} />)}
<meta name="msapplication-TileColor" content="#ffffff" />
<meta name="msapplication-TileImage" content={msIcon144x144} />
<meta name="theme-color" content="#ffffff" />
</React.Fragment>
)
export default Favicons
Thinking about it, I second @Kernle32DLL . A decent enough work around is to return your head components (Translated title, Favicons etc) wrapped in a <Helmet>
tag.
Kind of makes sense because the components intended to render in the head, I would guess would not be intended to be rendered elsewhere, so it is safer to return them wrapped in the <Helmet>
?
const Favicons = props => (
<Helmet>
<link rel="apple-touch-icon" sizes="152x152" href="../static/favicons/apple-icon-152x152.png" />
<link rel="icon" type="image/png" sizes="16x16" href="../static/favicons/favicon-16x16.png" />
</Helmet>
)
<Helmet>
<meta />
{/* more initial head stuff */}
</Helmet>
<Favicons />
{/* ^^ This will render in the head anyway because it returns wrapped in <Helmet> */}
So why this isn't being implemented? It's just ridiculous to expect all components to be rendered as direct children of ReactHelmet. It breaks composability and forces you to wrap everything in ReactHelmet (if even possible).
I am trying to implement a generic SEO component as a separate library. It would be nice to have it working without making react-helmet a mandatory dependency. I can't just import the component and render it inside ReactHelmet, nope have to wrap it inside the component (in this case completely separate library) which then has an implicit peerDependency on react-helmet.
Please fix this issue, I don't think I can hot-fix this for my own use-case. Have to invent some trick if I want to get it working.
@TeemuKoivisto this have not to be implemented to use Helmet like you want. Just use function as child pattern:
<T component="title" key="some.translation.string">
{(translated) => <Helmet>{translated}</Helmet>}
</T>
In this case T can be library component (without Helmet dependency).
Is there any reason why elements must not be nested? we got hit by this too. Looks strange?
Getting this as well. Is there any plan to fix this?