I've been trying to build this blog app, Before is fine and in the dev environment is also fine. But, as soon as i try to build it for production it caught in this error.
Error occurred prerendering page "/blogs/[slug]". Read more: TypeError: Cannot read property 'title' of undefined
I am confused because this app had been build before and nothing is wrong. This is the culprit:
import Head from "next/head";
import "bootstrap/dist/css/bootstrap.min.css";
import ReactMarkdown from "react-markdown";
import moment from "moment";
const Post = ({ posts }) => {
return (
<>
<Head>
<title>{posts && posts.title}</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
></meta>
</Head>
<div className="container" style={{ marginTop: "3.85rem" }}>
<div className="row">
<h1>{posts.title}</h1>
</div>
<div className="row d-flex justify-content-end">
<p style={{ textAlign: "right" }}>
{moment(posts.dateTime).format("dddd, Do MMMM YYYY, hh:mm:ss a")}
</p>
</div>
<br />
<div className="row d-flex justify-content-center">
<img
className="postThumbNail"
src={posts.thumbnail.url}
alt={posts.thumbnail.url}
/>
</div>
<br />
<ReactMarkdown>{posts.body}</ReactMarkdown>
</div>
</>
);
};
export async function getStaticProps({ params: { slug } }) {
const res = await fetch(
`=${slug}`
);
const blogPost = await res.json();
return {
props: {
posts: blogPost[0],
},
revalidate: 600,
};
}
export async function getStaticPaths() {
// Get Post From Strapi
const res = await fetch(
";
);
const post = await res.json();
// Retrun Enrich Content
return {
paths: post.map((posts) => ({
params: { slug: String(posts.slug) },
})),
fallback: true,
};
}
export default Post;
I've been trying to build this blog app, Before is fine and in the dev environment is also fine. But, as soon as i try to build it for production it caught in this error.
Error occurred prerendering page "/blogs/[slug]". Read more: https://nextjs/docs/messages/prerender-error TypeError: Cannot read property 'title' of undefined
I am confused because this app had been build before and nothing is wrong. This is the culprit:
import Head from "next/head";
import "bootstrap/dist/css/bootstrap.min.css";
import ReactMarkdown from "react-markdown";
import moment from "moment";
const Post = ({ posts }) => {
return (
<>
<Head>
<title>{posts && posts.title}</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
></meta>
</Head>
<div className="container" style={{ marginTop: "3.85rem" }}>
<div className="row">
<h1>{posts.title}</h1>
</div>
<div className="row d-flex justify-content-end">
<p style={{ textAlign: "right" }}>
{moment(posts.dateTime).format("dddd, Do MMMM YYYY, hh:mm:ss a")}
</p>
</div>
<br />
<div className="row d-flex justify-content-center">
<img
className="postThumbNail"
src={posts.thumbnail.url}
alt={posts.thumbnail.url}
/>
</div>
<br />
<ReactMarkdown>{posts.body}</ReactMarkdown>
</div>
</>
);
};
export async function getStaticProps({ params: { slug } }) {
const res = await fetch(
`https://someapi./blogs?slug=${slug}`
);
const blogPost = await res.json();
return {
props: {
posts: blogPost[0],
},
revalidate: 600,
};
}
export async function getStaticPaths() {
// Get Post From Strapi
const res = await fetch(
"https://someapi./blogs"
);
const post = await res.json();
// Retrun Enrich Content
return {
paths: post.map((posts) => ({
params: { slug: String(posts.slug) },
})),
fallback: true,
};
}
export default Post;
Share
Improve this question
asked Sep 27, 2021 at 5:24
KotjengOrenKotjengOren
791 silver badge8 bronze badges
2
-
2
You checked if posts exists here:
<title>{posts && posts.title}</title>
, but forgot to do it here:<h1>{posts.title}</h1>
– Nicholas Tower Commented Sep 27, 2021 at 5:25 - @NicholasTower Yep Sure, But now the problem is goes to post.dateTime, I mean the conditional did help but shall I do that for all? Is there any more graceful way of solving this? – KotjengOren Commented Sep 27, 2021 at 5:31
3 Answers
Reset to default 3Solution 1 You need to check if attribute exists first with optional chaining :
<h1>{posts?.title}</h1>
Solution 2
or object is not null return null
until data loads
if(!posts) return null
return <div> ...Code </div>
The issue occurs because you're using fallback: true
in getStaticPaths
, and you're not handling the fallback page which gets served by Next.js before getStaticProps
finishes running. At that point the props passed to the page ponent will be empty.
In the “fallback” version of a page:
- The page’s props will be empty.
- Using the router, you can detect if the fallback is being rendered,
router.isFallback
will betrue
.— Next.js, Fallback Pages documentation
#1 Handle fallback page with router.isFallback
You can properly handle the fallback version of the page using router.isFallback
.
const Post = ({ posts }) => {
const router = useRouter()
// Display loading until `getStaticProps()` finishes running, and populates the props.
if (router.isFallback) {
return <div>Loading...</div>
}
return (
<h1>{posts.title}</h1>
);
};
#2 Use fallback: 'blocking'
instead
As an alternative, if you don't really need the fallback functionality, you can change the fallback value in getStaticPaths
to fallback: 'blocking'
. This will still generate new pages like fallback: true
, but will wait until getStaticProps
has ran before rendering the page.
export async function getStaticPaths() {
const res = await fetch("https://someapi./blogs");
const post = await res.json();
return {
paths: post.map((posts) => ({
params: { slug: String(posts.slug) }
})),
fallback: 'blocking'
};
}
<Head>
<title>{posts && posts.title}</title>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
></meta>
</Head>
<div className="container" style={{ marginTop: "3.85rem" }}>
<div className="row">
<h1>{posts && posts.title}</h1> // You've just forgot that if posts exist condition
</div>
<div className="row d-flex justify-content-end">
<p style={{ textAlign: "right" }}>
{moment(posts.dateTime).format("dddd, Do MMMM YYYY, hh:mm:ss a")}
</p>
</div>
<br />
<div className="row d-flex justify-content-center">
<img
className="postThumbNail"
src={posts.thumbnail.url}
alt={posts.thumbnail.url}
/>
</div>
<br />
<ReactMarkdown>{posts && posts.body}</ReactMarkdown>
</div>