Why do we only need getStaticPaths() when we use getStaticProps? - javascript

If we have a dynamic route
[id].js
function id() {
return <h1>Hello World</h1>
}
export default id
and we build the static pages with npm run build, the result is the [id].html page. Any route with /something will work and displays Hello World.
But if we generate the content of the page dynamically by using the return value of getStaticProps()
in the component:
function id(props) {
const { test } = props
return <h1>{test.foo}</h1>
}
export default id
Then we need getStaticPaths() again to specify the possible routes.
Why do we only need getStaticPaths() when we use getStaticProps?

The nextjs docs put it simply:
If a page has Dynamic Routes and uses getStaticProps, it needs to define a list of paths to be statically generated.
getStaticPaths is required for dynamic SSG pages because that how Vercel decided to make it.
If you want to use getStaticProps while maintaining the behavior of not requiring predefined routes from getStaticPaths, you can use fallback: true to tell nextjs to render routes even if they are not predefined by getStaticPaths.
You can send an empty array of paths and set fallback: true:
export async function getStaticPaths() {
const paths = [];
return { paths, fallback: true };
}
This will result in all routes still being accessible, like the behavior from the first part of your question. Here's a full example on codesandbox. In the example, try going to /test/any-route-you-want and note that the route will still be rendered:

Related

NextJS use getServerSideProps with a URL sub path

I've been using the solution at NextJS deploy to a specific URL path (and Deploy your NextJS Application on a different base path (i.e. not root)
) for a year now on our project. Our next.js server is hosted on another website under a sub-path, but the sub-path comes from the other server, not ours. I.e. we're at http://example.com/project/path. But our server is running at http://localhost:8081/ so we can't use the basePath since that changes our local path. This has been working great. We use a wrapped next/link for our links and just prepend our own basePath to API calls in most cases.
I recently tried switching to use getServerSideProps() rather than getInitialProps() since I was reading online that is the new preferred way (a single API call rather than the multiple we were making before). The problem I'm having is that I can't find a way to tell next.js that it needs to add the basePath to the getServerSideProps API calls it makes. We are using assetPrefix to point our images and css at the sub-path but it looks like there was a bug https://github.com/vercel/next.js/issues/15563 which was fixed that let us use assetPrefix rather than basePath.
Again, we can't use basePath since it changes localhost to http://localhost/project/path and it's the external website that has the extra pathing. Is there any other solution? Or are we just stuck on getIntialProps since it works and hope we don't lose that option in the future?
Currently, server-side rendering works, but when I navigate client-side, it returns a 404 on the getServerSideProps API call and the browser than redirects and loads the page server-side like:
Chrome network load. While it "works", it's not very performant since it must server-side render every page.
Edit: The proposed solution https://stackoverflow.com/a/66642225/1870780 does not work since we cannot use a basePath since it changes the local server base, not a remote server path pointing to our server.
Thank you #juliomalves for pointing me in the direction of the solution. The discussion at https://github.com/vercel/next.js/discussions/25681#discussioncomment-2026813 has a potential solution which works. In-lining the solution here to save others time, but credit goes to alizeait for the solution. Main difference between my solution an his, is his namespace does not include a leading / while mine does.
Typescript solution:
import { AppProps } from "next/app";
import { Router } from "next/router";
import { useEffect } from "react";
const useInterceptNextDataHref = ({
router,
namespace
}: {
router: Router;
namespace: string;
}) => {
useEffect(() => {
if (router.pageLoader?.getDataHref) {
const originalGetDataHref = router.pageLoader.getDataHref;
router.pageLoader.getDataHref = function (args: {
href: string;
asPath: string;
ssg?: boolean;
rsc?: boolean;
locale?: string | false;
}) {
const r = originalGetDataHref.call(router.pageLoader, args);
return r && r.startsWith("/_next/data")
? `${namespace}${r}`
: r;
};
}
}, [router, namespace]);
};
export default function MyApp ({ Component, pageProps, router }: AppProps) {
useInterceptNextDataHref({
router,
namespace: "/my-base-path"
});
return <Component {...pageProps} />;
}

