SEO metatags for React apps, mainly to be used with Gatsby + react-helmet





GitHub Stars



Last Commit

10mos ago






Size (min+gzip)




Type Definitions





react-seo-meta-tags npm version

SEO metatags for React apps, originally designed for a Gatsby blog site with react-helmet. But it should work with any React setup, such as Next.js apps.

The motive behind it was the infuriating complexity of SEO coupled with the fact there wasn't any simple and understandable SEO packages out there for React.

Example Gatsby site

How to install

Requires react >=16.2.0. You might also want to use react-helmet.

npm i react-seo-meta-tags

How to use

With react-helmet:

import React from 'react'
import Helmet from 'react-helmet'
import { ReactSEOMetaTags } from 'react-seo-meta-tags'
// Or
import ReactSEOMetaTags from 'react-seo-meta-tags'

      render={(el: React.ReactNode) => <Helmet>{el}</Helmet>}
      website={{ ...siteMetadata }}


import Head from 'next/head'
import { ReactSEOMetaTags } from 'react-seo-meta-tags'

      render={(el: React.ReactNode) => <Head>{el}</Head>}
      website={{ ...siteMetadata }}

where siteMetadata could be:

  url: '',
  title:  'This is a 70 character long title with a lot of padding to make it so!',
  datePublished: '2019-10-06T13:56:03.123Z',
  description: 'This is a 200 character long description of this web page which is quite interesting and which describes its contents well with a lot of relevant keywords and isn\'t just general marketing mumbo-jumbo.',
  language: 'en-US',
  image: '',
  author: {
    email: '',
    name: 'John Smith',
    image: '',
  site: {
    siteName: 'IMDb',
    searchUrl: '',

It appears both react-helmet and Next.js are a bit picky how they accept meta tags. ReactHelmet being the more choosy as it doesn't accept any nesting. Next.js works for some children but not all and therefore using the render function is required.


import React from 'react'
import { ReactSEOMetaTags } from 'react-seo-meta-tags'

      website={{ ...siteMetadata }}
        { name: 'My Site', url: '' },
        { name: 'Blog', url: '' },
        name: 'Google',
        legalName: 'Google Inc',
        url: '',
        logo: ''
      blogPost={{ ...blogPost }}
      facebook={{ facebookAppId: 'abc123' }}
      twitter={{ twitterUser: '@mickey_mouse' }}

In the previous example the inheritance of the properties goes like this: website < blogPost < facebook | twitter. So if the same property (eg image) is specified in blogPost and facebook, the facebook object's property will be the one used in its respective tag (og:image in this case).

The current properties shown are the ones I've seen websites use the most. Most sites don't even use JSON-LD, for example Google doesn't use it even though they have been a big contributor to it. So yeah, I wouldn't recommend going too deep into this rabbit hole. The API is complicated and if you feel there's something missing create a PR.

General page

For example a front or about page. I recommend adding just the website property with an organization if you have one. In that case remember to add good enough list of sameAs URLs (they seem to be helpful). Breadcrumb if you feel it makes sense. NOTE: Always remember to add the title of the page, it's used also to render a <title> tag so be aware!

For the image of a general page I just used the logo for my example site. I strongly recommend having that image in 2:1 ratio eg 440x220 in order to make it appear nicely when sharing.

Blog post

For a blog post, provide all the blogPost values. I know it's a bit of work, but this way you'll avoid any complaints from SEO validation tools such as For Facebook and Twitter you can customize the image sizes or titles as they appear when the post is shared. I only added the tags I thought were useful, if something's missing please create an issue or PR. Also Google expects BlogPosts's to have publisher property and that to be an organization. In spec Person is also a valid value, but Google doesn't think so. You could just use yourself as a fake organization I guess if nothing else.


ReactSEOMetaTags currently is used for only two types of pages: website and blogpost.

Website is a general page with some added Facebook (og) and Twitter tags for better SEO. Also includes JSON-LD schema.

BlogPost is a page for a blogpost (og:type article) that can have quite a few properties. I made only title to be required but you probably want at least description, image, datePublished and author to be specified. Well in general it's probably best for SEO purposes to include most of them, if not all. Each bit helps, but real traffic will always boost your SEO much more than these types of meta tags.

For more custom tags you either have to render them by yourself or, and I hope you will, create a PR that includes the missing features in a compact and well documented way. There's so much boilerplate already with these tags so I don't want to include some random tags without knowing what they are for.

export interface ReactSEOMetaTagsProps {
  render?: (el: React.ReactNode) => React.ReactNode
  website?: WebsiteProps
  breadcrumb?: BreadcrumbList
  organization?: OrganizationProps
  blogPost?: BlogPostProps
  facebook?: FacebookProps
  twitter?: TwitterProps

export interface WebsiteProps {
  url?: string // The URL of this page (eg
  title: string // Maximum 70 characters.
  // The original publication date. ISO 8601 timestamp eg "2019-10-06T13:56:03.123Z"
  // Don't know how useful for random webpages. Add it at least for blog posts.
  datePublished?: string
  description?: string // Maximum 200 characters.
  language?: string // Default "en-US" and
  image?: string // URL to the image, PNG, JPEG, or GIF recommended.
  imageAlt?: string // Alt for the image
  // Possibly redundant property. But at least bots can scrape your email and that's fun right? :)
  author?: PersonProps
  site?: {
    // "If your object is part of a larger web site, the name which should be displayed for the overall site. e.g., "IMDb"."
    // At least Telegram uses this as gray text above the title when sharing links.
    siteName?: string
    // If your website has a search functionality, enter the URL with parameter here eg ""
    searchUrl?: string

export interface FacebookProps {
  title?: string // The title of your article without any branding such as your site name.
  description?: string // A brief description of the content, usually between 2 and 4 sentences.
  language?: string // Used for og:locale. Default "en-US"
  // Facebook recommends 1200x630 size, ratio of 1.91:1. PNG, JPEG, or GIF.
  // But if you want your image to be displayed as a smaller image (aka thumbnail, similar to 'summary' Twitter card),
  // your image should be smaller than 400x209 and preferably with a ratio of 1:1. Unless your image can be cropped
  // into a 1:1 box without making it look bad. So you probably want to use something like 200x200.
  // Note the "minimum size constraint of 200px by 200px".
  image?: string
  imageAlt?: string // Alt for the image
  video?: string // "A URL to a video file that complements this object."
  audio?: string // "A URL to an audio file to accompany this object."
  facebookAppId?: string // "Insights lets you view analytics for traffic to your site from Facebook."

export interface TwitterProps {
  title?: string // Title of content (max 70 characters). Fallback: og:title.
  description?: string // Description of content (maximum 200 characters). Fallback: og:description.
  image?: string // Twitter card image, optimal ratio 1.91:1. Recommended: 1200x628. PNG, JPEG, or GIF. Fallback: og:image.
  imageAlt?: string // Alt for the image. Fallback: og:image:alt.
  // So since I saw that the large image looked dumb if you are using your faceshot as the image, I added the smaller 'summary'
  // type as an option. It should be in 1:1 scale. Also there exists 'player' and 'app' types but since I'm not using those,
  // I'm not going to spend my precious time figuring out how they work for now.
  cardType?: 'summary_large_image' | 'summary' // Default 'summary_large_image'
  twitterUser?: string // @username of content creator.
  twitterSite?: string // @username of the site eg @nytimes

export interface BlogPostProps {
  // The canonical URL for your page. This should be the undecorated URL, without session
  // variables, user identifying parameters, or counters.
  url?: string
  title: string // Title of the post. Max 70 characters.
  description?: string // Should be a short description about the topic, <=200 words. Mainly for SEO purposes.
  image: string // You should add this. Just use the same image as og/twitter eg 1200x630 with 1.91:1 ratio in PNG, JPEG, or GIF.
  imageAlt?: string // Some SEO tools really want this so it's probably wise to add it
  datePublished?: string // The original publication date. Don't change arbitrarily, Google might downrank you.
  dateModified?: string // Google prefers recent content in search results and also users are more likely to click a recent article
  tags?: string[]
   * Technically either Person or Organization, but since it doesn't make any sense to not to credit this content to a human, use person.
   * From
   * "Please note that author is special in that HTML 5 provides a special mechanism for indicating authorship
   * via the rel tag. That is equivalent to this and may be used interchangeably."
   * Which means.. something.
  author?: PersonProps
   * You should add this since otherwise Google's structured-data tool will complain...
   * However
  publisher?: OrganizationProps
  site?: {
    // "If your object is part of a larger web site, the name which should be displayed for the overall site. e.g., "IMDb"."
    // Used for og:site_name
    siteName?: string

export type BreadcrumbList = BreadcrumbProps[]

export interface BreadcrumbProps {
  url: string
  title: string
  image?: string

export interface PersonProps {
  email?: string
  name?: string
  image?: string

export interface OrganizationProps {
  '@type'?: string // Default 'Organization'. You could use eg 'Corporation'.
  description?: string
  name: string // Eg "Google"
  legalName?: string // Eg "Google Inc"
  logo: string // URL to the logo image.
  // List of other webpages referencing this organization, eg Wikipedia, Facebook, Twitter, Instagram etc.
  // Will show these sites alongside your website in Google search results and probably boost your SEO rank too.
  sameAs?: string[]
  url: string // URL to the organization, eg ""
  parentOrganization?: OrganizationProps // You can nest as many organizations as you'd like, dunno how useful it's

How to develop locally

Requires: Node.js >= 8.

  1. Clone this repo & run npm i
  2. Star the TypeScript compiler: npm run ts:watch
  3. In another terminal go to the example project and install dependencies: cd example & npm i
  4. Start the Gatsby app with: npm start
  5. The app should open in http://localhost:8000. The changes to the source code should be reloaded automatically and be seen in the <head>'s metatags. Happy hacking!

How to publish changes

This one is more for the maintainers such as me. All the changes should go through PRs. But because there's no automatic CI such as Travis set up (yet), the new versions are published locally.

1) Login to your npm account: npm login. 2) After making changes to the code (cough pulling from master I mean), run npm run compile. 3) Depending on the changes you have made, use either npm version patch|minor|major to update the version in package.json. Do it semantically*. 4) It should automatically tag it, but if it didn't tag it yourself: git tag v1.5.1. 5) Push the changes to GitHub: git push origin master && git push origin master --tags. 6) Publish to npm: npm publish. This will push the files specified in package.json "files"-block + default files (package.json, README, LICENSE).

*Semantically: big breaking changes is a major release, general changes or bug fixes are minor releases and patches are just some general maintenance or refactoring.

How to contribute

Create issues or PRs for bug reports/feedback/feature requests.

SEO Tools

Other resources about SEO in general

Open Graph

Metadata format developed by Facebook.

Rate & Review

Great Documentation0
Easy to Use0
Highly Customizable0
Bleeding Edge0
Responsive Maintainers0
Poor Documentation0
Hard to Use0
Unwelcoming Community0
No reviews found
Be the first to rate


No tutorials found
Add a tutorial