Skip to content

Commit

Permalink
update seo example to use location and static query hooks. (#22772)
Browse files Browse the repository at this point in the history
  • Loading branch information
bradgarropy committed Apr 7, 2020
1 parent 0c48023 commit f95cd06
Showing 1 changed file with 93 additions and 94 deletions.
187 changes: 93 additions & 94 deletions docs/docs/add-seo-component.md
Expand Up @@ -4,11 +4,11 @@ title: "Adding an SEO Component"

Every site on the web has basic _meta-tags_ like the title, favicon or description of the page in their `<head>` element. This information gets displayed in the browser and is used when someone shares your website, e.g. on Twitter. You can give your users and these websites additional data to embed your website with more data — and that's where this guide for a SEO component comes in. At the end you'll have a component you can place in your layout file and have rich previews for other clients, smartphone users, and search engines.

_Note: This component will use StaticQuery. If you're unfamiliar with that, have a look at the [StaticQuery documentation](/docs/static-query/). You also have to have `react-helmet` installed for which you can have a look at [this document](/docs/add-page-metadata)._
_Note: This component will use `useStaticQuery`. If you're unfamiliar with that, have a look at the [useStaticQuery documentation](/docs/use-static-query/). You also have to have `react-helmet` installed for which you can have a look at [this document](/docs/add-page-metadata)._

## gatsby-config.js

Gatsby makes all data put into the `siteMetadata` section of your `gatsby-config` file automatically available in GraphQL and therefore it's a good idea to place your information for the component there.
Gatsby automatically exposes the `siteMetadata` section of the `gatsby-config` file in the GraphQL datalayer. It's considered best practice to place your site meta information there.

```js:title=gatsby-config.js
module.exports = {
Expand All @@ -26,31 +26,30 @@ module.exports = {

## SEO component

Create a new component with this initial boilerplate:
First create a new component with this initial boilerplate.

```jsx:title=src/components/SEO.js
import React from "react"
import { Helmet } from "react-helmet"
import PropTypes from "prop-types"
import { StaticQuery, graphql } from "gatsby"
import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ title, description, image, pathname, article }) => ()
const SEO = ({ title, description, image, article }) => ()

export default SEO

SEO.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.string,
pathname: PropTypes.string,
article: PropTypes.bool,
}

SEO.defaultProps = {
title: null,
description: null,
image: null,
pathname: null,
article: false,
}
```
Expand All @@ -59,12 +58,15 @@ SEO.defaultProps = {

As the SEO component should also be usable in other files, e.g. a template file, the component also accepts properties for which you set sensible defaults in the `SEO.defaultProps` section. This way the information you put into `siteMetadata` gets used every time unless you define the property explicitly.

Now define the query and place it in the StaticQuery (you can also save the query in a constant). You can also alias query items, so `title` gets renamed to `defaultTitle`.
Now define the query and pass it to `useStaticQuery`. You can also alias query items, so `title` gets renamed to `defaultTitle`.

```jsx:title=src/components/SEO.js
const SEO = ({ title, description, image, pathname, article }) => (
<StaticQuery query={query} render={} />
)
const SEO = ({ title, description, image, article }) => {
const { location } = useLocation()
const { site } = useStaticQuery(query)

return null
}

export default SEO

Expand All @@ -84,114 +86,111 @@ const query = graphql`
`
```

The next step is to destructure the data from the query and to create an object that checks if the props were used — if not the default values are utilized. The name aliasing comes in handy here: It avoids name collisions.
The next step is to destructure the data from the query and create an object that checks if the props were used. If not, the default values are applied. Aliasing the properties comes in handy here to avoid name collisions.

```jsx:title=src/components/SEO.js
const SEO = ({ title, description, image, pathname, article }) => (
<StaticQuery
query={query}
render={({
site: {
siteMetadata: {
defaultTitle,
titleTemplate,
defaultDescription,
siteUrl,
defaultImage,
twitterUsername,
}
}
}) => {
const seo = {
title: title || defaultTitle,
description: description || defaultDescription,
image: `${siteUrl}${image || defaultImage}`,
url: `${siteUrl}${pathname || '/'}`,
}
const SEO = ({ title, description, image, article }) => {
const { pathname } = useLocation()
const { site } = useStaticQuery(query)

const {
defaultTitle,
titleTemplate,
defaultDescription,
siteUrl,
defaultImage,
twitterUsername,
} = site.siteMetadata

const seo = {
title: title || defaultTitle,
description: description || defaultDescription,
image: `${siteUrl}${image || defaultImage}`,
url: `${siteUrl}${pathname}`,
}

return ()
}}
/>
)
return null
}

