Next.js keeps generating 404 page instead of blog details page - reactjs

I am trying to generate my blog details page but next keeps giving me the 404 page. I am fetching the data from a headless cms called contentful. Here is what my file structure and code looks like
And this is my [path]s.js page
import {createClient} from "contentful"
const client = createClient({
space: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
accessToken: "xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
});
export const getStaticPaths = async () => {
const res = await client.getEntries({
content_type: "blog"
})
const paths = res.items.map(item => {
return {
params: {slug: item.fields.slug}
}
})
return {
paths,
fallback: false
}
}
export const getStaticProps = async ({params}) => {
const {items} = await client.getEntries({
content_type: "blog",
"fields.slug": params.slug
})
return {
props: {blog: items[0]}
}
}
function blogDetails({blog}) {
console.log(blog)
return (
<div>
Blog details
</div>
)
}
export default blogDetails
I do not understand where I am going wrong. What could be my problem?.

Firstly, you cannot use the square brackets as a "wildcard" at the beginning of the path and add some other characters after. So you should rename your file as just [path].js (without the s after [path]).
Secondly, in your getStaticPaths function, when you return the object of params inside paths, name the keys as the dynamic path you've named your file as. In your case, the object you return should be params: {path: item.fields.slug}. Alternatively, you can rename your file as [slug].js and leave the code like it is.
// [path].js
export const getStaticPaths = async () => {
const res = await client.getEntries({
content_type: "blog"
})
const paths = res.items.map(item => {
return {
params: {path: item.fields.slug} //renamed the key as "path"
}
})
return {
paths,
fallback: false
}
}
You can read more about dynamic routes and getStaticPaths() in the official documentation.

Related

My dynamic routing isn't displaying any information, I just get 404. next.js react

I am having issues with dynamic routes in Next.js.
Setup :
I have getStaticPaths and getStaticProps in my dynamic page. When I click on an item that should take me to the dynamic route, the browser navigates to a page with the right url "properties/propertyId" ; However, the page says 404, the page could not be found.
I am not sure if my problem is how I am fetching my data from firebase . I tried using the data fetching method on another page and it does fetch the data, so I am not 100% sure where the problem is.
When I inspect the network tab, it says 404 not found
http://localhost:3000/Properties/5f328e9c-656f-4691-8b4b-4f414bbfdee2
the path is correct, it matches whats in the database, but the webpage says 404
Here is my dynamic page , I believe the getStaticPaths portion is ok, but a second opinion would be nice, however regarding the getStaticProps, I think the problem is there.
import { get, ref } from "firebase/database";
import React, { useState } from "react";
import BookingMenu from "../../components/BookingMenu";
import { database } from "../../components/firebase";
import PropertyStyles from "../../styles/ViewProperty.module.css";
const ViewProperty = ({ property }) => {
return (
<>
<div style={{ display: "flex" }}>
<div className={PropertyStyles.album}>Property id</div>
<BookingMenu />
</div>
</>
);
};
export default ViewProperty;
export const getStaticPaths = async () => {
const propertyRef = ref(database, "properties");
const allProperties = [];
const getProperties = async () => (await get(propertyRef)).val();
getProperties().then((properties) => {
allProperties.push[properties ? Object.values(properties) : []];
});
console.log(allProperties);
const paths = allProperties.map((property) => {
return {
params: { Id: property.Id.toString() },
};
});
return {
paths,
fallback: false,
};
};
export const getStaticProps = async (context) => {
const id = context.params.Id;
const propertyRef = ref(database, "properties/" + id);
const property = [];
const getProperties = async () => (await get(propertyRef)).val();
getProperties().then(
(prop) => property.push[prop ? Object.values(prop) : []]
);
return {
props: { property: property },
};
};
pages folder structure
use id insted of Id
const paths = allProperties.map((property) => {
return {
params: { id: property.Id.toString() },
};
});
...
const id = context.params.id;
I changed fallback in getStaticPaths to true.
return {
paths,
fallback: true,
};
Try to visit http://localhost:3000/properties/5f328e9c-656f-4691-8b4b-4f414bbfdee2 - "properties" in lowercase. If you are using Mac OS this problem is possible, because there is case insensitive file system, for example you name folders pages/Properties but for Mac OS it is the same as pages/properties and nextJs will generate /properties

