How to change default root file in Next.js? [duplicate] - next.js

I have a dynamic route at the root of my pages folder in Next; like so src/pages/[page].js
This works great for anything that actually has a slug for example example.com/my-page will return the correct data for my-page.
I want to be able to access a slug named homepage when you're visiting the root of the website. So visiting example.com would actually return the page homepage.
How can this be done? Here's a stripped back version of my component:
import React from "react";
import { fetchAPI } from "../lib/api";
const Page = () => <p>Content...</p>;
export default Page;
export async function getStaticPaths() {
const pages = await fetchAPI("/pages");
return {
paths: pages.map((page) => ({
params: {
page: page.slug,
},
})),
fallback: false,
};
}
export async function getStaticProps({ params }) {
const pages = await fetchAPI(`/pages?slug=${params.page}`);
return {
props: { page: pages[0] },
revalidate: 1,
};
}

You can configure rewrites in the next.config.js file to map the / path to the /homepage destination path.
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: '/',
destination: '/homepage'
}
];
}
};
This will not change the visible URL (/) in the address bar.

In getStaticProps, your params.page will be undefined on your home page because you're using a dynamic route there, so you just need to handle that case:
export async function getStaticProps({ params }) {
const pageSlug = params.page ?? "homepage"
const pages = await fetchAPI(`/pages?slug=${pageSlug}`);
return {
props: { page: pages[0] },
revalidate: 1,
};
}
Update To Redirect 404 Pages to Homepage
OP - in your comment below, you said "...but if a user visited something like example.com/asdopmsaiond wouldn't it also show the homepage in this case instead of a 404". While my code above will give you the homepage slug when a user visits the home page, it seems you also want to display the home page when a user visits any page that doesn't truly exist. There are generally three ways to go about this:
Redirect to the home page on 404 (Preferred)
You can redirect the user to the home page if no page is found by checking your pages variable - if it doesn't have a valid pages[0] value, you'll know (assuming your api is set up well) that no page was found and you can then redirect to the home page:
export async function getStaticProps({ params }) {
const pageSlug = params.page ?? "homepage"
const pages = await fetchAPI(`/pages?slug=${pageSlug}`);
if(!pages || !pages.length > 0) {
return {
redirect: {
destination: '/',
permanent: false,
},
}
}
return {
props: { page: pages[0] },
revalidate: 1,
};
}
I say that this is "preferred" because it will not result in SEO duplicate content penalties like the last option would, and doesn't result in any additional processing or api calls.
Redirect with 404.js (or 404.tsx)
You can create a 404.js (or 404.tsx) file which Next uses when it needs to display a 404 page. This file also takes getStaticProps which you can use to just redirect to the home page:
// 404.js
export default function FourOhFourPage() {
return null
}
export async function getStaticProps({ params }) {
return {
redirect: {
destination: '/',
permanent: false,
},
};
}
Display the home page on any 404 url
You can also resubmit your api call with the homepage slug if no pages are found in your api call, and then display that homepage content. This would result in a second api call and you'd want to include a canonical url to your home page so you don't get hit with duplicate content penalties for SEO:
export async function getStaticProps({ params }) {
const pageSlug = params.page ?? "homepage"
// Changed `pages` from `const` to `let` so we can modify it
// if we don't get the desired result from the api call
let pages = await fetchAPI(`/pages?slug=${pageSlug}`);
if(!pages || !pages.length > 0) {
pages = await fetchAPI(`/pages?slug=homepage`);
}
return {
props: { page: pages[0] },
revalidate: 1,
};
}

Related

Next.js change url without building the page using ISR

