NextJS dynamic metatag without getServerSideProps - next.js

i want when u share a link from a topic in my forum it shows the title as the topic title, just like https://facebook.com/
that shows Facebook - log in or sign up
but i cant use
getServerSideProps
because i'm using html files to host (i need to) next build && next export -o
and its not supported on static html export:
https://nextjs.org/docs/advanced-features/static-html-export#unsupported-features
there is any other way to make it work?
i tried:
import React from "react";
import { useQuery } from "react-query";
import { fetchTopic } from "../../fetchers/forum";
import EmptyChangingPage from "../empty";
import Head from "next/head";
import { useRouter } from "next/router";
import { baseURL } from "../../baseUrl";
export default function Topic() {
const router = useRouter();
const { topicId } = router.query;
//
const {
data: topic,
isLoading: isLoadingTopic,
isFetched: isFetchedTopic,
} = useQuery([`topic:${topicId}`], () => fetchTopic(Number(topicId)), {
staleTime: 60000,
enabled: topicId !== undefined,
});
if (isLoadingTopic || !isFetchedTopic)
return <EmptyChangingPage />;
return (
<>
<Head>
<meta property="og:type" content="website" />
<meta
property="og:image"
content={`http://${baseURL}/imgs/og_logo.png`}
/>
<meta
property="og:url"
content={`http:/${baseURL}/forum/topic/?topicId=${topic?.topicId}`}
/>
<meta property="og:title" content={`Forum - ${topic?.title}`} />
<meta
property="og:description"
content={`content - ${topic?.message}`}
/>
</Head>
<main>
<div style={{ backgroundColor: "#dcdcdc" }}>
{/* forum stuff */}
</div>
</main>
</>
);
}
but when i send a link it shows
Forum - undefined
content - undefined

Related

Missing meta information in "view-page-source"

I need basic webpage description for SEO in Next.js project, but it is missing in the page source. However it is shown in chrome developer tools on Elements tab. What I am doing wrong?
my _app.js file-
import { useState, useEffect } from 'react';
import Head from 'next/head';
import Layout from '../components/layout/Layout';
import ContextProvider from '../store/context';
import '../styles/globals.css';
import '../styles/sideDrawer.css';
function MyApp({ Component, pageProps }) {
const [showChild, setShowChild] = useState(false);
useEffect(() => {
setShowChild(true);
}, []);
if (!showChild) {
return null;
}
if (typeof window === 'undefined') {
return <></>;
} else {
return (
<>
<Head>
<title>Reklaminiai.lt</title>
<meta name="description" content="reklaminės prekės ir aksesuarai" />
</Head>
<ContextProvider>
<Layout>
<Component {...pageProps} />
</Layout>
</ContextProvider>
</>
);
}
}
export default MyApp;
view-source:
<!DOCTYPE html><html><head><style data-next-hide-fouc="true">body{display:none}</style><noscript data-next-hide-fouc="true"><style>body{display:block}</style></noscript><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills.js?ts=1675875034598"></script><script src="/_next/static/chunks/webpack.js?ts=1675875034598" defer=""></script><script src="/_next/static/chunks/main.js?ts=1675875034598" defer=""></script><script src="/_next/static/chunks/pages/_app.js?ts=1675875034598" defer=""></script><script src="/_next/static/chunks/pages/index.js?ts=1675875034598" defer=""></script><script src="/_next/static/development/_buildManifest.js?ts=1675875034598" defer=""></script><script src="/_next/static/development/_ssgManifest.js?ts=1675875034598" defer=""></script><noscript id="__next_css__DO_NOT_USE__"></noscript></head><body><div id="__next"></div><script src="/_next/static/chunks/react-refresh.js?ts=1675875034598"></script><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/","query":{},"buildId":"development","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
I found information on this topic, but nothing solved my problem.

getServerSideProps not working returning undefined when mapping next js

