Add twitter:card with react helmet - reactjs

I added all the meta tags to create a twitter card but when I try to do the twitter validation it tells me: INFO: Page fetched successfully
INFO: 3 metatags were found
ERROR: No card found (Card error)
I don't think I can put all the meta tags in meta={[...]} because it doesn't work the same.
What can I do to resolve? It's important!!!
import React from 'react'
import PropTypes from 'prop-types'
import Helmet from 'react-helmet'
import Header from '../components/header'
import './index.css'
const Layout = ({ children, data }) => (
<div>
<Helmet
title={data.site.siteMetadata.title}
meta={[
{ name: 'description', content: data.site.siteMetadata.description },
{ name: 'keywords', content: data.site.siteMetadata.keywords },
]}
>
<meta name="twitter:card" content="summary" />
<meta
name="twitter:description"
content="xxx"
/>
<meta
name="twitter:title"
content="xxx"
/>
<meta property="og:locale" content="en_US" />
<meta property="og:type" content="website" />
<meta
property="og:title"
content="xxx"
/>
<meta
property="og:description"
content="xxx"
/>
<meta property="og:url" content="http://example.com/" />
<meta property="og:site_name" content="xxx" />
</Helmet>
<Header />
{children()}
</div>
)
Layout.propTypes = {
children: PropTypes.func,
}
export default Layout
export const query = graphql`
query SiteTitleQuery {
site {
siteMetadata {
title
description
keywords
}
}
}
`
INFO: Page fetched successfully
INFO: 3 metatags were found
ERROR: No card found (Card error)

Related

Meta tags from pages are missing in page source