I am using ISR to build static product pages using next.js. Since there are a lot of product page to generate I only generated a few pages for it. The problem that I am trying to solve is the delay in transferring the view to the product page.
So I have a category page and within it have a list of products. On each product card item, I use next.js link so that the user can go to the product page.
The problem here is the delay going to the product page when the page is not yet generated. Going to the product page is slow because next.js is building the page. I want to transfer the user to the product page immediately while showing the loading state of the page via the router.isFallback condition.
What I'm trying to achieve is the same as what a normal link would do because it shows the loading state of the page but I don't want to reload the page.
Instead of using next/link or router.push, use router.replace
router.replace(`/product/${id"}`)
Let me know if this work.
What you could do is to make the props not required,
The thing that must take time during the loading of you're ISG page of nextJs is the call api in GetStaticProps,
Something like that:
export async function getStaticProps({ params }) {
const { status, data } = await axios.get<Data>(
`${server}/data`
);
if (status === 404) {
return { notFound: true };
}
return {
props: {
...data
},
revalidate: 60,
};
}
But you could also decide that you will fetch the data during the loading of the state with a fall-back blocking:
const MyPage = (props) => {
const [data,setData] = useState<Data>(null);
useEffect(() => {
(function()
const {data} = axios.get(`${server}/data`);
setData(data);
)()
},[])
return(
<div>
!data ? <div>loading ... </div> : <div>Product: {data}</div>
</div>
)
}
export async function getStaticProps({ params }){
return {
props: {
isloading: true
},
revalidate: 60,
};
}

Next JS Static Site: Redirect specific routes to another site?

I've made a static site using Next JS that is deployed on cloudfront. I'd like to have some routes redirect to another site by just changing the base path (e.g if the route is www.mysite.com/stuff, it will redirect to www.redirectedSite.com/stuff). Is this possible and if so, what would I use to set this up?
Inside the getStaticProps function, there is an option to return a redirect value. This allows for redirecting to internal and external resources.
You could have the code below saved under pages/stuff.tsx. When a request is made to /stuff, they will be redirected.
import { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async () => {
return {
redirect: {
destination: 'http://www.redirectedsite.com/stuff',
permanent: false,
},
};
};
const TestPage = (): JSX.Element => {
return <div></div>;
};
export default TestPage;
Next JS Documentation on getStaticProps redirect

Is there any why to show loading screen while fetching API data using getStaticProps in next js?

Is there any why to show loading screen while fetching API data using getStaticProps in next js?
export async function getStaticProps() {
const data = await fetch(API_END_POINT);
return {
props: {
data
}
}
}
getStaticProps runs at build time so there is no need for the loading screen since the data will always be available (statically generated).
However, there is another option: getStaticPaths
In combination with fallback pages.
Basically there is a mix of static and server rendered pages (but there is no request and response objects)
// pages/posts/[id].js
import { useRouter } from 'next/router'
function Post({ post }) {
const router = useRouter()
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>
}
// Render post...
}
// This function gets called at build time
export async function getStaticPaths() {
return {
// Only `/posts/1` and `/posts/2` are generated at build time
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
// Enable statically generating additional pages
// For example: `/posts/3`
fallback: true,
}
}
// This also gets called at build time
export async function getStaticProps({ params }) {
// params contains the post `id`.
// If the route is like /posts/1, then params.id is 1
const res = await fetch(`https://.../posts/${params.id}`)
const post = await res.json()
// Pass post data to the page via props
return {
props: { post },
// Re-generate the post at most once per second
// if a request comes in
revalidate: 1,
}
}
export default Post
Just make sure to add getStaticPaths with fallback equal to true. See example below
export async function getStaticPaths() {
return {
paths: [],
fallback: true,
}
}
then in the component(example a post page component), add a check if router isFallback is true. See below example
import { useRouter } from 'next/router'
function Post({ post }) {
const router = useRouter()
// If the page is not yet generated, this will be displayed
// initially until getStaticProps() finishes running
if (router.isFallback) {
return <div>Loading...</div>
}
// Render post...
}

How to clear/delete cache in NextJs?