404 when requesting a single entry in contentful

I am trying to request a single entry from contentful CMS with the getEntry() method. I keep getting a 404. I really don't understand what am I doing wrong.
export async function getStaticProps({ params }) {
const p = params.id;
const client = contentful.createClient({
space: process.env.CONTENTFUL_SPACE_ID,
environment: process.env.CONTENTFUL_ENVIRONMENT_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
const entry = await client.getEntry(p);
return { props: { entry: entry } };
}
I created a small project to replicate the issue, but I couldn't. It works fine for me. This is what my sample code looks like:
import {createClient} from 'contentful'
export default function Comp() {
return (
<div>
Hello
</div>
)
}
export async function getStaticProps({ params }) {
const p = params.id;
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
environment: process.env.CONTENTFUL_ENVIRONMENT_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
const entry = await client.getEntry(p);
return { props: { entry: entry } };
}
export async function getStaticPaths() {
const client = createClient({
space: process.env.CONTENTFUL_SPACE_ID,
environment: process.env.CONTENTFUL_ENVIRONMENT_ID,
accessToken: process.env.CONTENTFUL_ACCESS_TOKEN,
});
const entry = await client.getEntries({
content_type: "blogPost"
})
const paths = entry.items.map(item => {return {params: {id: item.sys.id}}});
return { paths, fallback:true}
}
You might be getting a 404 because the paths might be correctly configured. If the above code sample doesn't help, feel free to link the repo or share more details :)

Use CSR in Next.js as fallback when SSR path is not available

So I have this blog, with dynamic url for posts (blog/[postId]). Posts are being added through keystone.js cms. It worked fine via CSR, but i came to a necessity to add proper meta open graph tags for each post (for correct share snippets).
I added getStaticProps and getStaticPaths to my post page, and now my meta tags are working fine, but now i get 404 when trying to access any post, that was added after build. Meaning, now i have to rebuild after each new post. My question is - is it possible to set Next to render component as CSR, if it fails to find path for it with getStaticPaths? I've tried to use try catch, but that didn't work. Now i came up with this concept of using container, but it does not seem to be the right way to use fallback. Or is it? I am fairly new to Next, so i am a bit confused.
const PostContainer = (props) => {
const [postData, setPostData] = React.useState(null);
const router = useRouter();
React.useEffect(() => {
if (router.query?.id) {
getPost();
}
}, [router.query]);
const getPost = async () => {
const postData = await getPost(`${router.query.id}`);
if (postData) {
setPostData(postData);
}
};
if (router.isFallback) {
return <Post data={postData}
metaProps={defaultMetaProps}
/>
}
return (
<Post data={postData}
metaProps={props}
/>
);
};
export const getStaticPaths = async () => {
const list = await getPosts();
if (list?.length) {
const paths = list.map((post) => {
return {
params: {
id: post.id,
},
};
});
return {
paths,
fallback: true,
};
}
return {
paths: [],
fallback: true,
};
};
export const getStaticProps = async ({ params }) => {
const { id } = params;
const post = await getPost(id);
const {title, description, previewImage} = post
const props = {
props: {
title,
description,
url: `https://sitename./blog/${id}`,
image: previewImage,
},
revalidate: 1,
};
return props
};
export default PostContainer;
And one more thing - do i understand correctly that if fallback option is true, it takes only one visit to the page by ANY user, after which new path is generated on the server permanently and is accessible for anyone who comes next, therefore fallback always fires just once for this first visit of first user?

NEXT.js ISR remove static page

I have a little problem with the ISR. I have the revalidate prop equal 1s like here
export async function getStaticProps({ params }) {
const data = await client.getEntries({
content_type: "product",
"fields.name": params.slug,
});
if (!data.items[0]) {
return {
notFound: true,
};
}
return {
props: {
article: data.items[0],
revalidate: 1,
},
};
}
When I create product in Contentful, the page is created as I expected. When I want to entry to page that doesn't exist I get 404 error as expected. Problem starts when I change something in Contentful in existing product or I delete it.
When I delete the product in Contentful, the list of products in the products page is updated and the product disappears but I can still entry in page of that product. Also when I rename the product name the list of products is updated but I still can entry to the earlier page name.
Is there any solution to solve this problem?
getStaticPaths
export async function getStaticPaths() {
const data = await client.getEntries({
content_type: "product",
});
return {
paths: data.items.map((item) => ({
params: { slug: item.fields.name },
})),
fallback: true,
};
}
Product page
const Article = ({ article }) => {
const router = useRouter();
if (router.isFallback) return <p>Loading...</p>;
return (
<div>
<h1>Hello! {article.fields.name}</h1>
<Link href="/about">
<a>Back to about!</a>
</Link>
</div>
);
};
EDIT
When I change product name from "product77" to "product7" in Contentful after revalidate static page in my build for product77 still exist and I still can entry to that page.
Shouldn't it be removed after revalidation?
Removed pages can't be deleted, but served with a 404 instead using On-demand Revalidation, if getStaticPaths and getStaticProps are configured accordingly.
// /pages/blog/[...post].jsx
function Post({ postData }) {
return (
// ...
);
}
export async function getStaticPaths() {
const res = await fetch("https://.../posts");
const posts = await res.json();
// Prerender paths during build
const paths = posts.map((post) => ({
params: { post: post.id },
}));
return { paths, fallback: "blocking" };
}
export async function getStaticProps(context) {
const res = await fetch(`https://.../posts/${context.params.post}`);
const postData = await res.json();
if(!postData){
// The page doesn't exist in the CMS, return notFound to trigger a 404
return{
notFound: true,
revalidate: 30
}
}
// Return page props
return {
props: {
postData,
},
revalidate: 30,
};
}
The main takeaway from the above code is:
You can pre-render paths in getStaticPaths
getStaticPaths must use "blocking" or true as the fallback value
Handle unknown paths inside getStaticProps (thanks to fallback). If the page doesn't exist in the CMS, return notFound: true triggering the 404 page
Then, using the On-demand revalidation approach in a webhook:
// /pages/api/revalidate-post.js
export async function handler(req, res) {
try {
const reqBody = JSON.parse(req.body);
await res.revalidate(`/${reqBody.path}`);
return res.json({ revalidated: true });
} catch (err) {
return res.status(500).send("Error revalidating");
}
}
The res.revalidate(/${reqBody.path}) invokes a fresh evaluation of a page using the logic from getStaticProps.
Now, if somebody would delete the page in the CMS, and trigger the above revalidation webhook handler for the deleted page path, then the path serves a 404 page instead of the original page.
The page itself, however, is not deleted from the disk.

NextJS: How do I get exportPathMap page data? I don't see it in getStaticProps

I have set up exportPathMap but I get an empty object when exporting getStaticProps from my component. I don't understand how I'm supposed to access it?
// next.config.js
exportPathMap: async function (
defaultMapPath,
{ dev, dir, outDir, distDir, buildId }
) {
const paths = {}
const response = await fetch(...)
const data = await response.json()
data.forEach((item) => {
paths[`/item/${item.id}`] = {
page: '/itemPage',
query: {
item,
},
}
})
return paths
},
And
// itemPage.js
...
export async function getStaticProps(props) {
console.log('props', props) // returns "props {}""
return {
props: props
}
}
...
It's difficult to find it specifically stated in the documentation right now, but here are your options.
OPTION 1
Use getStaticPaths and getStaticProps like so (from "with-static-export" in the nextJS examples)
// [id].js post file
export async function getStaticPaths() {
const response = await fetch(
'https://jsonplaceholder.typicode.com/posts?_page=1'
)
const postList = await response.json()
return {
paths: postList.map(post => {
return {
params: {
id: `${post.id}`,
},
}
}),
fallback: false,
}
}
export async function getStaticProps({ params }) {
// fetch single post detail
const response = await fetch(
`https://jsonplaceholder.typicode.com/posts/${params.id}`
)
const post = await response.json()
return {
props: post,
}
}
OPTION 2
Use exportPathMap AND getInitialProps
// item.js
...
Page.getInitialProps = function (context) {
return context.query.item
}
// next.config.js
module.exports = {
// ...Same as in your question...
}
Both will allow you to have static optimization. With Option 1, you need not export, next build is sufficient. For Option 2 you will need to run next export
From: https://nextjs.org/docs/advanced-features/static-html-export
If your pages don't have getInitialProps you may not need next export at all; next build is already enough thanks to Automatic Static Optimization.
Another note: You can use both methods inside a single project.

Resources