I am having the Nextjs-based Web App for my Portfolio Website, where I've defined some common meta tags in the _document.jsx file and I have a few static pages in which in the browser tab I can see the title, but when I open the Page Source those titles are missing.
My _document.jsx file code are below:
// Next
import Document, { Html, Head, Main, NextScript } from 'next/document';
import Script from 'next/script';
// styled-components
import { ServerStyleSheet } from 'styled-components';
const APP_NAME = `Dhaval Vira Resume | CV | Portfolio`;
const APP_DESC = `A skilled full-stack developer who has worked on projects ranging from small personal sites to large enterprise systems.`;
const APP_URL = `https://dhavalvira.com`;
class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (Component) => (props) =>
sheet.collectStyles(<Component {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: [initialProps.styles, sheet.getStyleElement()],
};
} finally {
sheet.seal();
}
}
render() {
return (
<Html lang='en-US'>
<Head>
{/* General Meta Tags */}
<meta charSet='utf-8' />
<meta httpEquiv='X-UA-Compatible' content='IE=edge' />
<link rel='icon' href='/favicon.ico' />
{/* Meta Tags */}
<meta name='application-name' content={APP_NAME} />
<meta name='description' content={APP_DESC} />
<meta name='author' content='Dhaval Vira' />
<meta name='robots' content='index, follow' />
<meta name='rating' content='general' />
<link rel='canonical' href={APP_URL} />
<meta httpEquiv='Content-Type' content='text/html; charset=utf-8' />
<meta name='language' content='English' />
<meta name='revisit-after' content='1 day' />
<meta name='creationdate' content='14-Feb-2022' />
<meta name='distribution' content='global' />
<meta
name='keywords'
content='CV, resume, online cv, online resume, professional resume, portfolio, next js developer, nextjs developer, freelance developer, full stack developer, full-stack developer, freelance full stack developer, freelance full-stack developer, freelance next js developer, freelance next.js developer, freelance nextjs developer'
/>
{/* Open Graph */}
<meta property='og:url' content={APP_URL} />
<meta property='og:type' content='website' />
<meta property='og:title' content={APP_NAME} />
<meta property='og:description' content={APP_DESC} />
<meta property='og:image' content='/D_V_Cropped.png' />
{/* Twitter Meta Tags */}
<meta name='twitter:card' content='summary_large_image' />
<meta property='twitter:domain' content={APP_URL} />
<meta property='twitter:url' content={APP_URL} />
<meta name='twitter:title' content={APP_NAME} />
<meta name='twitter:description' content={APP_DESC} />
<meta name='twitter:image' content='/D_V_Cropped.png' />
{/* Some Extra Tags */}
<meta name='apple-mobile-web-app-capable' content='yes' />
<meta
name='apple-mobile-web-app-status-bar-style'
content='default'
/>
<meta name='apple-mobile-web-app-title' content={APP_NAME} />
<meta name='description' content={APP_DESC} />
<meta name='format-detection' content='telephone=no' />
<meta name='mobile-web-app-capable' content='yes' />
<meta name='theme-color' content='#000000' />
<link rel='manifest' href='/manifest.json' />
<link rel='apple-touch-icon' href='/D_V_Cropped.png' />
{/* Icons */}
<link
rel='apple-touch-icon'
sizes='57x57'
href='/icons/apple-icon-57x57.png'
/>
<link
rel='apple-touch-icon'
sizes='60x60'
href='/icons/apple-icon-60x60.png'
/>
<link
rel='apple-touch-icon'
sizes='72x72'
href='/icons/apple-icon-72x72.png'
/>
<link
rel='apple-touch-icon'
sizes='76x76'
href='/icons/apple-icon-76x76.png'
/>
<link
rel='apple-touch-icon'
sizes='114x114'
href='/icons/apple-icon-114x114.png'
/>
<link
rel='apple-touch-icon'
sizes='120x120'
href='/icons/apple-icon-120x120.png'
/>
<link
rel='apple-touch-icon'
sizes='144x144'
href='/icons/apple-icon-144x144.png'
/>
<link
rel='apple-touch-icon'
sizes='152x152'
href='/icons/apple-icon-152x152.png'
/>
<link
rel='apple-touch-icon'
sizes='180x180'
href='/icons/apple-icon-180x180.png'
/>
<link
rel='apple-touch-icon'
sizes='192x192'
href='/icons/apple-icon-precomposed.png'
/>
<link
rel='icon'
type='image/png'
sizes='192x192'
href='/icons/android-icon-192x192.png'
/>
<link
rel='icon'
type='image/png'
sizes='32x32'
href='/icons/favicon-32x32.png'
/>
<link
rel='icon'
type='image/png'
sizes='96x96'
href='/icons/favicon-96x96.png'
/>
<link
rel='icon'
type='image/png'
sizes='16x16'
href='/icons/favicon-16x16.png'
/>
<meta name='msapplication-TileColor' content='#54ca95' />
<meta name='msapplication-TileImage' content='/ms-icon-144x144.png' />
<meta name='msapplication-TileImage' content='/ms-icon-150x150.png' />
<meta name='msapplication-TileImage' content='/ms-icon-310x310.png' />
<meta name='theme-color' content='#54ca95' />
{/* Google Tag Manager */}
// GTM iFrame Tag
{/* LinkedIn Insight Tags */}
// LinkedIn Insight Scrip Tags
{/* Google Tag Manager */}
// Google Tag Manager Script Tag
</Script>
{/* Schema.org */}
// Schema.org Script Tag
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
Below code is from blogs.jsx in which the title inside the <Head></Head> tag remains static <Head><title>Blog :: Dhaval Vira Resume</title></Head>. this title is visible in the Chrome Tab, but when I open the Page Source, that time it's missing.
Below is the code from pages/blog/[index].js Page, and when I open the below Page Source - all those Meta Tags are missing.
const BlogPost = (props) => {
const router = useRouter();
useEffect(() => {
Prism.highlightAll();
}, []);
// destructuring props
const { singleData, ...other } = props;
return (
<Fragment>
<Head>
<title>{singleData[0].blogTitle} :: Dhaval Vira Resume</title>
<meta name='description' content={singleData[0].seoDescription} />
<meta name='keywords' content={singleData[0].tags.map((tag) => tag)} />
{/* OG Meta Tags */}
<meta property='og:title' content={singleData[0].seoTitle} />
<meta
property='og:description'
content={singleData[0].seoDescription}
/>
<meta property='og:type' content='article' />
<meta
property='og:url'
content={`${process.env.SHARE_URL}${router.asPath}`}
/>
<meta
name='image'
property='og:image'
content={singleData[0].displayImageUrl}
/>
{/* Twitter Meta Tags */}
<meta property='twitter:title' content={singleData[0].seoTitle} />
<meta
property='twitter:description'
content={singleData[0].seoDescription}
/>
<meta
property='twitter:image'
content={singleData[0].displayImageUrl}
/>
<link
rel='canonical'
href={`${process.env.SHARE_URL}${router.asPath}`}
/>
</Head>
<div>{/* HTML Code goes here */}</div>
</Fragment>
);
};
export default BlogPost;
export const getServerSideProps = async (context) => {
const { index } = context.params;
const response = await axios({
url: `${process.env.SERVER_URL}/api/get-single-blog-detail?slug=${index}`,
method: 'GET',
validateStatus: function (status) {
return status >= 200 && status < 599;
},
});
switch (response.status) {
case 200:
return {
props: {
singleData: response.data.comments,
},
};
case 500:
return {
redirect: {
destination: '/500',
permanent: false,
},
};
default:
return {
redirect: {
destination: '/404',
permanent: false,
},
};
}
};
Also, if I'm sharing the link of my Portfolio Site with anyone (WhatsApp or LinkedIn) at that place also when the title is coming up, it's coming from either Static Page or Dynamic Page, it's coming from _document.jsx file.
Below is the _app.jsx code:
// React
import React, { Fragment, useEffect, useState } from 'react';
// next component
import Head from 'next/head';
import Router from 'next/router';
// Preloader Component
import Loader from '../Loader/loader';
// react-hot-toast
import { Toaster } from 'react-hot-toast';
// NProgress Package & CSS
import NProgress from 'nprogress';
import '../styles/nprogress.css';
Router.events.on('routeChangeStart', NProgress.start);
Router.events.on('routeChangeError', NProgress.done);
Router.events.on('routeChangeComplete', NProgress.done);
import '../styles/globals.css';
// Google Firebase - Analytics - SDK
import { getAnalytics, logEvent } from 'firebase/analytics';
// Utils Func for Firebase
import { app } from '../utils/firebase';
function MyApp(props) {
const { Component, pageProps } = props;
const [loading, setLoading] = useState(true);
useEffect(() => {
setTimeout(() => {
setLoading(false);
}, 4000);
}, []);
if (loading) {
return (
<Fragment>
<Head>
<title>Dhaval Vira Resume</title>
<meta
name='description'
content='online resume website of Dhaval Vira, cv of Dhaval Vira, portfolio of Dhaval Vira'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<Loader />
</Fragment>
);
} else {
return (
<Fragment>
<Head>
<title>Dhaval Vira Resume</title>
<meta
name='description'
content='online resume website of Dhaval Vira, cv of Dhaval Vira, portfolio of Dhaval Vira'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<Component {...pageProps} />
{/* react-hot-toast */}
<Toaster position='top-right' reverseOrder={false} />
</Fragment>
);
}
}
export default MyApp;
In _app.jsx, you're forcing client-side rendering of your pages content after 4s have passed due the the loading logic you have there. This means that when the pages gets pre-rendered on the server you're only getting the Loader component in the HTML sent from the server.
Remove the loading logic entirely so that your pages can be server-side rendered properly.
function MyApp({ Component, pageProps }) {
return (
<Fragment>
<Head>
<title>Dhaval Vira Resume</title>
<meta
name='description'
content='online resume website of Dhaval Vira, cv of Dhaval Vira, portfolio of Dhaval Vira'
/>
<link rel='icon' href='/favicon.ico' />
</Head>
<Component {...pageProps} />
{/* react-hot-toast */}
<Toaster position='top-right' reverseOrder={false} />
</Fragment>
);
}

React Helmet how to set title image favicon?

I created ReactHelmet component:
import { Helmet } from "react-helmet";
interface HelmetProps {
title: string;
description: string;
}
function ReactHelmet({ title, description }: HelmetProps) { return (
<Helmet>
<title>{title}</title>
<meta property="og:title" content={title} />
<meta
property="og:image"
content="https://thepracticaldev.s3.amazonaws.com/i/6hqmcjaxbgbon8ydw93z.png"
/>
<meta name="description" content={description} />
</Helmet> ); }
export default ReactHelmet;
Title is correctly appplied as below for hi. But image is not changed with below code. My goal is to change with relative path.
(But not working even with absolute path as below.)
<meta
property="og:image"
content="https://thepracticaldev.s3.amazonaws.com/i/6hqmcjaxbgbon8ydw93z.png"
/>
What is the problem here?

favicon on safari and ios platforms doesn't appear

I am trying to change the favicon dynamically on all browsers, I get a response from the backend containing an https link to the icon, I am using react-helmet to change the favicon but it only works on chrome and android and doesnt work on safari, any ideas? please check out the code below:
import React from "react";
import { Helmet } from "react-helmet";
import { FavIconProps } from "./FavIconProps.types";
export const FavIcon = (props: FavIconProps) => {
const { faviconLink } = props;
console.log(faviconLink);
return (
<Helmet>
<meta name="favicon" />
<meta data-react-helmet="true" content="yes" name="apple-mobile-web-app-capable" />
<meta
data-react-helmet="true"
name="apple-mobile-web-app-status-bar-style"
content="black-translucent"
/>
<link id="favicon" rel="icon" data-react-helmet="true" href={faviconLink} />
<link
rel="apple-touch-icon"
data-react-helmet="true"
sizes="180x180"
href={faviconLink} />
</Helmet>
);
};
export default FavIcon;

Razzle with React Helmet: Meta tags displayed wrong in crawlers when using dynamic values from Axios

I'm using Razzle for using React and Server Side Rendering with React Helmet. I have this problem when you use React Helmet to set meta tags with dynamic values, it's not displayed correctly. But it works if you set the meta tags with static values.
Please take a look at some codes.
SEO.js Component
import React, { Component } from 'react';
import { Helmet } from "react-helmet-async";
class SEO extends Component {
constructor(props) {
super(props);
this.state = {
title: this.props.title,
description: this.props.description,
image: this.props.image
}
}
shouldComponentUpdate(nextProps) {
if(this.props != nextProps) {
this.setState({
title: nextProps.title,
description: this.props.description,
image: nextProps.image
})
return true;
} else {
return false;
}
}
render() {
return (
<div>
<Helmet>
<title>{this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}</title>
<meta name="title" content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"} />
<meta
name="description"
content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
/>
<meta
property="og:title"
content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}
/>
<meta
property="og:description"
content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
/>
<meta
property="og:image"
content={this.state.image ? this.state.image : "https://volunteerhub.id/assets/logo/seo.jpg"}
/>
<meta property="og:url" content="https://volunteerhub.id" />
<meta
name="twitter:title"
content={this.state.title ? this.state.title : "Volunteer Hub by Indorelawan"}
/>
<meta
name="twitter:description"
content={this.state.description ? this.state.description : "Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia."}
/>
<meta
name="twitter:image"
content={this.state.image ? this.state.image : "https://volunteerhub.id/assets/logo/seo.jpg"}
/>
<meta name="twitter:card" content="summary_large_image" />
</Helmet>
</div>
);
}
}
export default SEO;
Here is the example of setting up the static meta tags:
import React, {Component} from "react";
import SEO from "../../components/SEO";
class ScheduleContainer extends Component {
constructor(props) { super(props); }
render() {
return(
<div>
<SEO
title="Cek Jadwal | Volunteer Hub by Indorelawan"
description="Cek jadwal kegiatan di Volunteer Hub! Volunteer Hub by Indorelawan adalah tempat kolaborasi antara relawan dan komunitas sosial yang memiliki semangat kerelawanan dan gotong royong untuk Indonesia." />
</div>);
}
}
And here is the example of setting up the dynamic meta tags:
import React, {Component} from "react";
import axios from "axios";
import SEO from "../../components/SEO";
class EventContainer extends Component {
constructor(props) {
super(props);
this.state = {
event: {}
}
}
componentDidMount() {
axios.get('API_URL')
.then(response => {
this.setState({ event: response.data.result })
});
}
render() {
return(
<div>
<SEO
title={this.state.event.title}
description={this.state.event.description} />
</div>);
}
}
Server.js
import RootContainer from "./containers/RootContainer";
import React from "react";
import { StaticRouter } from "react-router-dom";
import express from "express";
import { renderToString } from "react-dom/server";
import { Helmet, HelmetProvider } from "react-helmet-async";
const assets = require(process.env.RAZZLE_ASSETS_MANIFEST);
const server = express();
server
.disable("x-powered-by")
.use(express.static(process.env.RAZZLE_PUBLIC_DIR))
.get("/*", (req, res) => {
const context = {};
const helmetContext = {};
const markup = renderToString(
<HelmetProvider context={helmetContext}>
<StaticRouter context={context} location={req.url}>
<RootContainer />
</StaticRouter>
</HelmetProvider>
);
const { helmet } = helmetContext;
if (context.url) {
res.redirect(context.url);
} else {
res.status(200).send(
`<!doctype html>
<html lang="">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<meta
name="keywords"
content="volunteer, hub, by, indorelawan, volunteer hub, volunteer hub by indorelawan, kolaborasi, dimulai, dari, sini, ubah, niat, baik, jadi, aksi, baik, hari, ini"
/>
<meta name="robots" content="index, follow" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="language" content="Indonesia" />
<meta name="author" content="Indorelawan" />
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="theme-color" content="#222222" />
${helmet.title.toString()}
${helmet.meta.toString()}
${
assets.client.css
? `<link rel="stylesheet" href="${assets.client.css}">`
: ""
}
${
process.env.NODE_ENV === "production"
? `<script src="${assets.client.js}" defer></script>`
: `<script src="${
assets.client.js
}" defer crossorigin></script>`
}
...
</head>
<body>
<div id="root">${markup}</div>
<script>
if ("serviceWorker" in navigator) {
if (navigator.serviceWorker.controller) {
console.log("[PWA Builder] active service worker found, no need to register");
} else {
// Register the service worker
navigator.serviceWorker
.register("pwabuilder-sw.js", {
scope: "./"
})
.then(function (reg) {
console.log("[PWA Builder] Service worker has been registered for scope: " + reg.scope);
});
}
}
</script>
</body>
</html>`
);
}
});
export default server;
Now that you have seen the code, here is the result when I copy pasted to Google SERP Simulator and WhatsApp:
Static meta tags result from Schedule Page:
Google SERP Simulator
WhatsApp
Dynamic meta tags result from Event Page:
Google SERP Simulator
WhatsApp
From the result, it always return the default title and description tag not the title and description passed from the axios. Is is normal behavior or am I doing something wrong?
Razzle IS server side rendering, the problem with your dynamic meta tags scenario is that you're relying on data that is fetched in componentDidMount, and componentDidMount, as a lifecycle method from the commit phase, is not called on the server since there's no actual mounting on server side.
NextJS solves this problem for you because of getInitialProps, which is invoked on server and client.
Turns out Razzle is not Server Side Rendering. Either you have to define the SEO tags using a custom express server, or just use SSR for React.
I am using NextJS for this and this is a no problem.

How to fix gatsby_1 is undefined

I'm trying to use graphquery with react and gatsby but I can't get the data. I could get the data if it's written in js. How can I fix it? Thanks.
I started it from gatsby typesript starter but it doesn't work properly.
Uncaught ReferenceError: gatsby_1 is not defined
at ./src/templates/index-page.tsx.exports.IndexPageTemplate (index-page.tsx:56)
index.js:2177 The above error occurred in the <TemplateWrapper> component:
in TemplateWrapper (created by IndexPage)
React will try to recreate this component tree from scratch using the error boundary you provided, AppContainer.
I changed sitemetadata file from js to tsx and then the error happeend
SiteMetaData.tsx
import { graphql, useStaticQuery } from "gatsby";
const useSiteMetadata = () => {
const { data } = useStaticQuery(
graphql`
query GetSiteMetaData{
site {
siteMetadata {
title
description
}
}
}
`
);
return data.siteMetaData;
};
export default useSiteMetadata;
Layout.tsx
import * as React from "react";
import Helmet from "react-helmet";
import Footer from "../Organisms/Footer";
import Navbar from "../Organisms/Navbar";
import useSiteMetadata from "../Organisms/SiteMetadata";
interface TemplateWrapperProps {
children: React.ReactChild;
}
const TemplateWrapper: React.SFC<TemplateWrapperProps> = ({ children }) => {
const { title, description }: any = useSiteMetadata();
return (
<div>
<Helmet>
<html lang="ja" />
<title>{title}</title>
<meta name="description" content={description} />
<meta name="theme-color" content="#fff" />
<meta property="og:type" content="business.business" />
{/**<meta property="og:title" content={title} /> */}
<meta property="og:url" content="/" />
<meta property="og:image" content="/img/og-image.jpg" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
</Helmet>
<Navbar />
<div>{children}</div>
<Footer />
</div>
);
};
export default TemplateWrapper;
Try to separate graphql import into his own line, worked for me:
import { graphql } from 'gatsby';
import { Link, useStaticQuery } from 'gatsby';
There seem to be an issue open about it on gatsby-plugin-ts-loader repository: https://github.com/AdamLeBlanc/gatsby-plugin-ts-loader/issues/1#issuecomment-453876850

Resources