Giter VIP home page Giter VIP logo

react-head's Introduction

react-head npm Version bundlephobia PRs Welcome

Asynchronous SSR-ready Document Head management for React 16.3+

Motivation

This module allows you to define document.head tags anywhere in your component hierarchy. The motivations are similar to react-helmet in that you may only have the information for certain tags contextually deep in your component hiearchy. There are no dependencies (it does not use react-side-effects) and it should work fine with asynchronous rendering; the only requirement is React 16.3+.

Read more about react-head and how it works on Medium

Installation

npm i react-head

or

yarn add react-head

How it works

  1. You wrap your App with <HeadProvider />
  2. From the server, you pass headTags[] array to <HeadProvider />
  3. Then call renderToStaticMarkup(headTags) and include in the <head /> block of your server template
  4. To insert head tags within your app, just render one of <Title />, <Meta />, <Style />, <Link />, and <Base /> components as often as needed.

On the server, the tags are collected in the headTags[] array, and then on the client the server-generated tags are removed in favor of the client-rendered tags so that SPAs still work as expected (e.g. in cases where subsequent page loads need to change the head tags).

You can view a fully working sample app in the /example folder.

Server setup

Wrap your app with <HeadProvider /> on the server, using a headTags[] array to pass down as part of your server-rendered payload. When rendered, the component mutates this array to contain the tags.

import * as React from 'react';
import { renderToString } from 'react-dom/server';
import { HeadProvider } from 'react-head';
import App from './App';

// ... within the context of a request ...

const headTags = []; // mutated during render so you can include in server-rendered template later
const app = renderToString(
  <HeadProvider headTags={headTags}>
    <App />
  </HeadProvider>
);

res.send(`
  <!doctype html>
    <head>
      ${renderToString(headTags)}
    </head>
    <body>
      <div id="root">${app}</div>
    </body>
  </html>
`);

Client setup

There is nothing special required on the client, just render one of head tag components whenever you want to inject a tag in the <head />.

import * as React from 'react';
import { HeadProvider, Title, Link, Meta } from 'react-head';

const App = () => (
  <HeadProvider>
    <div className="Home">
      <Title>Title of page</Title>
      <Link rel="canonical" href="http://jeremygayed.com/" />
      <Meta name="example" content="whatever" />
      // ...
    </div>
  </HeadProvider>
);

Contributing

Please follow the contributing docs

react-head's People

Contributors

alekrist avatar arkist avatar ashfurrow avatar dpeek avatar gjacobrobertson avatar jdtanacredi avatar marhaupe avatar msokk avatar praxxis avatar rdsedmundo avatar tizmagik avatar trysound avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

react-head's Issues

Create common aliases (title, style, meta, link)

Would be nice to create some aliases for convenience, so that instead of doing this:

import HeadTag from 'react-head';

<HeadTag tag="link" rel="canonical" content="http://jeremygayed.com/" />

You could do this:

import { Link } from 'react-head'; // maybe HeadLink better?

<Link rel="canonical" content="http://jeremygayed.com/" />

The common aliases should be:

  • Title
  • Style
  • Meta
  • Link

Technically base and script are also allowed, but I can't think of a use case where you don't already know those values server-side.

data-rh attribute

Hi, just stumbled upon your module, while trying to evaluate the use of React Portals for and as your plugin proofs it does indeed 👍
haven't had the time to test it out yet, but I was actually looking for an alternative to react-helmet because this nfl/react-helmet#79 kind of bugs me..

not sure how big of an impact this has, but wanted to try it out and didn't liked the idea of hacking the attribute away like mentioned in the comments..

any thoughts on this?

by the way, in my case I'm using GatsbyJs

Proposal: headTagsToElement(headTags)

Currently renderToString of headTags with parent elements gives Warning: Each child in an array or iterator should have a unique "key" prop..

const headHtml = renderToString(
  <head>
     {sheet.getStyleElement()}

     {headTags}
  </head>
)