Next.js overlaps static route with dynamic route

I am using Next.JS application routing system.
I have created a dynamic route with structure like pages/[country]/[language]/index.js.
Also there is a static route with structure pages/cz/cz/index.js.
Issue appears then i am on static page and trying to navigate throught Link component to access static route content in my case should go to home page & reload same page, instead of that dynamic route content is rendered.
In my case link is just simple navigation to home page <Link to="/"> for both routes.
But maybe issue lies on how index.js file is setup for predefined & dynamic routes.
cz/cz/index.js
export { default } from '../../../components/HomePage/';
const { getStaticProps } = createRoute({
path: '/',
locale: 'cs',
});
export { getStaticProps };
[country]/[language]/index.js
export { default } from '../../../components/HomePage/v2';
const { getStaticPaths, getStaticProps } = createRoute({
path: '/',
exclude: ['cs'],
otherLocales: ['cs'],
});
export { getStaticPaths, getStaticProps };
createRoute
export default function createRoute({
path,
otherLocales,
getPathParams,
locale,
} = {}) {
const locales = _.without(include, ...exclude);
return {
getStaticPaths: createGetStaticPaths({
locales,
getPathParams,
}),
getStaticProps: createGetStaticProps({
path,
locale,
locales,
otherLocales,
}),
};
}
Pages structure
So why [country]/[language]/index.js overrides cz/cz/index.js ?
So is there anything available in nextJS route to match a URL absolutely?
Or insure that going from static route should go to static route?
Following next.js documentation predefined routes take precedence over dynamic routes, and dynamic routes over catch all routes.
Take a look at the following examples:
pages/post/create.js - Will match /post/create
pages/post/[pid].js - Will match /post/1, /post/abc, etc. But not /post/create
pages/post/[...slug].js - Will match /post/1/2, /post/a/b/c, etc. But not /post/create, /post/abc
In your case you have defined predefined routes cz/cz/index.js and this route have priority
If you happen to be using redirects, note that they are processed before next ever goes to the pages.
Redirects are checked before the filesystem which includes pages and /public files.
https://nextjs.org/docs/api-reference/next.config.js/redirects

Understanding imports from component pages in NextJS

I am new to the Next.JS framework and do not fully understand the logic of importing data from a component page. For example, I have created a page like example.js in my components folder, where I am running an API that pulls data to a graph. Then, inside my index.js file, where I want my graph to be displayed, it is giving me a 'Unhandled Runtime Error', because the API function is not being triggered inside of example.js.
This is how I am importing the page in /pages/index.js:
import dynamic from 'next/dynamic'
const Example = dynamic(() => import('../pages/example.js'))
//Later on inside of my return
<Example />
And from my example.js page I am exporting as follows:
function Example(props) {
return <div>
... my code
}
Then below that, I have the following inside my example.js:
export default Example
Followed by my getStaticProps function:
export async function getStaticProps() {
const res = await fetch("")
const data = await res.json()
if (!data) {
return {
notFound: true
}
}
return {
props: {
data: data.reverse(),
},
}
}
If I use this exact code on my index.js it functions properly. Any ideas as to why this doesn't run, and any solutions as to a fix?
Edit for clarity: My getStaticProps function is in my example.js file, and my issue is that it is not being triggered.
Next.js does a lot of magic to handle server-side rendering, static-site generation and code splitting, and it does it all from within the /pages folder. It's important to know that components within the /pages folder work a little differently from a regular React component. Here are some tips for you that should solve your problem.
1. Put your page components inside the /pages folder.
Components inside this folder can have a default export (the component), along with named exports (the additional Next.js-specific functions like getStaticProps). If Next.js finds the exported functions, it will run them.
2. Use next/dynamic only for dynamic imports.
Dynamic imports are lazy-loaded -- this will affect Next.js' automatic code-splitting and potentially server-side rendering. I don't know the exact inner workings, but my guess is dynamically-loading a Page component (which are special in Next) is probably what's breaking things. I tend to only use dynamic imports when I have a 3rd-party component that breaks SSR, so I need to dynamically import it so it's not server-side rendered.
The simplest way to handle this is probably:
in /pages/index.js:
import Example from '../path/to/example';
export default (props) => (
<div>
<p>My page component</p>
<Example />
</div>
);
export async function getStaticProps() {
return {
props: {
data: // ...
}
};
}
With this solution above, you can use dynamic imports as well -- what matters is that you have your Next.js-specific function exported from the file in /pages immediately:
in /pages/index.js:
import dynamic from 'next/dynamic';
const Example = dynamic(() => import('../components/example'));
export default (props) => (
<div>
<p>My page component</p>
<Example />
</div>
);
export async function getStaticProps() {
return {
props: {
data: // ...
}
};
}
Update to solve "My getStaticProps function is in my example.js file, and my issue is that it is not being triggered.":
Next.js will only call a getStaticProps function that is exported from a file in the /pages directory. If you want to define your getStaticProps method from another directory, you can do that, but you need to import it into /pages and then re-export it like so:
in /components/example.js:
export default () => (
// ... component ...
);
export function getStaticProps() {
// ... function ...
};
and in /pages/index.js:
import Example, { getStaticProps } from '../components/example';
export default Example;
export {
getStaticProps
}
This should work. However, you can't import Example using dynamic imports in this case.

