This question already has an answer here:
NextJs dynamic open graph meta tags not rendering for facebook debugger
(1 answer)
Closed 11 months ago.
I want to change the Head tag dynamically using next-seo.
Browser validation will reflect NEXTSeo for individual pages, but Twitter, Firebase's card validation tool, etc. will respond to the default next-seo-config.js.
Does anyone know what to do?
Similar problems:
Facebook debugger does not pick up Next.js next-seo meta tags
https://github.com/garmeeh/next-seo/issues/113
https://github.com/garmeeh/next-seo/issues/99
_app.js
import { useEffect } from 'react'
import { useRouter } from 'next/router'
import Script from 'next/script'
import Head from 'next/head'
import * as gtag from '../lib/gtag.js'
import { GA_TRACKING_ID } from '../lib/gtag'
import { Provider } from "react-redux"
import { PersistGate } from 'redux-persist/integration/react'
import redux, { persistor } from '../components/store/redux.js'
import { DefaultSeo } from 'next-seo'
import SEO from '../../next-seo.config'
import Header from '../components/block/Header'
import Footer from '../components/block/Footer'
import { config } from '#fortawesome/fontawesome-svg-core'
import '#fortawesome/fontawesome-svg-core/styles.css'
config.autoAddCss = false
export default function App({ Component, pageProps }) {
const router = useRouter()
useEffect(() => {
const handleRouteChange = (url) => {
gtag.pageview(url)
}
router.events.on('routeChangeComplete', handleRouteChange)
return () => {
router.events.off('routeChangeComplete', handleRouteChange)
}
}, [router.events])
return (
<>
<Head>
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
</Head>
{/* Analytics */}
<Script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<Script
id="analytics"
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,
});
`,
}}
/>
<DefaultSeo {...SEO} />
<Provider store={redux}>
<PersistGate loading={null} persistor={persistor}>
<Header />
<Component {...pageProps} />
<Footer />
</PersistGate>
</Provider>
</>
)
}
_document.js
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 lang="ja">
<Head>
<meta charSet="UTF-8" />
<meta name="theme-color" content="#000000" />
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
Page.js
import { NextSeo } from 'next-seo'
const Page = () => {
const title = "title"
const description = "description"
const url = "https://test.com/"
return (
<div>
<NextSeo
title={title}
description={description}
url={url}
canonical={url}
openGraph={{
ur: url,
title: title,
description: description,
type: "article"
}}
/>
<div>contents</div>
</div>
)
}
export default Page
Self resolved.
The cause of not being reflected was redux-persist.
Reference ↓
https://qiita.com/miyabiya/items/14e4f133d5df5d53cd77
Related
I am a novice coder trying to work through a Typescript/NextJS project that uses Auth0. It is currently set up to take users to a page (code below) that just has a login button that takes you to the Auth0 page.
This is an unnecessary step. How can I edit this to directly take the user to the Auth0 auth page upon navigating to the URL?
import "../styles/stripe.css";
import "../styles/globals.css";
import "../styles/App.css";
import "../styles/index.css";
import "../styles/progress.css";
import { FC, StrictMode } from "react";
import { getCLS, getFCP, getFID, getLCP, getTTFB } from "web-vitals";
import { useUser, UserProvider } from "#auth0/nextjs-auth0";
import type { AppProps } from "next/app";
import { ToastContainer } from "react-toastify";
import { useRouter } from "next/router";
import Head from "next/head";
import Header from "../views/Header";
const SignInRequired: FC = ({ children }) => {
const { user, error, isLoading } = useUser();
const router = useRouter();
if (isLoading) return <div />;
if (error) return <div>{error.message}</div>;
if (!user) {
return (
<>
<div className="container flex-col flex-center">
<h1>AppCo</h1>
<h2>Please sign in.</h2>
<button
className="card-test"
onClick={() => router.push("/api/auth/login")}
>Login</button>
<p>Questions?</p>
<p>hi#appco.com</p>
</div>
</>
);
}
return (
<>
{children}
</>
);
};
export default function App({ Component, pageProps }: AppProps) {
return (
<StrictMode>
<UserProvider>
{/* Default SEO. */}
<Head>
<title>AppCo</title>
<meta name="description" content="AppCo" />
<link rel="icon" href="/favicon.ico" />
</Head>
<div className="w-full">
{/* Max layout width. */}
<Header />
<SignInRequired>
<main className="w-full py-8">
<Component {...pageProps} />
</main>
<ToastContainer
autoClose={2000}
/>
</SignInRequired>
</div>
</UserProvider>
</StrictMode>
);
}
/**
* Use Web Vitals.
*
* #see https://nextjs.org/docs/advanced-features/measuring-performance
*/
export const reportWebVitals = (onPerfEntry) => {
if (onPerfEntry && onPerfEntry instanceof Function) {
getCLS(onPerfEntry);
getFID(onPerfEntry);
getFCP(onPerfEntry);
getLCP(onPerfEntry);
getTTFB(onPerfEntry);
}
};
Currently the code is saying "if not a user, return the sign in button".
Instead of returning that sign in button, you can redirect using Router assuming this is your next.js file.
You'll need to also import router:
import Router from 'next/router'
// or add push to your current line
import { useRouter, push } from "next/router";
if (!user) {
Router.push('/new-route')
}
I'm afraid I'm not a next.js expert but hope this gets the job done for you! Good luck!
I am working on a NextJS site that has some custom styling that is being applied via MaterialUI's makeStyles. It works fine on the first load and then undoes all of the custom work on the second. It seems that it has something to do with the route as the only time that it works is when I first am directed to the page itself. It is happening on 2 different pages one is being directed via href='/login'and the other is being directed via next/router router.push('/register')
I am assuming that this has to do with the way that Next loads the page? But I will say that both of these are prerendered pages according to the icon at the bottom right.
import React, {useState} from 'react';
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import { login } from '../store/user/action'
import { useRouter } from 'next/router'
import TextField from '#material-ui/core/TextField';
import { makeStyles } from '#material-ui/core/styles';
const style = makeStyles({
root: {
marginBottom: '20px',
textAlign: 'center'
},
});
function Signin({login}) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const router = useRouter();
const clickRegister = (e) => {
e.preventDefault();
router.push('/register')
}
const classStyle = style();
return (
<div className='flex flex-column center m-20 w-750'>
<h3 className='m-20'>Login</h3>
<form className='flex flex-column w-750 center' onSubmit={e=>login(e, {email, password})} >
<TextField
className={classStyle.root}
required
type='email'
id="email"
label="Email"
variant="outlined"
onChange={e=>setEmail(e.target.value)}
/>
<TextField
className={classStyle.root}
required
type='password'
id="password"
label="Password"
variant="outlined"
onChange={e=>setPassword(e.target.value)}
/>
<input
type='submit'
className='purple-button mt-20 h-3'
onClick={e=>login(e)}
value='Login' />
</form>
<p>Don't Have an account?</p>
<form onSubmit='/register'>
<input value='Register' type='submit' className='flex flex-column w-750 center purple-button h-3' onClick={e=>clickRegister(e)} />
</form>
</div>
)
}
const mapStateToProps = (state) => ({
email: state.user.email,
token: state.user.token,
isLoggedIn: state.user.isLoggedIn,
})
const mapDispatchToProps = (dispatch) => {
return {
login: bindActionCreators(login, dispatch),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Signin)
"dependencies": {
"#material-ui/core": "4.11.3",
"axios": "0.21.1",
"material-ui": "0.20.2",
"next": "9.4.1",
"next-redux-wrapper": "^6.0.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-redux": "7.1.3",
"redux": "4.0.5",
"redux-devtools-extension": "2.13.8",
"redux-thunk": "2.3.0"
},
You need additional setup for Material-UI stylesheets in pages/_document.js to support SSR styling.
From MaterialUI v5
If you don't have a custom _document yet, create one with the following code:
import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import createEmotionServer from '#emotion/server/create-instance';
import theme from '../src/theme';
import createEmotionCache from '../src/createEmotionCache';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const originalRenderPage = ctx.renderPage;
const cache = createEmotionCache();
const { extractCriticalToChunks } = createEmotionServer(cache);
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => function EnhanceApp(props) {
return <App emotionCache={cache} {...props} />;
}
});
const initialProps = await Document.getInitialProps(ctx);
const emotionStyles = extractCriticalToChunks(initialProps.html);
const emotionStyleTags = emotionStyles.styles.map((style) => (
<style
data-emotion={`${style.key} ${style.ids.join(' ')}`}
key={style.key}
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: style.css }}
/>
));
return {
...initialProps,
emotionStyleTags,
};
}
render() {
return (
<Html lang="en">
<Head>
<meta name="theme-color" content={theme.palette.primary.main} />
<link rel="shortcut icon" href="/static/favicon.ico" />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
{this.props.emotionStyleTags}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
};
Then, in your _app.
import React from 'react';
import Head from 'next/head';
import { ThemeProvider } from '#mui/material/styles';
import CssBaseline from '#mui/material/CssBaseline';
import { CacheProvider } from '#emotion/react';
import theme from '../src/theme';
import createEmotionCache from '../src/createEmotionCache';
const clientSideEmotionCache = createEmotionCache();
export default function MyApp(props) {
const { Component, emotionCache = clientSideEmotionCache, pageProps } = props;
return (
<CacheProvider value={emotionCache}>
<Head>
<meta name="viewport" content="initial-scale=1, width=device-width" />
</Head>
<ThemeProvider theme={theme}>
<CssBaseline />
<Component {...pageProps} />
</ThemeProvider>
</CacheProvider>
);
}
Before MaterialUI v5
If you don't have a custom _document.js yet, create one with the following code:
import React from 'react';
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheets } from '#material-ui/core/styles';
class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) => sheets.collect(<App {...props} />)
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
// Styles fragment is rendered after the app and page rendering finish.
styles: [
...React.Children.toArray(initialProps.styles),
sheets.getStyleElement()
]
};
}
render() {
return (
<Html lang="en">
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;
You can then remove the server-side injected styles in your custom _app.js.
useEffect(() => {
const jssStyles = document.querySelector('#jss-server-side');
if (jssStyles) {
jssStyles.parentElement.removeChild(jssStyles);
}
}, []);
For further details check Material-UI official documentation.
I want to statically generate my navbar, so that it does not need to fetch from client side.
I am using appollo graphql and my current _app.js looks like this:
import React from 'react';
import Head from 'next/head';
import { ApolloProvider } from '#apollo/react-hooks';
import withData from '../utils/apollo';
import Header from '../components/Header';
import '../assets/css/tailwind.css';
const App = (props) => {
const { Component, pageProps, apollo } = props;
return (
<ApolloProvider client={apollo}>
<Head>
<title>...</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta charset="utf-8"></meta>
</Head>
<Header />
<Component {...pageProps} />
</ApolloProvider>
);
};
export default withData(App);
And my Header component looks like this
import React, { useEffect, useState } from 'react';
import Link from 'next/link';
import Query from '../components/query';
import CATEGORIES_QUERY from '../apollo/queries/category/categories';
const Header = () => {
const [isExpanded, setIsExpanded] = useState(false);
const expandedClasses = isExpanded ? 'flex ' : 'hidden ';
return (
<Query query={CATEGORIES_QUERY} id={null}>
{({ data: { categories } }) => {
return (
<React.Fragment>
<nav
className={`${expandedClasses}`}
>
{categories.map((category, i) => {
return (
<Link
href="/category/[cid]"
as={`/category/${category.id}`}
passHref
key={`nav-link-${i}`}
>
<a className="text-gray-800">
{category.name}
</a>
</Link>
);
})}
</nav>
<button
className="flex"
onClick={() => {
setIsExpanded(!isExpanded);
}}
>
<span className="mb-1 bg-orange-500"></span>
<span className="mb-1 bg-orange-500"></span>
<span className="mb-1 bg-orange-500"></span>
</button>
</React.Fragment>
);
}}
</Query>
);
};
export default Header;
Next.js only allows getStaticProps on page components, I am trying to get a similar functionality on my Header Component.
I tried adding getStaticProps on header component and also on _app.js that did not work.
How can I achieve this?
This was posted recently. Also no need to ever import React itself when using next.
https://github.com/vercel/next.js/discussions/10949![enter image description here](https://i.stack.imgur.com/6hfVo.jpg)
I have copied over the _document.js from the next styled components example and I am using the babel plugin from the styled components docs, but I am still getting the error.
_document.js
import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet()
const originalRenderPage = ctx.renderPage
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
})
const initialProps = await Document.getInitialProps(ctx)
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
),
}
} finally {
sheet.seal()
}
}
}
.babelrc
{
"presets": [
"next/babel"
],
"plugins": [
[
"babel-plugin-styled-components"
]
]
}
Just started using next on wednesday so still fairly new with the technology, but I've looked through all the docs and this seems to be what is supposed to work, but I am still getting the error, any thoughts?
You aren't rendering() then returning () any jsx. That's your issue here most likely. You should also be importing Html, Head, Main, and NextScript from "next/document".
To resolve your issue, try refactoring as follows:
import Document { Html, Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
const sheet = new ServerStyleSheet();
const originalRenderPage = ctx.renderPage;
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: (App) => (props) =>
sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
return {
...initialProps,
styles: (
<>
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
};
} finally {
sheet.seal()
}
}
render() {
return (
<Html lang='en'>
<Head>
<meta charSet='utf-8' />
{this.props.styles}
</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
Then in _app.jsx you will import { ThemeProvider } from styled-components. ThemeProvider wraps the custom styled-components GlobalTheme JSX Element imported from another file. The following snippet is in typescript from a project I built a couple months ago. Most is relevant with the exception of the AppProps tidbit:
import Head from "next/head";
import { AppProps } from "next/app";
import { ThemeProvider } from "styled-components";
import { GlobalStyle, theme } from "../shared";
export default function App({ Component, pageProps }: AppProps) {
return (
<ThemeProvider theme={theme}>
<GlobalStyle theme={theme} />
<Head>
<title>Next.js</title>
</Head>
<main className="main">
<Component {...pageProps} />
</main>
</ThemeProvider>
)
}
I've a problem to integrate the theming functionality inside my next.js project who use react-jss. I tried the ThemeProvider who I've found inside the documentation.
Everytime, my front-end page refresh two times. The first times, I can see that the CSS theme is apply but fews milliseconds later, the page refresh and the theme disappear.
Do you have an idea to fix my problem? Here are my files:
_document.jsx
import React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import {
SheetsRegistry,
JssProvider,
ThemeProvider,
} from 'react-jss';
const theme = {
colorPrimary: 'green',
};
export default class JssDocument extends Document {
static getInitialProps(ctx) {
const registry = new SheetsRegistry();
const page = ctx.renderPage(App => props => (
<ThemeProvider theme={theme}>
<JssProvider registry={registry}>
<App {...props} />
</JssProvider>
</ThemeProvider>
));
return {
...page,
registry,
};
}
render() {
return (
<html lang="en">
<Head>
<style id="server-side-styles">
{this.props.registry.toString()}
</style>
</Head>
<body>
<Main />
<NextScript />
</body>
</html>
);
}
}
index.jsx
import React from 'react';
import injectSheet from 'react-jss';
import PropTypes from 'prop-types';
const styles = theme => ({
myButton: {
backgroundColor: 'black',
color: theme.colorPrimary,
},
});
const Index = ({ classes }) => (
<div className={classes.myButton}>Welcome to Novatopo website!</div>
);
Index.propTypes = {
classes: PropTypes.shape({
myButton: PropTypes.string,
}).isRequired,
};
export default injectSheet(styles)(Index);
_app.jsx
import App from 'next/app';
export default class MyApp extends App {
componentDidMount() {
const style = document.getElementById('server-side-styles');
if (style) {
style.parentNode.removeChild(style);
}
}
}
Here a CodeSandbox to reproduce the problem: codesandbox.io/s/pyrznxkr1j
2 things you could do:
prepare a codesandbox example
use ThemeProvider inside of JssProvider
I resolved the problem. You need to integrate the ThemeProvider logic inside the _app.jsx file.
Like that:
export default class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<ThemeProvider theme={theme}>
<Component {...pageProps} />
</ThemeProvider>
);
}
}