We have created custom _document.js page in page folder .So that we can easily add GTM(Google tag Manager) in body and head.
_document.js
import React from 'react'
import Document, { Head, Main, NextScript } from 'next/document'
import flush from 'styled-jsx/server'
import PropTypes from 'prop-types';
const defaultDescription = ''
const defaultOGURL = ''
const defaultOGImage = ''
export default class MyDocument extends Document {
static getInitialProps ({ renderPage }) {
const {html, head, errorHtml, chunks} = renderPage()
const styles = flush()
return { html, head, errorHtml, chunks, styles }
}
render () {
return (
<html>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>{this.props.title || ''}</title>
<meta name="description" content={this.props.description || defaultDescription} />
<meta name="keywords" content={this.props.keywords || ''} />
<meta name="robots" content="INDEX,FOLLOW" />
<meta name="HandheldFriendly" content="true" />
<link rel="canonical" href={this.props.canonical || ''} />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap-theme.min.css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" />
<script dangerouslySetInnerHTML={{__html: `(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXX');`}} />
</Head>
<body className="custom_class">
{this.props.customValue }
<Main />
<NextScript />
</body>
</html>
)
}
}
MyDocument.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
url: PropTypes.string,
ogImage: PropTypes.string
}
Because previously I was directly using next/head custom component and it was working properly for each page.But now to use custom addition in Body and Head tag I am using the concept of _document.js page .So need help on how to pass custom props (Title,description,keywords,canonical) as per page navigation
any luck?
You can't since _document.js is only rendered server side on the first page load. You can use a layout component to append stuff to the body on every page / page load.
hi @aaawtest && @Rupeshn73
What you could do is to return query and pathname from getInitialProps and then use those in your render function since they will be part of the props object.
static async getInitialProps({ renderPage, query, pathname }) {
const page = renderPage();
const styles = renderStatic(() => page.html);
return { ...page, ...styles, query, pathname };
}
render() {
const {pathname, query} = this.props;
....
}
Just define the info that will not change per page inside the <head> in _document and define the ones that do will change inside every page/component using Head component from next/head
@aaawtest To fix this the workaround that I have done was.
In page file I have added below code.
`static async getInitialProps(props) {
const { pathname, query, asPath } = props;
return {title:config.title,description:config.description,canonical:config.canonical,keywords:config.keywords}
}`
And to fetch that information in _document file I have used below code.
`
<meta name="description" content={this.props.__NEXT_DATA__.props.description || defaultDescription} />
<meta name="keywords" content={this.props.__NEXT_DATA__.props.keywords || ''} />
<meta name="robots" content="INDEX,FOLLOW" />
<meta name="HandheldFriendly" content="true" />
<link rel="canonical" href={this.props.__NEXT_DATA__.props.canonical || ''} />`
I did what @epicallan suggested and it works. But still when the user navigates the app, from client-side you need to set the title too. Thanks.
renderPage method returns array of head object defined by next/head on pages
I highly recommend using pages/_app.js combined with next/head for this.
i am new on nextjs. Any exmample for solving this issue?
I accomplished this by adding a meta tag to my Layout.js file like so:
<meta name="language" content={props.page.language.language_code} />
Then in _document.js I did this:
`import Document, { Head, Main, NextScript } from 'next/document'
let mainLang = 'en'
const getLang = (lng) => {
mainLang = lng;
return;
}
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
initialProps.head.forEach((value, index)=>{
if(value.type === 'meta' && value.props.name === 'language') {
getLang(value.props.content);
}
})
return { ...initialProps }
}
render() {
return (
<html lang={mainLang}>
<Head>
<style>{`body { margin: 0 } /* custom! */`}</style>
</Head>
<body className="custom-class">
<Main />
<NextScript />
</body>
</html>
)
}
}`
Adding tags seems to be the only way to pass this information upstream - and it works great for me!
Most helpful comment
Just define the info that will not change per page inside the
<head>in_documentand define the ones that do will change inside every page/component usingHeadcomponent fromnext/head