How to make path for pages in NextJS case insensitive

I have a file under the pages folder named about.tsx. So the path for the page is /about and I'm able to access the page by visiting example.com/about. However, if I visit example.com/About, it will redirect to a 404 page.
I've checked the Nextjs repo, seems like this is the expected behavior. Therefore, is there a workaround that can make the path case insensitive so that example.com/About will also work and direct users to the /about page?
With using next v12
There are a lot of similar questions here that has this answer already. I'd like to note that this answer is handling redirecting with the url parameters by adding this after the pathname:
${request.nextUrl.search}
Add a new file in /pages named _middleware.ts
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest) {
if (request.nextUrl.pathname === request.nextUrl.pathname.toLocaleLowerCase())
return NextResponse.next();
return NextResponse.redirect(`${request.nextUrl.origin}${request.nextUrl.pathname.toLocaleLowerCase()}${request.nextUrl.search}`);
}
I agree that's Next.js's behavior, they only handle exact page name about instead of both about & About using the same file page/about.tsx
But the solution is you keep implementing a main page (e.g: about.tsx) and setup other pages to redirect to that page (e.g: About -> about) following this guide https://nextjs.org/docs/api-reference/next.config.js/redirects
// next.config.js
module.exports = {
async redirects() {
return [
{
source: '/About',
destination: '/about',
permanent: true,
},
]
},
}
// Set permanent:true for 301 redirect & clean SEO!

React: How to add new value to props from imported file?

I am (very) new to React and have been given the task of adding some data to a component that's being brought in from another file. This file spits out some JSON and I want to access certain pieces of data from it, for example:
config.forms.enquiry.title
I am importing the file fine - no problems there. But I am not sure how to include config into my props.
I found a working example, in another file, and have copied what it does. My code is as such
Brings in file with JSON:
import { withSettings } from 'services/settingsFile';
Add config in render function:
render () {
const styles = getStyles(this.props, this.context, this.state);
const { config } = this.props;
// other stuff
Add to propTypes:
enquiryForm.propTypes = {
config: PropTypes.object.isRequired,
// other stuff
Add to compose:
export const enquiryForm = compose(
withSettings,
// other stuff
However, I get the error:
Failed context type: The context config is marked as required in
n, but its value is undefined.
And from here I am not sure what to do. I know it's a tough question, but I know very little about React and have been thrown in the deep end.
Would anyone know what/where I should be searching for to fix this?
If you can import it like,
import { withSettings } from 'services/settingsFile';
why dont you use it like,
const { config } = withSettings;
OK, so the issue was that there was no wrapping element setting config as am attribute.
I had to go up a level to where my component was being brought in and wrap:
<SettingsFile config={window.settingsFile}>
around:
<Component conf={config} />
Then, the component I was working on was able to read config.

Categories