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>
);
}
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;
I would like to embed this code on my Gatsby site, what would be the best practice? I'm still learning and any help would be much appreciated.
Thanks
<script
id="nc-booking-widget-script"
src="https://www.neatcal.com/app/services/booking/assets/js/integrate.js?v=21.04.26.5"
data-access-key="fcd3a2ae209d9ccae0d5fb3e66e9dabc"
data-nc-root="https://www.neatcal.com/app"
data-booking-url="https://www.neatcal.com/app/services/booking/book?u=fcd3a2ae209d9ccae0d5fb3e66e9dabc&widget_order=all_services,time&photo_mode=0&click_and_go=1&class_list_tmpl=list-1"></script>
The easiest and native approach, not only in Gatsby, but generally in React is using the Helmet component. Basically, whatever is wrapped inside this component is outputted in the <head> tag, no matter the original component.
To use it, just install (via yarn add react-helmet or npm install --save react-helmet) it and use like:
import React from "react";
import {Helmet} from "react-helmet";
const HomePage = (props)=> {
return (
<div className="application">
<Helmet>
<meta charSet="utf-8" />
<title>My Title</title>
<link rel="canonical" href="http://someSite.net/example" />
<script id="nc-booking-widget-script" src="https://www.neatcal.com/app/services/booking/assets/js/integrate.js?v=21.04.26.5" data-access-key="fcd3a2ae209d9ccae0d5fb3e66e9dabc" data-nc-root="https://www.neatcal.com/app" data-booking-url="https://www.neatcal.com/app/services/booking/book?u=fcd3a2ae209d9ccae0d5fb3e66e9dabc&widget_order=all_services,time&photo_mode=0&click_and_go=1&class_list_tmpl=list-1"/>
</Helmet>
...
</div>
);
};
In Gatsby, in addition, you can customize the resultant HTML (created once build) by:
cp .cache/default-html.js src/html.js
This will copy the default-html.js file from .cache to src/html.js. There, you can embed your script like:
import React from "react"
import PropTypes from "prop-types"
import React from "react"
import PropTypes from "prop-types"
export default function HTML(props) {
return (
<html {...props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<script
id="nc-booking-widget-script"
src="https://www.neatcal.com/app/services/booking/assets/js/integrate.js?v=21.04.26.5"
data-access-key="fcd3a2ae209d9ccae0d5fb3e66e9dabc"
data-nc-root="https://www.neatcal.com/app"
data-booking-url="https://www.neatcal.com/app/services/booking/book?u=fcd3a2ae209d9ccae0d5fb3e66e9dabc&widget_order=all_services,time&photo_mode=0&click_and_go=1&class_list_tmpl=list-1"></script>
{props.headComponents}
</head>
<body {...props.bodyAttributes}>
{props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
{props.postBodyComponents}
</body>
</html>
)
}
HTML.propTypes = {
htmlAttributes: PropTypes.object,
headComponents: PropTypes.array,
bodyAttributes: PropTypes.object,
preBodyComponents: PropTypes.array,
body: PropTypes.string,
postBodyComponents: PropTypes.array,
}
Both are valid and cleanest approaches.
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)
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