So as a workaround I'm using React.createElement + React.Fragment.

const headHtml = renderToString(
  <head>
     {sheet.getStyleElement()}

     {React.createElement(React.Fragment, {}, ...headTags)}
  </head>
)

It would be good if react-head had it's own utility to handle this situation, e.g:

export const headTagsToElement = headTags =>
  React.createElement(React.Fragment, {}, ...headTags);

I would glad to add PR after merge of #69.

react-head supports meta update on lazy load

Can react-head be updated when i am on the some component and that component get more data after certain scroll (lazy load) to render then can i update the meta with total count of product?

Question: Does it work without ssr for SEO, on fb and twitter ?

Hi,
I can't have SSR in my current project as my backend is developed by someone else in PHP,
all I have is a server serving static Build of my App built over create-react-app.

It's a blog basically with all normal functionality like reading, creating of blog post and sharing the same
my issue is SEO I am unable to preview the dynamic headers and meta data on social site like facebook and other

please help.

Entities in computed attribute values? Suggested approach?

Wonderful package :) I've been using it for a year or so, and it's awesome.

Question on usage when rendering a computed attribute that contains an ampersand (&). React likes to escape that to &amp;... any suggested workarounds?

This is the use case:

<Meta
  property="og:image"
  content={imageUrl + '?height=630&width=1200'}
/>

The content attribute ends up looking like this when rendered with ReactDOM.renderToStaticMarkup:

content="https://example.com/image.jpg?height=630&amp;width=1200"

Thanks!

3.0 api

We have a couple of breaking changes and I need some brainstorming before the next release.

  • HeadProvider has necessary headTags prop which is not necessary for client-only apps so users should specify it as empty arrays
  • HeadProvider is used to solve cascading tags
  • HeadTag is able to work without HeadProvider; will always render tag
  • cascading handling requires unchanged tag prop in HeadTag

Questions

  1. Should we provide default prop for headTags with empty array?
  2. Should we enforce having HeadProvider in the tree by throwing an error if context value is null?
  3. Should we throw and error in componentDidUpdate if tag prop is changed or just remove HeadTag from public api and provide more aliases?

/cc @tizmagik

Tests

There's some test case descriptions already written out and checked in, but they need the actual tests written

Expose HeadTag?

I was hoping to create a base tag, and found it is missing. It should be added, but more generally the generic HeadTag should be exposed in the exports.

Scripts?

I noticed that there is no support for script tags. This is useful for e.g. conditionally including trackers in SSR.
React-helmet can do it, but unfortunately it loads the script twice on SSR if you also render it on the client.

Title component doesn't override default <title> tag provided via HTML template

I'm using client-side rendering and react-head is not able to replace a default tag that I provided on my HTML template. It only adds a new <title> tag at the end of the head, and due to the browser behavior, only the first one is actually used. react-helmet as a reference supported this fine.

template.html

<html>
  <head>
    <title>Test</title>
  </head>
  <body>
     <div id="react-mount"></div>
  </body>
</html>

index.js

import React from 'react';
import ReactDOM from 'react-dom';

import { Title, HeadProvider } from 'react-head';

ReactDOM.render(
  <div>
    <HeadProvider>
      <Title>Test2</Title>
    </HeadProvider>
  </div>
  document.getElementById('react-mount'),
)

The expected behavior for me is that Test2 is going to replace and be the title of the page, but what happens is that Test remains as the title, as its tag is positioned first.

Is this library still maintained?

I could imagine that this library is mostly complete and so it doesn't need further work, but do new features like React Server components need any special consideration? It's hard to tell.

Suggestion: use renderToStaticMarkup instead of renderToString

In the readme renderString is used to render the headTags array. This adds a data-reactroot attribute to every element. Since only data-rh is used to "hydrate" anyway, renderToStaticMarkup might be a better choice to save a few bytes and get cleaner markup.

