nfl / react-helmet

A document head manager for React
MIT License
17.34k stars 660 forks source link

React-Helmet: doesn't add and update html attributes #625

Closed 01Kuzma closed 3 years ago

01Kuzma commented 3 years ago

Having troubles with ReactJs Helmet 6.0.
The app has a language switch option and some other pages.
Once the page or language is changing - the new metadata is rendering.
The description, keywords and title are updated, but nothing is happening to all tags inside htmlAttributes.
So schema or html lang tags are not added to the DOM.

The index.html is simple straightforward:

<!doctype html>
<html lang="en"> <!-- nothing is happening if I remove it -->
<head>  
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="author" content="author">
    <link rel="icon" href="/img/favicon.png">

</head>
  <body>   
    <div id="app"></div>
  </body>
</html>

index.js

class App extends React.Component {

  componentWillMount() {       
    var metaTitle=[];
    var desc=[];
    var kewd=[];

    if (i18n.language=='en') {   
      this.metaTitle = "En title";      
      this.desc = 'En desc';    
      this.kewd = 'En keywords';
    } else {
      this.metaTitle = "other lang title";      
      this.desc = 'other lang desc';    
      this.kewd = 'other lang keywords';
    }    

  }    

  render() {       

    var lang = i18n.language;    

    return (      
      <I18nextProvider i18n={i18n}>
        <div>               
          <SEO
            schema="Product"
            title={this.metaTitle}
            lang={i18n.language}
            keywords={this.kewd}
            description={this.desc}            
            base="base.com"
            path={`/${lang}`}
            contentType="product"
          />   
          <Router history={customHistory}>
            <Switch>
              <Route paths .../>
            </Switch>
          </Router>
        </div>
      </I18nextProvider>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('app'));

SEO.js

import { Helmet } from 'react-helmet';

  class SEO extends React.Component {

    render() {      

      const { schema, lang, title, description, keywords, base, path, contentType } = this.props;
      const meta = [
                    {name: 'author', content: 'author'.toString() },
                    {name: 'description', content: description.toString()},
                    {name: 'keywords', content: keywords.toString()},
                    {property: 'og:title', content: `${title}`.toString()},
                    {property: 'og:description', content: description},
      ];

      return (
              <Helmet
                  htmlAttributes={{
                    lang: i18n.language,
                    itemscope: undefined,
                    itemtype: `http://schema.org/${schema}`,                
                  }.toString()}
                  title={title}
                  meta={meta}
                  />
                  )
                }
              }
export default SEO;
kinafu commented 3 years ago

I beieve the .toString() call is the cause.

For the similar property bodyAttributes Helmet requires an object instead of a string:

<Helmet bodyAttributes={{ style: "margin: 0; padding: 0; border: 0;", class="body"}}>

using react-helmet 6.1.0

01Kuzma commented 3 years ago

Thank you for response. I've removed .toString() but this doesn't changed the situation. For example, the html lang attribute still doesn't change...

joshbedo commented 3 years ago

Any fix for this? Trying to use react-snap and react-helmet together. I noticed it's creating a duplicate meta description tag. react-snap gets added first so the react-helmet description is never showed.

EDIT: fixed just had to remove meta description from the index.html file in the public directory now I'm seeing just the react-helmet description and it gets written correctly in react-snap.

Also needed to add the react the data-react-helmet="true" to my description tag in public/index.html so it can find it and replace it correctly. Everything seems to be working though.

ivansvlv commented 3 years ago

@01Kuzma

Thank you for response. I've removed .toString() but this doesn't changed the situation. For example, the html lang attribute still doesn't change...

Have you removed this .toString()?

htmlAttributes={{
  lang: i18n.language,
  itemscope: undefined,
  itemtype: `http://schema.org/${schema}`,                
}.toString()}

Calling .toString() on an object literal will always return [object Object] string, unless .toString() is redefined in prototype.

01Kuzma commented 3 years ago

Have you removed this .toString()?

Yes, I did

ivansvlv commented 3 years ago

@01Kuzma few more suggestions: 1) attributes should have camelCase, refer to the link to React docs, and read the warning there - https://reactjs.org/docs/introducing-jsx.html#specifying-attributes-with-jsx Also, react-helmet, converts it to the proper names with this function: https://github.com/nfl/react-helmet/blob/master/src/HelmetUtils.js#L545

2) make sure that i18n.language has some value when you use it in htmlAttributes. I'm not quite sure why do you use i18n.language in that place, since you've provided lang as a prop to SEO component.

So I would try:

htmlAttributes={{
  lang: i18n.language,
  itemScope: undefined,
  itemType: `http://schema.org/${schema}`,                
}}

UPDATE: i'm actually not sure that itemScope or itemType would work, because I can't see the mapping in their constants: https://github.com/nfl/react-helmet/blob/master/src/HelmetConstants.js#L36

01Kuzma commented 3 years ago

Thank you @ivansvlv , I will try

kinafu commented 3 years ago

@01Kuzma Another idea: Maybe it seems like the lang attribute does not appear, because you are looking at the html source file (Ctrl + U in Firefox) instead of the react-generated DOM (using inspector in developer console).

01Kuzma commented 3 years ago

kinafu, yes, you are right! I just forgot to post this information earlier... Thanks to all of you! Closing this issue

dmitryshelomanov commented 2 years ago

Helmet lang setup for SSR 1) Add htmlAttributes={{ lang }} 2) In your html markup you need to extract html attrs via helmetCtx.htmlAttributes.toString()


const html = `
<html ${helmet?.htmlAttributes.toString()}>
  body
</html>
`

@01Kuzma