what am i doing wrong here? i am trying to do getServerSideProps but
localhost is working fine when hosted i get
Internal Server Error 500
index.js
import React from "react";
import Head from "next/head";
import Navigation from "./navigation";
import { GetServerSideProps } from "next";
// import MyEditor from "./editor";
import Form from "react-bootstrap/Form";
import { useState } from "react";
import Questions3 from "../pages/question";
import axios from "axios";
import { FormControl, Button } from "react-bootstrap";
import InputGroup from "react-bootstrap/InputGroup";
function Home({ data }) {
const [Questions, setQuestions] = useState();
const [deatils1, setdeatils] = useState();
function clickQuestion() {
axios
.post("https://ask-over.herokuapp.com/questionpost", {
Name: Questions,
Summary: deatils1,
// username: this.props.sign.displayName,
// useremail: this.props.sign.email,
})
.then(() => {
window.location.reload();
});
}
function question(e) {
setQuestions(e.target.value);
// this.setState({ ask: e.target.value });
}
function deatils(e) {
setdeatils(e.target.value);
// this.setState({ ask: e.target.value });
}
return (
<>
<Head>
<title>wixten </title>
<meta
name="google-site-verification"
content="rqVH7Jc-L-NyyCYGf2LOEjRPFEUvi8iImncslSfxtac"
/>
<link rel="shortcut icon" href="/wixten.png" />
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
<meta
name="description"
content="have all ur doubts cleared here at wixten . At wixten ask any thing you want and anyone in the world can see your questin and will be able to answer it "
/>
</Head>
<Navigation />
<div>
<div className="container search-box">
<Form>
<Form.Group className="mb-3" controlId="exampleForm.ControlInput1">
<Form.Label>Title</Form.Label>
<Form.Control
type="text"
onChange={question}
placeholder="ask anything?"
/>
</Form.Group>
<Form.Group
className="mb-3"
controlId="exampleForm.ControlTextarea1"
>
<Form.Label>question</Form.Label>
<Form.Control onChange={deatils} as="textarea" rows={3} />
</Form.Group>
</Form>
{/* <Form>
<InputGroup
className="mb-3"
// onChange={this.question}
// value={ask}
// value={this.state.ask}
>
<FormControl
placeholder="ask anything?"
aria-label="ask anything?"
// aria-label="ask anything?"
aria-describedby="basic-addon2"
/>
<FormControl as="textarea" rows={3} />
</InputGroup>
</Form> */}
<Button
type="submit"
disabled={!deatils1 || !Questions}
onClick={clickQuestion}
variant="outline-secondary"
id="button-addon2"
>
ask?
</Button>
<Questions3 data={data} />
</div>
</div>
</>
);
}
export async function getServerSideProps() {
// Fetch data from external API
const res = await fetch(`https://ask-over.herokuapp.com/questapi`);
const data = await res.json();
// console.log(data);
// Pass data to the page via props
return { props: { data } };
}
export default Home;
pages/question/index.jsx
import React from "react";
import Alert from "react-bootstrap/Alert";
import axios from "axios";
import { useState } from "react";
import { useEffect } from "react";
import Link from "next/link";
import Head from "next/head";
function Question3(props) {
const data = props.data;
return (
<div className="question11">
{data.map((itm) => (
<Link
key={itm._id}
href={{
pathname: "query/[itm]",
}}
as={`query/${encodeURIComponent(itm._id)}`}
>
<Alert className="question13">{itm.Name}</Alert>
</Link>
))}
</div>
);
}
export default Question3;
when i call http://localhost:3000/the question page is rendering
after deploying to vercel i get the following error
when i depolyed i get this error
This looks like it's a non-page component. You can't use getServerSideProps in non-page components.
Try calling the API from your page file and pass it down as props. You could also create a context.
getServerSideProps can only be exported from a page. You can’t export it from non-page files.
Source

How to create dynamic route for top-level page in NEXT?

To support these URLs:
/account?tab=profile
/account?tab=pass
/account?tab=points
I know that I can change them to:
/account/profile
/account/pass
/account/points
And then create this route:
/pages/account/[tab].js
But this means that the accoun is a directory, not a file.
I want to have a account.js top-level file, and have a route for query strings on it.
I don't know how to do it. Something like /account?[tab] route. Is it possible?
import type { GetServerSideProps, NextPage } from 'next'
import Head from 'next/head'
import { ParsedUrlQuery } from 'querystring';
import styles from '../styles/Home.module.css'
const About: NextPage<{ query: ParsedUrlQuery }> = ({ query }) => {
console.log(query);
return (
<div className={styles.container}>
<Head>
<title>Create Next App</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<h1>About</h1>
</main>
</div>
)
}
export const getServerSideProps: GetServerSideProps = async ({ query }) => {
return {
props: {
query
}
}
}
export default About

Dynamic component imported stuck in loading phase : next js

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

Facebook unable to read open graph tags generated by nextjs

