I want to disable a chat widget on my page /embed/. The widget is loaded in _document.js. I am familiar useRoute or withRouter to disable a component. Example
import React, { Component } from "react";
import Link from "next/link";
import { withRouter } from "next/router";
class Footer extends Component {
render() {
const pathname = this.props.router.pathname;
if (pathname.startsWith("/embed/")) {
return null;
} else {
return (
<div className='footer container'>
My footer displayed on all pages expect on /embed/
</div>
);
}
}
}
export default withRouter(Footer);
But unfortunately this doesn't work in _document.js and keep getting internal error 500.
import Document, { Head, Main, NextScript } from "next/document";
import { GA_TRACKING_ID } from "../lib/gtag";
import WidgetChat from "../lib/WidgetChat";
import { withRouter } from "next/router";
class MyDocument extends Document {
render() {
const pathname = this.props.router.pathname;
if (pathname.startsWith("/embed/")) {
return null;
} else {
return (
<html>
<Head>
{/* Global Site Tag (gtag.js) - Google Analytics */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}', {
page_path: window.location.pathname,
});
`,
}}
/>
</Head>
<body>
<Main />
<NextScript />
<WidgetChat />
</body>
</html>
);
}
}
}
export default withRouter(MyDocument);
Any advice?
Many thanks
Frido
Related
I have deployed the next js app to the server using vercel. I have referenced the two google fonts in _document.js. While I am running the app locally both font load without any problem.
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>
<link href="https://fonts.googleapis.com/css2?family=Crete+Round&family=Work+Sans:wght#500;600&display=swap" rel="stylesheet" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
index.js
import Head from "next/head";
import Script from "next/script";
import Banner from "../components/Banner";
import { fetchAPI } from "../lib/api";
import Articles from "../components/Articles";
export default function Home({ articles })
{
return (
<>
<Head>
<title>Life Sciencify - Explore the mystery of life with Science! </title>
</Head>
<Articles articles={articles} />
</>
);
}
export async function getServerSideProps()
{
const [articlesRes] = await Promise.all([
fetchAPI("/posts", { populate: ["cover", "category"] })
]);
console.log(articlesRes)
return {
props: {
articles: articlesRes.data
}
};
}
app.js
import Script from "next/script";
import "bootstrap/dist/css/bootstrap.css";
import "../styles/globals.css";
import { useEffect } from "react";
import Header from "../components/Header";
import SearchBlock from "../components/SearchBlock";
import Footer from "../components/Footer";
function MyApp({ Component, pageProps })
{
useEffect(() =>
{
import("bootstrap/dist/js/bootstrap");
}, []);
return (
<>
<Component {...pageProps} />
</>
);
}
export default MyApp;
After the deployment it is showing the weird behavior.
Initially When I am in the home page the page doesn't load any font.
Now, when I click the link Post1 or Post 2, it will be redirected to the detail page.
at first font is not loaded in this page too.
Now, after the page refresh the font gets loaded.
Now, when I go to the back page in the browser, the home page will have the font loaded. But again when the page is refreshed the font will be gone.
What is the causing the weird behavior?
I am running the application in the next js version of "12.1.6".
Referenced:
google-font-display
font-optimization
In the _document.js i used two google fonts separately and it is working now.
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>
<link href="https://fonts.googleapis.com/css2?family=Work+Sans:wght#500;600&display=swap" rel="stylesheet" />
<link href="https://fonts.googleapis.com/css2?family=Crete+Round&display=swap" rel="stylesheet" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
)
}
}
export default MyDocument
and in index.js, change server side rendering to static props:
export async function getStaticProps()
{
const [articlesRes] = await Promise.all([
fetchAPI("/posts", { populate: ["cover", "category"] })
]);
console.log(articlesRes)
return {
props: {
articles: articlesRes.data
}
};
}
After this changes I deployed to vercel it worked fine, again after some time i changes to getServerSideProps, it was not working. So, the culprit was getServerSideProps with google font.
I am new to next js. In my project I need to display youtube videos. I have an api which provides me the video ids to show with its meta details. I wanted to create dynamic pages for each videos. I am using react-player as player.
Here is my code
[videoId].tsx
import Head from 'next/head';
import { useRouter } from 'next/router'
import Layout from '../../components/layout';
import { IVideoItem } from '../../models/videos.model';
import VideoContainer from '../../components/videos-page/video-container';
import { getVideosPaths, getVideosPageTitle, getVideosPageDescription, getVideosData } from '../../services/videos-page.services';
export default function VideoPage({videoInfo} :IVideosPageProp) {
const router = useRouter()
if (router.isFallback) {
return <div>Loading...</div>
}
return(
<>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
<title>{getVideosPageTitle(videoInfo)}</title>
<meta name="description" content={getVideosPageDescription(videoInfo)} />
<meta property="og:title" content={getVideosPageTitle(videoInfo)} key="ogtitle" />
<meta property="og:description" content={getVideosPageDescription(videoInfo)} key="ogdesc" />
</Head>
<VideoContainer data={videoInfo} />
</>
)
}
export async function getStaticPaths() {
const paths = await getVideosPaths()
//console.log('paths: ',paths);
return {
paths,
fallback: false
}
}
export async function getStaticProps({ params }:IVideosPageStaticProp) {
const {videoId} = params;
const videoInfo = await getVideosData(videoId)
return {
props: {
videoInfo
}
}
}
interface IVideosPageProp {
videoInfo: IVideoItem
}
interface IVideosPageStaticPropParams {
videoId: string
}
interface IVideosPageStaticProp {
params: IVideosPageStaticPropParams
}
video-container.tsx
import { Row, Col } from 'react-bootstrap'
import { IVideoItem } from '../../models/videos.model';
import styles from './videos-container.module.scss';
import VideoTag from '../home/videos-block/video-tag';
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome';
import { faEye, faThumbsUp, faThumbsDown } from '#fortawesome/free-solid-svg-icons';
import moment from 'moment';
import dynamic from 'next/dynamic';
const ReactPlayer = dynamic(
() => import('react-player'),
{ loading: () => <p>...</p>, ssr: false }
)
export default function VideoContainer({data} :IVideosPageProp){
const videoInfo:IVideoItem = data;
const videoTag = [{"tagName": "Foo", "tagId": 1}]
const fallBackElement = () => {
return <img src={videoInfo.default_thumbnail_url} width="100%"/>
}
return (
<div className={styles['videos-container']}>
<ReactPlayer
url={`https://youtu.be/${data.video_id}`}
controls
width = "100%"
light={true}
playing={true}
fallback={fallBackElement()}
config={{
youtube: {
playerVars: { showinfo: 1 }
}
}}
/>
<div className={styles['videos-body']}>
<div className={styles['tag-list-container']}>
{videoTag.map((tag, index) =>{
return <VideoTag videoTag={tag} key={index}/>
})}
</div>
<div className={styles['video-title']}>
{videoInfo.title}
</div>
<Row className={styles['video-numbers']}>
<Col md={2} xs={2}><FontAwesomeIcon icon={faEye} className={styles['views-icon']} />{videoInfo.views_count}</Col>
<Col md={2} xs={4}>{moment(new Date(videoInfo.published_at)).format('Do MMMM YYYY')}</Col>
<Col md={4} xs={2}></Col>
<Col md={2} xs={2}><FontAwesomeIcon icon={faThumbsUp} className={styles['views-icon']} />{videoInfo.like_count}</Col>
<Col md={2} xs={2}><FontAwesomeIcon icon={faThumbsDown} className={styles['views-icon']} />{videoInfo.dislike_count}</Col>
</Row>
<div className={styles['video-description']}>
{videoInfo.description}
</div>
</div>
</div>
)
}
interface IVideosPageProp {
data:IVideoItem
}
When I run yarn dev the page is loading properly and the video player is rendering and working as expected. But when I run next buld and after that next start, the page is loading, but player is not loading. Insted it shows the "Loading..." message on the page, I refreshed several times, no luck. Not able to understand the issue. Can any one help?
Update 1:
The page is rendering with video title, video description etc. But the dynamically imported video player is not rendered. At the place of video player, it shows 'Loading...'.
Not sure if you can dynamically load from the node_module, like this:
const ReactPlayer = dynamic(
() => import('react-player'),
{ loading: () => <p>...</p>, ssr: false }
)
But you should be able to do this by creating a react-player component first, then dynamic import it like this:
// create a component named Player.js
import ReactPlayer from 'react-player';
const Player = props => (<ReactPlayer {...props}/>)
export default Player;
// then dynamic import it:
const Player = dynamic(
() => import('../components/Player'),
{ ssr: false }
)
// Then use <Player> with the same props
I am using NextJS and when I use direct css import ...css alongside a styled-component createGlobalStyle. My direct css imports are always included last in my html causing some overriding issues that I've setup in GlobalStyles.
How can I resolve this?
rendered html
<head>
<link href="/_next/static/chunks/main.js?ts=1609163560022">
<style></style> // global styled component
<style></style> // bootstrap imported after global styled component
</head>
_app.js
import { ThemeProvider } from "styled-components";
import 'bootstrap/dist/css/bootstrap.min.css' // this is always loaded last <head>
import GlobalStylesfrom 'styles/global'; // this is always loaded first in <head>
const theme = {
...
};
export default function App({ Component, pageProps }) {
return (
<ThemeProvider theme={theme}>
<GlobalStyles/>
<Component {...pageProps} />
</ThemeProvider>
)
}
_document.js
import Document, { Html, Head, Main, NextScript } from "next/document";
import { ServerStyleSheet } from "styled-components";
export default class MyDocument extends Document {
static getInitialProps({ renderPage }) {
const sheet = new ServerStyleSheet();
const page = renderPage(App => props =>
sheet.collectStyles(<App {...props} />)
);
const styleTags = sheet.getStyleElement();
return { ...page, styleTags };
}
render() {
return (
<Html>
<Head>{this.props.styleTags}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
I have a problem with my script, I am using next and React in my pages / _app.js file I have the following code:
import axios from "axios";
import { $ } from "jquery";
import App from "next/app";
import Router from "next/router";
import { destroyCookie, parseCookies } from "nookies";
import Layout from "../components/_App/Layout";
import "../public/css/boot.css";
import "../public/css/icons.css";
import "../public/css/themes/style.css";
import "../public/jquery";
import "../public/scripts";
import baseUrl from "../utils/baseUrl";
class MyApp extends App {
static async getInitialProps({ Component, ctx }) {
const { token } = parseCookies(ctx);
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
if (!token) {
const isProtectedRoute =
ctx.pathname === "/profile" ||
ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/my-orders-history" ||
ctx.pathname === "/admin/users" ||
ctx.pathname === "/admin/dashboard";
if (isProtectedRoute) {
redirectUser(ctx, "/auth/login");
}
} else {
try {
const payload = { headers: { Authorization: token } };
const url = `${baseUrl}/api/account`;
const response = await axios.get(url, payload);
const user = response.data;
const isRoot = user.role == "root";
const isAdmin = user.role == "admin";
// if authenticated but not root or admin
const isNotPermitted =
!(isRoot || isAdmin) &&
(ctx.pathname === "/admin/add-product" ||
ctx.pathname === "/admin/customers" ||
ctx.pathname === "/admin/orders" ||
ctx.pathname === "/admin/dashboard");
if (isNotPermitted) {
redirectUser(ctx, "/products");
}
pageProps.user = user;
} catch (error) {
// console.error("Error getting current user", error);
//invalid token
destroyCookie(ctx, "token");
redirectUser(ctx, "/auth/login");
}
}
return { pageProps };
}
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
require($)(window);
}
syncLogout = e => {
if (e.key === "logout") {
Router.push("/");
}
};
render() {
const { Component, pageProps } = this.props;
return (
<Layout {...pageProps}>
<Component {...pageProps} />
</Layout>
);
}
}
export default MyApp;
In my components/_App/Layouts.js tenho o seguinte codigo:
import React from "react";
import Head from "next/head";
import Footer from "./Footer";
import StaticHeader from "./StaticHeader";
const Layout = ({ children, user }) => {
return (
<React.Fragment>
<Head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WdpShoes | Home</title>
<link rel="preconnect" href="https://fonts.gstatic.com" />
<link
href="https://fonts.googleapis.com/css2?family=Open+Sans:wght#400;700;800&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="./public/css/icons.css" />
<link href="https://file.myfontastic.com/gMNiHf8HU5GG4r6y622k2N/icons.css" rel="stylesheet" />
<link rel="stylesheet" href="/css/boot.css" />
<link rel="stylesheet" href="/css/themes/style.css" />
<link rel="shortcut icon" href="/css/themes/logo/favicon.png" />
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</Head>
<body>
<StaticHeader user={user} />
{children}
<Footer />
<script src="/jquery.js"></script>
<script src="/scripts.js"></script>
<script src="/css/themes/script.js"></script>
</body>
</React.Fragment>
);
};
export default Layout;
In the Components that are in my Layout the jquery and javascript are working but in my components they are not
When I import my script into my pages / _app.js file I encounter the following error:
ReferenceError: $ is not defined
at Object../public/scripts.js (C:\Users\walter\Desktop\mystore.next\server\pages_app.js:5217:1)
I looked for some materials and I didn't find anything that could help me solve this problem that is preventing me from developing several projects
I know the purpose of the site is to answer questions but to better explain my problem I have the complete code at:
https://codesandbox.io/s/dawn-mountain-vme5e?file=/pages/_app.js
And in its current state it encounters the Internal Server Error due to the fact that the $ function is returning an undefined,Because my script and jquery are not working correctly in my components
To understand your issue, a little theory is needed. Next.js uses server-side rendering and static generation, which means the all code is initially run on the server. jQuery is a client-side library and it can't run on the server. Therefore, you need to ensure that all the jQuery code is executed only on the client side. You can do this by checking if the window object exists and only then executing jQuery. Also, you've forgotten to include jQuery in your scripts.js file.
Modify the
scripts.js file:
import $ from "jquery";
if (typeof window !== "undefined") {
$(function () {
// Rest of the code...
}
}
Next, modify the componentDidMount function in _app.js:
componentDidMount() {
window.addEventListener("storage", this.syncLogout);
}
What I have
My _document file:
import NextDocument, { Head } from 'next/document';
const GTM_TRACKING_ID = 'GTM-ID';
class WebAppDocument extends NextDocument {
render() {
return (
<html lang="es">
<Head>
{ /* Global Tag Manager (gtm.js) */}
{ /* => load GTM scripts according to environment variable */ }
<script dangerouslySetInnerHTML={{ __html: `window.dataLayer = window.dataLayer || []` }} />
<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_TRACKING_ID}');
`,
}}
/>
</Head>
</html>
);
}
}
My _app file:
import NextApp from 'next/app';
const pushEvent = ({ event, pagePath, pageTitle }) => {
window.dataLayer.push({
event,
pagePath,
pageTitle,
});
};
class WebApp extends NextApp {
componentDidMount() {
// push data to Google Tag Manager
pushEvent({
event: 'PageView',
pagePath: window.location.pathname + window.location.search + window.location.hash,
pageTitle: document.title,
});
}
}
What I want
I've seen the example and used Router.events.on('routeChangeComplete', url => dataLayer.push({})) to execute dataLayer. Is this the best approach? Or is it better to run the code to populate the dataLayer in componentDidMount.
I want to load GTM scripts according to environment variable. How could this be achieved?