In NextJs application, I want to add <script> code but not getting where to add and how? I tried by adding it in .js file but didn't recognise.
In react, we have index.html to add these things, what about in Nextjs app?
To edit the index.html, the equivalent in NextJS is the _document.js file.
A custom Document is commonly used to augment your application's <html> and <body> tags.
import Document, { Html, Head, Main, NextScript } from 'next/document'
class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
render() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
<script>...</script>
</body>
</Html>
)
}
}
If you only want to append elements to <head> tag (for a specific page), use next/head:
import Head from 'next/head'
function IndexPage() {
return (
<>
<Head>
<script>....</script>
</Head>
</>
)
}
Related
I am working with Reactjs (Nextjs),I have "Home page" and few other pages (about,services...etc),For integrate in Nextjs, I created "_document.js",Problem is there is no class attached with" tag" in home page but class added on "about, service and rest of page", so how can i add "class" according to page? In other words i want to add class "main-layout inner_page" on all pages except "home page",How can i do this ? Here is my "_document.js" file
import { Html, Head, Main, NextScript,link } from 'next/document'
import { fileURLToPath } from 'url'
export default function Document() {
return (
<Html>
<Head>
<link rel="stylesheet" href="css/bootstrap.min.css" />
<link rel="stylesheet" href="css/style.css" />
</Head>
<body>
<Main />
<NextScript />
<script src="/js/jquery.min.js"></script>
</body>
</Html>
)
}
I would specify the layout that I use per page:
NextJS Layout per-page
But you can also pass props to your Layout from _app or from your page, example:
(Not so recommended approaches)
const CustomApp: FC<AppProps> = ({ Component, pageProps, router }) => {
return (
<MainLayout
hasContainer={pageProps.hasContainer}
hasSecondClass={['/', '/company'].includes(router.pathname)}
>
<Component {...pageProps} />
</MainLayout>
);
};
And your page can change the hasContainer
export const getStaticProps: GetStaticProps = async ({ locale }) => {
return {
props: { hasContainer: false },
};
};
My Current Setup
I have a Google Tag Manager script tag placed in the _document.tsx file nested under the <Head> component:
class MyDocument extends Document {
render() {
return (
<Html>
<Head>
<script
dangerouslySetInnerHTML={{
__html: `<!-- Google Tag Manager -->`,
}}
/>
</Head>
<body>
<noscript
dangerouslySetInnerHTML={{
__html: `<!-- Google Tag Manager (noscript) -->`,
}}
/>
<style
dangerouslySetInnerHTML={{
__html: (this.props as any).globalStyles,
}}
></style>
<Main />
<NextScript />
</body>
</Html>
)
}
}
My Goal
We have certain pages set up that are meant for international customers, and those pages all have a "-INTL" at the end of the url (for example: https://www.mystore.com/product--INTL).
My goal is to have Google Tag Manager NOT render if it is an international link, which contains the "--INTL" string in the URL.
Ideally, I want to wrap the entire Google Tag Manager script into its own component, then conditionally render that based on the URL of the page.
Ideal Setup that doesn't work
GTM Component Code:
class GTMComponent extends React.Component {
let currentUrl;
componentDidMount(){
if(typeof window !== "undefined"){
currentUrl = window.location.href;
}
}
render(){
if(!currentUrl.contains("--INTL")){
return (
<script dangerouslySetInnerHTML={{__html: `<!--
Google Tag Manager Script -->`}}/>
)
}
else {
return (
<></>
)
}
}
}
export default GTMComponent
_document.tsx file with GTMComponent:
import GTMComponent from '#components/common/GTMComponent'
class MyDocument extends Document {
render(){
return(
<HTML>
<Head>
<GTMComponent/>
</Head>
<body>
{/* body code */}
</body>
</HTML>
)
}
}
export default MyDocument
My Problem
I want to be able to access the window object, but due to Server Side Rendering, the window object is never defined.
Additionally, I can't use useEffect since _document.tsx is a class-based component, and I weirdly can't get componentDidMount to work either.
I'm under the impression that the whole point of using SSR in Next.js is to have instant DOM render while react and other scripts are bootstrapped. However, when I throttle my connection, the page is just blank/white until the JS completes loading.
Here you can see the network preview (while throttling) shows the simple <div>LOADING</div> in the preview pane:
But the browser sees nothing until scripts finish loading. It has the same behavior even if we render _app.tsx content (with router, lazy-loaded components etc). CURL to same address shows the desired HTML.
Here's the minimal _document.tsx that's used:
import { GetStaticPaths, GetStaticProps } from 'next';
import Document, { Html, Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const initialProps = await Document.getInitialProps(ctx)
return { ...initialProps }
}
static getStaticProps: GetStaticProps = async () => {
return { props: {} };
};
render() {
return (
<Html lang="en">
<Head />
<body className="bg-gray-100">
<span style={{color: 'black', display: 'block', visibility: 'visible !important'}}>LOADING</span>
<Main />
<NextScript />
</body>
</Html>
);
}
}
Is something hiding the body's elements while scripts load? Am I not understanding how _document works?
Issue persists with all three fallbacks, too true false and blocking
I want to use web components, but I don't want to write all the contents in the same file. How to implement it? person-panel in index.html has been added in the server,not js.
file structure
/index.html
/index.js
/PersonPanel/personPanel.js
/PersonPanel/template.html
index.html
<html>
<head>
<script src="index.js" type="module" />
</head>
<body>
<person-panel></person-panel>
</body>
</html>
index.js
import personPanel from './PersonPanel/personPanel.js';
// other operations that may require a PersonPanel
template.html
<template>
<label class="field"></label>
</template>
personPanel.js
class PersonPanel extends HTMLElement {
constructor() {
super();
this.attachShadow({mode : 'open'});
// how to fetch template?
}
static get observedAttributes() {
return ['field'];
}
attributeChangedCallback(name, oldValue, newValue) {
if(name == 'field'){
this.shadowRoot.querySelector('label.field').innerText = newValue;
}
}
}
window.customElements.define('person-panel', PersonPanel);
export default PersonPanel;
Exploring NextJS a bit for its server side rendering features. It looks really nice and easy to use. I already explored the _document.js file which we can include to overwrite the default. I found the following code in an example:
import Document, { Head, Main, NextScript } from 'next/document'
export default class MyDocument extends Document {
render() {
return (
<html>
<Head>
<link rel="stylesheet" href="/_next/static/style.css" />
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
)
}
}
Now I get it that we are overwriting the current HTML template. But I'm a bit confused regarding the functionality of the Main and Nextscript.
Is Main just a page? What is Nextscript (which script)?
NextScript Class is here
and Main Class is here and I copied the same below. Main renders html/ errorHtml from this.context._documentProps
export class Main extends Component {
static contextTypes = {
_documentProps: PropTypes.any
}
render () {
const { html, errorHtml } = this.context._documentProps
return (
<Fragment>
<div id='__next' dangerouslySetInnerHTML={{ __html: html }} />
<div id='__next-error' dangerouslySetInnerHTML={{ __html: errorHtml }} />
</Fragment>
)
}
}
you can find actual documentation on Custom Document here
For those who will look for an answer to this question in the future...
Component NextScript from 'next/document' takes a list of files from context._documentProps and returns each of them as a element like this:
<script
key={file}
src={`${assetPrefix}/_next/${file}`}
nonce={this.props.nonce}
async
/>
It also takes dynamic imports from context._documentProps and returns them in a similar way:
<script
async
key={bundle.file}
src={`${assetPrefix}/_next/${bundle.file}`}
nonce={this.props.nonce}
/>