export default SEO
```

The last step is to return this data with the help of `Helmet`. Your complete SEO component should look like:
The last step is to return this data with the help of `Helmet`. Your complete SEO component should look like this.

```jsx:title=src/components/SEO.js
import React from "react"
import { Helmet } from "react-helmet"
import PropTypes from "prop-types"
import { StaticQuery, graphql } from "gatsby"

const SEO = ({ title, description, image, pathname, article }) => (
<StaticQuery
query={query}
render={({
site: {
siteMetadata: {
defaultTitle,
titleTemplate,
defaultDescription,
siteUrl,
defaultImage,
twitterUsername,
},
},
}) => {
const seo = {
title: title || defaultTitle,
description: description || defaultDescription,
image: `${siteUrl}${image || defaultImage}`,
url: `${siteUrl}${pathname || "/"}`,
}
import { Helmet } from "react-helmet"
import { useLocation } from "@reach/router"
import { useStaticQuery, graphql } from "gatsby"

const SEO = ({ title, description, image, article }) => {
const { pathname } = useLocation()
const { site } = useStaticQuery(query)

const {
defaultTitle,
titleTemplate,
defaultDescription,
siteUrl,
defaultImage,
twitterUsername,
} = site.siteMetadata

const seo = {
title: title || defaultTitle,
description: description || defaultDescription,
image: `${siteUrl}${image || defaultImage}`,
url: `${siteUrl}${pathname}`,
}

return (
<>
<Helmet title={seo.title} titleTemplate={titleTemplate}>
<meta name="description" content={seo.description} />
<meta name="image" content={seo.image} />
{seo.url && <meta property="og:url" content={seo.url} />}
{(article ? true : null) && (
<meta property="og:type" content="article" />
)}
{seo.title && <meta property="og:title" content={seo.title} />}
{seo.description && (
<meta property="og:description" content={seo.description} />
)}
{seo.image && <meta property="og:image" content={seo.image} />}
<meta name="twitter:card" content="summary_large_image" />
{twitterUsername && (
<meta name="twitter:creator" content={twitterUsername} />
)}
{seo.title && <meta name="twitter:title" content={seo.title} />}
{seo.description && (
<meta name="twitter:description" content={seo.description} />
)}
{seo.image && <meta name="twitter:image" content={seo.image} />}
</Helmet>
</>
)
}}
/>
)
return (
<Helmet title={seo.title} titleTemplate={titleTemplate}>
<meta name="description" content={seo.description} />
<meta name="image" content={seo.image} />

{seo.url && <meta property="og:url" content={seo.url} />}

{(article ? true : null) && <meta property="og:type" content="article" />}

{seo.title && <meta property="og:title" content={seo.title} />}

{seo.description && (
<meta property="og:description" content={seo.description} />
)}

{seo.image && <meta property="og:image" content={seo.image} />}

<meta name="twitter:card" content="summary_large_image" />

{twitterUsername && (
<meta name="twitter:creator" content={twitterUsername} />
)}

{seo.title && <meta name="twitter:title" content={seo.title} />}

{seo.description && (
<meta name="twitter:description" content={seo.description} />
)}

{seo.image && <meta name="twitter:image" content={seo.image} />}
</Helmet>
)
}

export default SEO

SEO.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.string,
pathname: PropTypes.string,
article: PropTypes.bool,
}

SEO.defaultProps = {
title: null,
description: null,
image: null,
pathname: null,
article: false,
}

Expand Down

0 comments on commit f95cd06

Please sign in to comment.