Link canonical cascade

First off, love the lib. Would there be some merit to cascading a <link rel="canonical" ... /> tag? (link tags aren't cascaded and for good reason)

But this particular link tag has a uniqueness requirement akin to title and other meta tags,
Multiple canonicals are ignored (this is old, but looks to be still relevant)

Specify no more than one rel=canonical for a page. When more than one is specified, all rel=canonicals will be ignored.

To bring this to a question: right now canonical is required to be set on every page, but should this be handled as an edge case and cascaded, or maybe there something a little more interesting here where we can leverage the platform and give any link tag the ability to cascade given a unique identifier via an id or class (or other) attribute?

One last thing, the canonical tag in the readme is using a content attribute but I think you want an href attribute: <Link rel="canonical" content="http://jeremygayed.com/" /> to <Link rel="canonical" href="http://jeremygayed.com/" />

Thank you for you time

Children prop type missing in HeadProvider

Getting this TS error

image

Type '{ children: Element; }' has no properties in common with type 'IntrinsicAttributes & { headTags?: ReactElement<unknown, string | JSXElementConstructor>[] | undefined; }'.

Fix for this-

since HeadProvider uses children prop, it's typescript definition should include children: ReactNode

Some tags not collected

When trying to add the following Meta tags (3.0.0-1):

<Title>{title}</Title>
<Meta name="description" content="The title" />
<Meta property="og:description" content="Some description" />
<Meta property="og:image" content="http://example.com/image.jpg" />
<Meta property="og:title" content="OG title" />
<Meta property="og:sometag" content="someValue" />

Only the first and last one is actually collected and rendered.

RFC: Managing tags without extra markup

Problem

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:

  1. Avoiding the extra markup, #84 #40 nfl/react-helmet#79
  2. Having react-head manage tags that it doesn't explicitly own (e.g. a default fallback title tag that's defined as part of a static HTML template), #83

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 */
const headTags = [];
<HeadProvider headTags={headTags} whitelist={['title', '[name="description"]', '[property^="og:"]'}>
  <App />
</HeadProvider>

/* on client */
<HeadProvider whitelist={['title', '[name="description"]', '[property^="og:"]'}>
  <div id="app">
    <Title>Title of page</Title>
    // ...
  </div>
</HeadProvider>

This solution has the following benefits:

  1. Easily implementable
  2. Completely opt-in

While this works, it has the following drawbacks:

  1. Creating that whitelist prop isn't straight-forward (you have to be familiar with uncommon CSS selectors, [foo^=bar])
  2. You have to provide this same selector set to the server and the client (more manual overhead)
  3. 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 */
const headTags = [];
<HeadProvider headTags={headTags} autoWhitelist>
  <App>
    // ... within any component, head tags are rendered as normal
    <Meta name="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:

const headTags = [
  // 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:

  1. 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:

  1. No additional setup required (as @CanRau suggested, the API doesn't need to change much!)
  2. Completely opt-in so it's non-breaking (current behavior would be the default). Maybe we can flip this with a future major release.
  3. 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.

  <title whitelist>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:

  1. Opt-in
  2. Selective SSR bloat vs all

CON:

  1. Complicates implementation
  2. 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 */
const headTags = [];
<HeadProvider headTags={headTags} manageAllTags>
  <App>
    // ... within any component, head tags are rendered as normal
    <Meta name="blah-long-name" content="lots of content here" />
    <Title>Title of page</Title>
  </App>
</HeadProvider>

/* on client */
<HeadProvider manageAllTags>
  <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:

  1. Simple to implement
  2. Simple mental model
  3. No SSR payload bloat

CONs:

  1. All or nothing
  2. May be surprising for folks who don't read the docs :)

Add a page to the SPA example app

Add a new route to the example app that changes the <title /> tag to show how this library works with client-side Single Page Apps.

Server rendering methods other than renderToString(headTags)

I implemented react-head in a small project, and got it working, great!

I did have some trouble however before I got it working, and had to make some modifications, I wonder if this may help others.

Because my server setup is somewhat different, I wanted to render the complete html using a react template that includes the complete head element. But I found that the HeadTag component only works correctly when the collected elements are rendered using renderToString(headTags), as shown in the docs/sample. HeadTag selects elements for client-side replacement by querying with [data-reactroot=""], which is added only to root elements by the renderToString function.

Doing it any other way breaks it. Rendering the headTags using a plain <head>{headTags}</head> does not insert the data-reactroot="" attribute, causing duplicate elements. Using <head>{renderToString(headTags)}</head> escapes it. Removing the [data-reactroot=""] selector breaks the clientside. In the end, I solved it by adding a specific data-ssr="" attribute on the server component and selecting on that. This makes HeadTag independent from the way it is rendered on the server. I also added a key to the client-side version, to get rid of some React warnings.

The result is this (it's also in typescript):

interface HeadTagProps {
    tag?: string;
}

export default class HeadTag extends React.Component<HeadTagProps & (React.MetaHTMLAttributes<{}> | React.LinkHTMLAttributes<{}>)> {
    static contextTypes = {
        reactHeadTags: PropTypes.object,
    };

    static propTypes = {
        tag: PropTypes.string,
    };

    static defaultProps = {
        tag: 'meta',
    };

    state = {
        canUseDOM: false,
    };

    componentDidMount() {
        // eslint-disable-next-line react/no-did-mount-set-state
        this.setState({ canUseDOM: true });

        const { tag, children, ...rest } = this.props; // eslint-disable-line react/prop-types
        const ssrTags = document.head.querySelector(`${tag}${buildSelector(rest)}[data-ssr=""]`);

        /* istanbul ignore else */
        if (ssrTags) {
            ssrTags.remove();
        }
    }

    render() {
        const { tag: Tag, ...rest } = this.props;

        if (this.state.canUseDOM) {
            const Comp = <Tag key={`${Tag}${Object.keys(rest).filter(key => key !== "content" && key !== "children").map(key => ':' + key).join('')}`} {...rest} />;
            return ReactDOM.createPortal(Comp, document.head);
        }

        // on client we don't require HeadCollector
        if (this.context.reactHeadTags) {
            const ServerComp = <Tag data-ssr="" {...rest} />;
            this.context.reactHeadTags.add(ServerComp);
        }

        return null;
    }
}

It's only three small changes, but server rendering is much more flexible, now I can render like this:

....
const GlobalScript = ({ state }) => <script dangerouslySetInnerHTML={{ __html: "window.initialReduxState = " + JSON.stringify(state) }} />

const Html = ({ headTags, appString, state }) => (
    <html lang="en">
        <head>
            <meta charSet="utf-8" />
            <meta name="viewport" content="width=device-width, initial-scale=1.0" />
            <link rel="stylesheet" href="/dist/site.css" />
            <base href="/" />
            {headTags}
        </head>
        <body>
            <div id="react-app" dangerouslySetInnerHTML={{__html: appString}} />
            <GlobalScript state={state} />
            <script src="/dist/vendor.js"></script>
            <script src="/dist/main-client.js"></script>
        </body>
    </html>
    );

// Once any async tasks are done, we can perform the final render
// We also send the redux store state, so the client can continue execution where the server left off
params.domainTasks.then(() => {
    let headTags: React.ReactElement<any>[] = [];
    const appString = renderToString(<App headTags={headTags} />); 

    resolve({
        html: renderToStaticMarkup(<Html headTags={headTags} appString={appString} state={store.getState()} />)
    });
}, reject); // Also propagate any errors back into the host application

React 16 warning

Figure out why React 16 shows this warning:

Warning: Expected server HTML to contain a matching <title> in <div>.

You can see this in action with the example app:

npm run build
cd example
npm install
npm start

Then navigate to http://localhost:3000

Error when meta content includes quote

Version: v3.0.0
Error stack:

index.esm.js?d732:58 Uncaught DOMException: Failed to execute 'querySelector' on 'Element': 'meta[property="og:title"][content=""Có tâm" như Lý Quý Khánh chụp ảnh cho Hà Hồ: Lần nào cũng hết hồn chim én!"][data-rh=""]' is not a valid selector.
    at HeadTag.componentDidMount (webpack-internal:///./node_modules/react-head/dist/index.esm.js:74:33)
    at commitLifeCycles (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:14361:22)
    at commitAllLifeCycles (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:15462:7)
    at HTMLUnknownElement.callCallback (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:100:14)
    at Object.invokeGuardedCallbackDev (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:138:16)
    at invokeGuardedCallback (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:187:29)
    at commitRoot (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:15603:7)
    at completeRoot (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:16618:34)
    at performWorkOnRoot (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:16563:9)
    at performWork (webpack-internal:///./node_modules/react-dom/cjs/react-dom.development.js:16482:7)

Quite serious that page just gone white.

It seems cause of meta content which include double quote mark(")
Maybe do not use content as part of querySelector? (or perform suitable escaping before query)

And, I only want head tags performs on server, and leave it alone at client side (My MPA do not use front-ending routing), Maybe add some option to do this.

Element.remove() not supported in IE

We get a JS exception when using the package in IE11 in this line (index.esm.js):
2018-11-07 11_06_52-index esm js

According to this page , the remove() method is not supported at all in IE.
We currently use the polyfill mentioned in the same link as a workaround, but it would be much better if the package already handles that.

Escaping selector strings

@tizmagik

Hey you should consider also escaping selector strings:

/* eslint no-useless-escape:0 */
const buildSelector = obj =>
  Object.keys(obj)
    .map(k => {
      const cnt = obj[k].replace(/["\\]/g, '\\$&');
      return `[${k}="${cnt}"]`;
    })
    .join('');

export default buildSelector;

TypeScript support

Thanks for providing such a great package! Was running into react-helmet’s deprecated lifecycle API and was happy to find this solution.

I’m using it in a TypeScript environment, which (obviously) doesn’t seem to understand Flow, and needs definitions. The @types repo recommends shipping these TS definitions in the original package rather than in a separate @types/react-head repo (workaround for when the original package is missing definitions).

Would it be possible to ship the following definition file as dist/index.d.ts to npm? I’m using the following locally and this seems to make TypeScript happy. Happy to submit a PR if that’s helpful. Feedback welcome.

declare module 'react-head' {
  export const HeadProvider: React.ComponentClass<{
    headTags?: React.ReactChildren;
  }>;

  export const Link: React.FunctionComponent<React.HTMLProps<HTMLLinkElement>>;

  export const Meta: React.FunctionComponent<React.HTMLProps<HTMLMetaElement>>;

  export const Style: React.FunctionComponent<React.HTMLProps<HTMLStyleElement>>;

  export const Title: React.FunctionComponent<React.HTMLProps<HTMLTitleElement>>;
}

Development server not working

If I follow the instructions in CONTRIBUTING.md, I am unable to get the dev server to run, with any amount of prodding.

OSX 10.13.4, Node 10.10.0, nvm or system/Brew didn't seem to matter.

Playing around with config to try to get it to run or checking out an earlier version will sometimes provide ES module / Babel looking errors but checkout out current master and following the instructions provides:

npm ERR! prepareGitDep 1>
npm ERR! prepareGitDep > [email protected] prepare /Users/jay/proj/ass/react-head
npm ERR! prepareGitDep > npm run test && npm run build
npm ERR! prepareGitDep
npm ERR! prepareGitDep
npm ERR! prepareGitDep > [email protected] test /Users/jay/proj/ass/react-head
npm ERR! prepareGitDep > jest --no-cache
npm ERR! prepareGitDep
npm ERR! prepareGitDep
npm ERR! prepareGitDep 2> npm WARN install Usage of the `--dev` option is deprecated. Use `--only=dev` instead.
npm ERR! prepareGitDep FAIL tests/ssr.test.js
npm ERR! prepareGitDep   ● fails if headTags is not passed to <HeadProvider />
npm ERR! prepareGitDep
npm ERR! prepareGitDep     expect(function).toThrowError(regexp)
npm ERR! prepareGitDep
npm ERR! prepareGitDep     Expected the function to throw an error matching:
npm ERR! prepareGitDep       /headTags array should be passed/
npm ERR! prepareGitDep     Instead, it threw:
npm ERR! prepareGitDep       ReferenceError: invariant is not defined
npm ERR! prepareGitDep           69 |
npm ERR! prepareGitDep           70 |   render() {
npm ERR! prepareGitDep         > 71 |     invariant(
npm ERR! prepareGitDep              |     ^
npm ERR! prepareGitDep           72 |       typeof window !== 'undefined' || Array.isArray(this.props.headTags),
npm ERR! prepareGitDep           73 |       'headTags array should be passed to <HeadProvider /> in node'
npm ERR! prepareGitDep           74 |     );
npm ERR! prepareGitDep
npm ERR! prepareGitDep       at HeadProvider.render (src/HeadProvider.js:71:5)
npm ERR! prepareGitDep       at d (node_modules/react-dom/cjs/react-dom-server.node.production.min.js:28:211)
npm ERR! prepareGitDep       at wa (node_modules/react-dom/cjs/react-dom-server.node.production.min.js:28:493)
npm ERR! prepareGitDep       at a.render (node_modules/react-dom/cjs/react-dom-server.node.production.min.js:33:48)
npm ERR! prepareGitDep       at a.read (node_modules/react-dom/cjs/react-dom-server.node.production.min.js:32:288)
npm ERR! prepareGitDep       at renderToStaticMarkup (node_modules/react-dom/cjs/react-dom-server.node.production.min.js:43:75)
npm ERR! prepareGitDep       at tests/ssr.test.js:76:5
npm ERR! prepareGitDep       at Object.<anonymous> (node_modules/expect/build/to_throw_matchers.js:51:9)
npm ERR! prepareGitDep       at Object.throwingMatcher [as toThrowError] (node_modules/expect/build/index.js:320:33)
npm ERR! prepareGitDep       at Object.toThrowError (tests/ssr.test.js:81:6)
npm ERR! prepareGitDep       at Object.toThrowError (tests/ssr.test.js:81:6)
npm ERR! prepareGitDep
npm ERR! prepareGitDep   ● fails if headTags is not an array
npm ERR! prepareGitDep
npm ERR! prepareGitDep     expect(function).toThrowError(regexp)
npm ERR! prepareGitDep
npm ERR! prepareGitDep     Expected the function to throw an error matching:

<html> attributes

While trying to migrate to react-head I noticed react-helmets and I'm not sure if current react-head can do this already?
If yes I would like some help, if not might this be integrated some day or should I handle it differently?

By the way, the lang attribute is still relevant as far as I can tell right?

edit: should have mentioned that I have actually multiple languages, so it's not about a static declaration but about a dynamic one based on props

maybe I just update it using plain js?

Support cascading tags

A potentially useful feature is the ability to support cascading tags with the same tag and/or name attribute.

This may be just a matter of collecting tags to an associative array (object) where the key is the tag name and then overriding previously collected tags as the components render in order.

Similar functionality exists in react-helmet which is inspiration for this library to begin with

itemProp support on <Meta />

TLDR: <Meta /> appears to not have support for the itemprop prop, which can be a great tool for improving SEO.

Problem: <Meta /> tags that have an itemProp set don't get added to the DOM.

Ask: Can you add support for the itemprop attribute on <Meta />? Alternatively, would you accept a PR if I did?

More context:

<meta /> supports the itemprop attribute, which is a way of annotating markup so that Google can know what a specific element corresponds to. Typically, you use it on the element it the text directly pertains to -- so you'd have itemProp="datePublished" on the date published timestamp.

Sometimes, you can't. Not all metadata is displayed in the page. In that case, you can provide a <meta itemprop="microdata_attribute_name" content="microdata_attribute_value" /> tag which lets you invisibly add the meta tag, so that Google still knows about the metadata even though it's not actually visible to the user in the same format.

Invariant failed in HeadTags

I following the example to try to run this library on dev env and and prod env with ssr.
dev env is working fine but prod env with ssr is crashing with:


(node:25652) UnhandledPromiseRejectionWarning: Error: Invariant failed
    at index (/home/pc/Documents/funspace/babysearch/isomorphic-react-redux-saga-ssr/build/Routes.js:12781:11)
    at Object.children (/home/pc/Documents/funspace/babysearch/isomorphic-react-redux-saga-ssr/build/Routes.js:2365:110)
    at a.render (/home/pc/Documents/funspace/babysearch/isomorphic-react-redux-saga-ssr/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:36:400)
    at a.read (/home/pc/Documents/funspace/babysearch/isomorphic-react-redux-saga-ssr/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:34:288)
    at Object.renderToString (/home/pc/Documents/funspace/babysearch/isomorphic-react-redux-saga-ssr/node_modules/react-dom/cjs/react-dom-server.node.production.min.js:45:6)
    at loadBranchData.then._data (/home/pc/Documents/funspace/babysearch/isomorphic-react-redux-saga-ssr/lib/server/ssr.js:116:36)
    at <anonymous>
    at process._tickCallback (internal/process/next_tick.js:188:7)
(node:25652) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3)

I took a screenshot to show you what the line /isomorphic-react-redux-saga-ssr/build/Routes.js:2365 correspond to:

screenshot from 2018-09-10 15-00-59

My SSR setup is pretty straightforward with:

  loadBranchData(location, routes).then(_data => {
    const context = {
      splitPoints: [],
    };
    const headTags = [];
    const rootComponent = PROD ? (
      <IntlProvider locale={locale} messages={messages[locale]}>
        <Provider store={store}>
          <HeadProvider headTags={headTags}>
            <StaticRouter location={location} context={context}>
              <Layout />
            </StaticRouter>
          </HeadProvider>
        </Provider>
      </IntlProvider>
    ) : null;
    const markup = ReactDOMServer.renderToString(rootComponent);
    const state = store.getState();
    const html = ReactDOMServer.renderToStaticMarkup(
      <Html
        PROD={PROD}
        assets={assets}
        markup={markup}
        state={state}
        splitPoints={context.splitPoints}
        localeData={localeData[locale]}
      />
    );
    res.send(doctype + html);

it is crashing on the line const markup = ReactDOMServer.renderToString(rootComponent);

My client is having this code:

const rootEl = document.getElementById("root");
const renderApp = localeData => {
  hydrate(
    <IntlProvider locale={appLocale} messages={localeData}>
      <Provider store={store}>
        <HeadProvider>
          <AppContainer history={history} />
        </HeadProvider>
      </Provider>
    </IntlProvider>,
    rootEl
  );
};

and down the AppContainer component and many other child components I render some Title and Meta

const DirectoryLayout = _props => (
  <div>
    <Title>Browse Directory</Title>
    <Meta name="description" content="Some directory" />
    <Meta property="og:title" content="Directory" />
    <Meta property="og:image" content="path/to/image.jpg" />
    <AlphabetLinks />
    <AlphabetDirectory />
  </div>
);

Can you please help solving this issue?

Duplicate Title tags

If I render a Title head tag in my root component and then another with a different value in a child component it results in two title tags being collected on the server. Are we meant to filter collected tags manually so that there are no duplicates?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.