I have a product page at /products/[slug].js
and I use Incremental Static Generation for a wordpress/graphql site:
export async function getStaticProps(context) {
const {params: { slug }} = context
const {data} = await client.query(({
query: PRODUCT_SLUG,
variables: { slug }
}));
return {
props: {
categoryName: data?.productCategory?.name ?? '',
products: data?.productCategory?.products?.nodes ?? []
},
revalidate: 1
}
}
export async function getStaticPaths () {
const { data } = await client.query({
query: PRODUCT_SLUGS,
})
const pathsData = []
data?.productCategories?.nodes && data?.productCategories?.nodes.map((productCategory) => {
if (!isEmpty(productCategory?.slug)) {
pathsData.push({ params: { slug: productCategory?.slug } })
}
})
return {
paths: pathsData,
fallback: true,
}
}
Everything works as expected except one thing. If I delete a product from wordpress which was previously published, NextJs serves the cached page instead of showing 404 - Not found page, and I think this is how it is supposed to work, meaning that if something isn't rebuilt, show the previous (stale) page.
But how can I completely remove the cache for a specific product which has been deleted and it is not fetched again from the PRODUCT_SLUGS query ?
I have read the fallback options: true, false, blocking but none of them seems to work.
Is there a solution to this, either a next.config.js configuration or another work around ?
So I ran into this same issue, although I am using GraphCMS. So here is what you need to do to fix:
export async function getStaticProps(context) {
const {params: { slug }} = context
const {data} = await client.query(({
query: PRODUCT_SLUG,
variables: { slug }
}));
if (!data) {
return {
notFound: true
}
} else {
return {
props: {
categoryName: data?.productCategory?.name ?? '',
products: data?.productCategory?.products?.nodes ?? []
},
revalidate: 1
}
}
}
You need to return notFound: true in getStaticProps
notFound - An optional boolean value to allow the page to return a 404 status and page.
See this page in the docs https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation
Then in getStaticPaths change fallback to fallback: "blocking". If you keep fallback: true it is going to serve the stale page since that built successfully.
I think this is possible starting from next#12.1.x using this feature On-demand Incremental Static Regeneration
https://nextjs.org/blog/next-12-1#on-demand-incremental-static-regeneration-beta
basically you can define an api path in this way
// pages/api/revalidate.js
export default async function handler(req, res) {
// Check for secret to confirm this is a valid request
if (req.query.secret !== process.env.MY_SECRET_TOKEN) {
return res.status(401).json({ message: 'Invalid token' })
}
const PRODUCT_SLUGS = req.query.product_slug;
try {
await res.unstable_revalidate(`/products/${PRODUCT_SLUGS}`)
return res.json({ revalidated: true })
} catch (err) {
// If there was an error, Next.js will continue
// to show the last successfully generated page
return res.status(500).send('Error revalidating')
}
}
Using this api path you can invalidate the cache for a specific product

Next.js does not delete dynamic page deleted in CMS

I am using next.js 10 and have a [slug] page which creates dynamic pages from Contentful CMS.
I am changing the slug inside CMS and run next dev the old slug correctly returns 404 and the new slug works.
However when I build and run next start the old slug still returns a cached page, the new slug works properly.
I am returning revalidate 10 and have assumed the page should refresh after 10sec
export const getStaticProps: GetStaticProps<SlugRoutePageProps> = async ({
params,
}) => {
....
....
const pageData = await getPageData(params.slug)
if (pageData.total === 0) return { notFound: true }
return {
props: {
pageType: "DynamicPage",
pageProps: {
pageData,
},
revalidate: 10,
},
}
}
in getStaticPaths I have fallback: "blocking", also tried fallback: true with no difference.
Edit:
getPageData is a basic call to the contentful api - no caching
const getPageData = async (
slug: string,
): Promise<FetchPagesResult> => {
const client = createContentfulClient()
return client.getEntries<Page>({
content_type: "page",
"fields.slug": slug,
include: 5,
order: "-sys.updatedAt",
limit: 1,
})
}

Resources