Everything is in this url:
https://developers.facebook.com/tools/debug/sharing/?q=https%3A%2F%2Fweally.org%2Fentity%2F5dcac229156b765cb8c45148%2Fissue%2F5dcac2085b0a145d1f08d16e
If you go to the page facebook is testing and lookup the source code, the meta tags are present (they are even duplicated, but that's another issue)
https://weally.org/entity/5dcac229156b765cb8c45148/issue/5dcac2085b0a145d1f08d16e
here's my _document.js file : I tried to enforce the tags on the first load in ssr
import React from 'react';
import Document, {Head, Main, NextScript} from 'next/document';
import {ServerStyleSheets} from '#material-ui/styles';
import theme from '../src/theme';
import {getIssueImageSrc} from "../src/utils/util";
import DeveloperError from "../src/error/DeveloperError";
const ISSUE_URL_PREFIX = '/entity/';
function isIssuePath(path) {
return !path.startsWith(ISSUE_URL_PREFIX) && path.indexOf('/issue/') != -1
}
function getIssueIdFromPath(path) {
const startIndex = path.indexOf('/issue/') + '/issue/'.length
let otherParam = path.indexOf('/', startIndex);
if (otherParam == -1) otherParam = path.length
const issueId = path.substr(startIndex, otherParam - startIndex)
console.log("issue id for paht ", path, " is : ", issueId)
return issueId
}
class MyDocument extends Document {
render() {
return (<html lang="en">
<Head>
<meta charSet="utf-8"/>
{/* Use minimum-scale=1 to enable GPU rasterization */}
<meta
name="viewport"
content="minimum-scale=1, initial-scale=1, width=device-width, shrink-to-fit=no"
/>
{/* PWA primary color */}
<meta name="theme-color" content={theme.palette.primary.main}/>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"/>
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
/>
{this.props.desc && <meta property="og:description" content={this.props.desc}/>}
{this.props.desc == null && <meta property="og:description"
content="Make your complaint about any company visible to the entire world on WeAlly.org. We can finally look at the problems companies have with their customers, complain on WeAlly and join the responsible citizens"/>}
{this.props.image && <meta property="og:description" content={this.props.image}/>}
{this.props.image == null && <meta property="og:image" content={'https://weally.org/static/images/fb_splash.jpg'}/>}
{this.props.title && <meta property="og:title" content={this.props.title}/>}
{this.props.title == null && <meta property="og:title" content="Allied together, our complaints are powerful"/>}
<link rel="icon" href="/static/images/favicon.ico"/>
<script language="JavaScript" type="text/javascript" src="/static/js/scripts.js">
</script>
<meta property="og:url" content={`https://weally.org`}/>
<meta property="og:type" content="website"/>
</Head>
<body>
<Main/>
<NextScript/>
</body>
</html>);
}
}
MyDocument.getInitialProps = async ctx => {
// Resolution order
//
// On the server:
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. document.getInitialProps
// 4. app.render
// 5. page.render
// 6. document.render
//
// On the server with error:
// 1. document.getInitialProps
// 2. app.render
// 3. page.render
// 4. document.render
//
// On the client
// 1. app.getInitialProps
// 2. page.getInitialProps
// 3. app.render
// 4. page.render
// Render app and page and get the context of the page with collected side effects.
const sheets = new ServerStyleSheets();
const originalRenderPage = ctx.renderPage;
ctx.renderPage = () => originalRenderPage({
enhanceApp: App => props => sheets.collect(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
const metaTags = {}
if (!process.browser) {
const path = ctx.asPath
if (path.startsWith(ISSUE_URL_PREFIX)) {
try {
const issueId = getIssueIdFromPath(path)
const mongoose = require('mongoose')
const Complaint = mongoose.model('Complaint')
const complaint = await Complaint.findById(issueId)
metaTags.image = getIssueImageSrc(complaint)
metaTags.desc = complaint.desc
metaTags.title = complaint.title
} catch (e) {
console.log( "Error in meta data prefetching ", e )
}
}
}
const propsToReturn = {
...metaTags,
...initialProps, // Styles fragment is rendered after the app and page rendering finish.
styles: (<React.Fragment>
{initialProps.styles}
{sheets.getStyleElement()}
</React.Fragment>),
}
return propsToReturn
}
export default MyDocument;
I naturally double checked, that the tags are inserted by this code, and they are.
Any ideas please, facebook is always falling back to the "default values" that are not relevant at all for that kind of urls. Also notice that facebook debugger is complaining about og:image that he doesn't seem to see at all

Resources