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
Related
I want to set category data at asyncData() hook. But MainHeader Page Component never calls asyncData even if it is placed in a page. Can you explain why MainHeader Page Component does not call asyncData?
MainHeader is placed inside "com" folder which is placed on pages (/pages/com/MainHeader)
<template>
<div>
<header-nav :cateList="cateList"/>
</div>
</template>
<script>
import HeaderNav from '~/components/com/nav/HeaderNav.vue';
import CateApi from "~/util/api/category/cate-api";
export default {
components: {HeaderNav},
async asyncData(){
const cateList = await CateApi.getDispCateList();
return{
cateList,
}
},
data() {
return {
cateList: [],
}
},
}
</script>
default
(/layouts/default)
<template>
<div>
<main-header/>
<Nuxt/>
</div>
</template>
<script>
import MainHeader from "~/pages/com/MainHeader.vue"
export default {
components :{
MainHeader,
},
name: "defaultLayout"
}
</script>
You're probably reaching your page directly, something like /com/test-page I guess, there you will get first initial result on the server (you can check, you'll be getting a console.log in there), which is legit because this is how Nuxt works (server-side first then client-side).
Please follow the convention of naming your pages like my-cool-page and not myCoolPage and also keep in mind that asyncData works only inside of pages.
Your project is working perfectly fine, as an example, create the following file /pages/com/main-header.vue
<template>
<div>
<p> main header page</p>
<header-nav :cate-list="cateList" />
</div>
</template>
<script>
import HeaderNav from '~/components/com/nav/HeaderNav.vue';
export default {
components: { HeaderNav },
async asyncData() {
console.log("check your server if accessing this page directly, otherwise you'll see this one in your browser if client-side navigation")
const response = await fetch('https://jsonplaceholder.typicode.com/todos')
const cateList = await response.json()
return { cateList }
},
}
</script>
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>
</>
)
}
I have open source library that I want to use. the library wrote in clean vanilla js:
follow their docs, if I want to use the library:
<html>
<head>
<script src="./jquery-2.0.3.min.js"></script>
<script src="./kinetic-v5.1.0.min.js"></script>
<script src="./inchlib-1.2.0.js"></script>
<script>
$(document).ready(function() { //run when the whole page is loaded
var inchlib = new InCHlib({"target": "inchlib",
"width": 800,
"height": 1200,
"column_metadata_colors": "RdLrBu",
"heatmap_colors": "RdBkGr",
"max_percentile": 90,
"middle_percentile": 60,
"min_percentile": 10,
"heatmap_font_color": "white",
text: 'biojs'});
inchlib.read_data_from_file("/microarrays.json");
inchlib.draw();
inchlib.onAll(function(name){
console.log(name + " event triggered");
});
});
</script>
</head>
<body>
<div class="heatmaps" style="margin:auto; align-items: center; margin-left:25%;">
<div id="inchlib"></div>
</div>
<div ></div>
</body>
</html>
The file inchlib-1.2.0.js contains the main logic and js code. I want to build react project and use this library there. How can I achieve this goal?
import React, { Component } from 'react';
import './App.css';
export default class App extends Component {
render () {
return (
<div>
<div>
</div>
</div>
)
}
}
You can create custom hook with useEffect. In useEffect you should paste your code. You can insert html elements, add event listeners and so on.
useLibrary.js
import { useEffect } from "react";
const useLibrary = () => {
useEffect(() => {
$.getScript("inchlib-1.2.0.js", function(){
var inchlib = new InCHlib({"target": "inchlib",
"width": 800,
"height": 1200,
"column_metadata_colors": "RdLrBu",
"heatmap_colors": "RdBkGr",
"max_percentile": 90,
"middle_percentile": 60,
"min_percentile": 10,
"heatmap_font_color": "white",
text: 'biojs'});
inchlib.read_data_from_file("/microarrays.json");
inchlib.draw();
inchlib.onAll(function(name){
console.log(name + " event triggered");
});
});
}, []);
};
export default useLibrary;
App.js
import useLibrary from ".useLibrary";
export default class App extends Component {
useLibrary();
render () {
return (
<div>
<div class="heatmaps" style="margin:auto; align-items: center; margin-left:25%;">
<div id="inchlib"></div>
</div>
</div>
)
}
}
But I warn you that this is a big crutch.
Depends on what you're gonna do with the library you want to integrate with. Checkout this as a base reference: Integrating with other libraries.
If you're gonna manipulate DOM elements you'll gonna need a reference to them. In this case checkout this: Refs and the DOM.
If the library provides some general logic, you have no problem using it anywhere throughout your code or more specifically in effects.
As inchlib is a visual element library, you'll need to go the first route and get a reference to a specific DOM element. As already noted, checkout Refs from react docs.
Alternative solution is to wrap the whole library usage in your own react component.
Well If I were to do the same thing then I would paste the script tags as you've done in your html file
<head>
<script src="./jquery-2.0.3.min.js"></script>
<script src="./kinetic-v5.1.0.min.js"></script>
<script src="./inchlib-1.2.0.js"></script>
<script>
</head>
For accessing an object into react app, Create a file named Inchlib.js in same directory as is your app.js
Contents of Inchlib.js should be
export default window.InCHlib;
Import the default export into your app.js
import InCHlib from "./inchlib";
function App() {
console.log(InCHlib); // prints the InCHlib object
return "hello";
}
Note: Although this should work, there might be a better way to do this. Also using global objects in react code is not usually a preferred option.
Hopefully this would help.
Just add the Libraries and Scripts you want in the public/index.html file in your react project.
create loadScript function:
function loadScript(src, position, id) {
if (!position) {
return;
}
const script = document.createElement('script');
script.setAttribute('async', '');
script.setAttribute('id', id);
script.src = src;
position.appendChild(script);
}
in Component:
export default function GoogleMaps() {
const loaded = React.useRef(false);
if (typeof window !== 'undefined' && !loaded.current) {
if (!document.querySelector('#google-maps')) {
loadScript(
'https://maps.googleapis.com/maps/api/js?key=AIzaSyBwRp1e12ec1vOTtGiA4fcCt2sCUS78UYc&libraries=places',
document.querySelector('head'),
'google-maps',
);
}
loaded.current = true;
}
}
now you can access window.google
here is a example
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}
/>
For simple html projects i can simple refer this link.
https://www.w3schools.com/howto/howto_google_translate.asp
But I'm trying to implement in react app . So I'm not able to replicate the code in react app.
componentDidMount() {
googleTranslateElementInit(() => {
new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element');
});
const script = document.createElement("script");
script.src = "//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit";
script.async = true;
document.body.appendChild(script);
}
And return render element .
render() {
return (
<div id="google_translate_element"></div>
);
}
This is showing me error saying google , googleTranslateElementInit is not defined.
How can I use google translator in react app ?
Also is there any npm packages which can translate whole site ?
Thanks
Move your google translate script to the root index.html of your project.
However, you should leave the below code at your desired location:
render() {
return (
<div id="google_translate_element"></div>
);
}
Fixes the problem easily.
Change render to:
render() {
return (
<script type='text/javascript' src='//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit' />
<div id="google_translate_element"></div>
);
}
Create googleTranslateElementInit and use window.google instead of google:
googleTranslateElementInit () {
/* eslint-disable no-new */
new window.google.translate.TranslateElement({pageLanguage: 'pt', layout: window.google.translate.TranslateElement.FloatPosition.TOP_LEFT}, 'google_translate_element')
}
Change componentDidMount to:
componentDidMount () {
window.googleTranslateElementInit = this.googleTranslateElementInit
}
For those in 2021 and hopefully a few more years before Google decides to change implementation method, this is how I resolved it.
Add the below script to your index.html file found in the public directory:
<script src="https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit" async></script>
<script type="text/javascript">
function googleTranslateElementInit() {
new google.translate.TranslateElement(
{
pageLanguage: "en",
layout: window.google.translate.TranslateElement.InlineLayout.VERTICAL,
},
'google_translate_element'
);
}
</script>
Then, create a component, to be imported anywhere you want to use the translate plugin, with any name of your choice. I will use GoogleTranslate.jsx for this purpose of this answer.
In the newly created component, paste this code:
import React, { useEffect } from "react";
const GoogleTranslate = () => {
useEffect(() => {
// in some cases, the google translate script adds a style to the opening html tag.
// this added style disables scrolling.
// the next 3 lines removes this added style in order to re-enable scrolling.
if (window.document.scrollingElement.hasAttribute("style")) {
window.document.scrollingElement.setAttribute("style", "");
}
});
return (
<div id="google_translate_element"></div>
);
};
export default GoogleTranslate;
Import the component wherever you want to use the translate plugin.
If this solution worked for you, kindly up vote so it can easily be shown to others searching. If it didn't, don't hesitate to drop a comment
Go to public folder > index.html
add code in body tag
<script type="text/javascript">
function googleTranslateElementInit() {
new google.translate.TranslateElement({pageLanguage: 'en', layout: google.translate.TranslateElement.InlineLayout.SIMPLE}, 'google_translate_element');
}
</script><script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
create component
import React from 'react';
import './style.css';
const GoogleTranslate = (props) => {
return(
<div id="google_translate_element"></div>
)
}
export default GoogleTranslate
import GoogleTranslate from './GoogleTranslate';
<GoogleTranslate />