How to dynamically create subpages/subroutes in NextJS? - next.js

I want to create subpages dynamically like
example.com/test/index1
example.com/test/index2
example.com/test/index3
example.com/test/index4
.......
or something like this
example.com/test/[index1]
example.com/test/[index2]
example.com/test/[index3]
The subpages should be created based on the number of indexes. in the base/parent page
I am totally unable to figure out a way to handle something like this
Help would be much appreciated

Nextjs has file system based routing. To create a dynamic route for the app you just need to create a js/ts file with a name similar to [slug].js(where slug will be the route param for the dynamic route) in the pages directory. In that file, you can write all the logic for data-fetching and export a React component as default export which will be used to render the page.
For your use case, the directory structure and some pseudo-code for the page might look something like this
// directory structure
- pages/
- test/
- [slug].js
In [slug].js The example below uses getServerSideProps as data-fetching method which will be used to fetch the data required for the page on request for that page from a client.
// data-fetching methods
export const getServerSideProps = async (ctx) => {
// you have access to the route param slug in the ctx object
const slug = ctx.params.slug
// fetch the data required for the page by a database query or from a remote API
// return the fetched data as props
return {
props: /* fetched-data */
}
}
// the page component
const SomeDynamicPage = (props) => {
// props will contain the data that was returned from the data-fetching method-
// getServerSideProps
return (
<>
<h1>Some page</h1>
<div>
/* some content based on the received props*/
</div>
</>
)
}
export default SomeDynamicPage;
There are additional data-fetching methods (getStaticProps, getStaticPaths, getInitialProps) which may be useful depending on different use cases. Read more about data-fetching and dynamic routes in nextjs docs.

Related

nextjs links without strings

Im new to nextjs, and Im checking if it will be good for the app that will have pretty complex and messy internal navigation. Just checked their documentation and I see that they recommend usage
of Link component like this <Link href="/your_path">Path</Link>. A bit scary is that I have to provide 'your_path' as a string so every time i change page file name I have to manually update code that redirects to this page. Is there any solution that allows me to define routing on my own so I can write something like (pseudocode)
routes = [
...
{
page : 'page_name',
path : 'path_to_page'
}
...
]
So instead of using string I can do <Link href="{route.path}">Path</Link> or Im condemned to use this file-system based router with all consequences?
The simple answer is yes!
When you want to change a user route in NextJs you have 2 options,
The first is with the <Link> Element that you can specify a href to where it directs.
And you also have a useRouter hook for more complex routing for example if the user does an action that requires moving him into a different route you can do it internally in your handlers.
For more information about useRouter hook.
What I usually do is storing my routes in an object
const ROUTES = {
HOME: "/",
ABOUT: "/about"
}
and wherever you call routes you just use the object so F.E
With Link tag
<Link href={ROUTES.ABOUT}>ABOUT PAGE</Link>`
with useRouter hook
// Inside a React Component
const router = useRouter();
const handleNavigateToAbout = () => {
router.push(ROUTES.ABOUT);
}
return (
// SOME JSX
<button onClick={handleNavigateToAbout}> Go to about page! </button>
)

Why is a route dynamically rendered when it contains useSearchParams in Next 13?

I noticed that when a route contains at least one child client component that uses useSearchParams hook, the route becomes dynamically rendered.
In the docs, it states that a component is dynamically rendered only if it uses 1) dynamic functions or 2) fetch is made with {cache: "no-store"}. It also states that the dynamic functions in Next 13 are cookies and headers.
useSearchParams alone doesn't fulfil either criterion, so why would its containing route still be dynamically rendered?
Example:
// ClientComponent.tsx
"use client"
import { useSearchParams } from "next/navigation";
export default function ClientComponent() {
const searchParams = useSearchParams();
return <div>ClientComponent</div>;
}
// page.tsx
import ClientComponent from "./ClientComponent";
export default function Page() {
return (
<div>
<ClientComponent />
</div>
);
}

Getting a query param off of a dynamic page in next.js, possible?

I am finding IF a user enters my next.js site directly WITH query params on it. Mind you, the page itself is dynamic so it has internal "param". Using useRouter is NOT "seeing" the additional actual query param.....
So, I enter my site like so: Hard reload
-=> http://localhost/sku/1234?discount_id=TESTER
useRouter => query, only shows "1234" as being query{id: '1234'}. Nowhere IN the object of router is discount_id or its value?
This seems like a bug, no? I have to parse that myself?
The router.query param includes both dynamic route params and query params.
Try this code:
// File location: /pages/sku/[sku].tsx
import { useRouter } from "next/router";
const Page = () => {
const r = useRouter();
console.log(r.query);
return (
<div>
<div>sku: {r.query?.sku}</div>
<div>discount_id: {r.query?.discount_id}</div>
</div>
);
};
export default Page;
Open http://localhost:3000/sku/1234?discount_id=TESTER
Output:

NextJS route gives 404 when not using Link

I have a Next.js page, where the pages are statically generated (using next export). It's a dynamic route, but I will only fetch data on the client after the initial page load (in a useEffect).
The path is something like: /pages/foo/[id].tsx. This is in fact the only route which is going to exist.
My problem is:
If I access the URL directly (e.g. typing in https://mysite.fake/foo/1337 into the URL bar) I get a 404.
If I navigate to that route using a <Link /> it does work as expected
If I then reload the page, I get a 404 again.
On local dev, it works fine. The problem only exists when deployed.
The page in question is using the router.query, but I have the same problem with a completely static page (e.g. just /pages/bar/baz.tsx).
Example:
export const Home: NextPage = () => {
const router = useRouter()
const { id } = router.query
const headerText = id ? `Hello ${id.toString()}` : ''
// in a later iteration, I will use the query id to fetch data in a useEffect
return <h1>{headerText}</h1>
}
As far as I understand from the Next.js documentation on dynamic routing and data fetching this should be possible to do.
My next.config.js is very bare:
/** #type {import('next').NextConfig} */
module.exports = {
reactStrictMode: true,
}
What am I doing wrong?

How to Manage a Navigation Menu in NextJS with WordPress

I'm building a NextJS app using headless WordPress with GraphQL. It's not clear from the documentation where I should be calling the query to create the site navigation.
https://github.com/lfades/next.js/tree/examples/cms-wordpress/examples/cms-wordpress
The navigation is controlled dynamically by WordPress Menus (Appearance > Menus) on the backend and I can successfully access these menuItems via GraphQL without any issue on the index.js and posts/[slug].js page templates in Next JS.
// index.js
export default function Index({ primaryMenu = [] }) {
return (
<Layout>
<Header>
{primaryMenu.map((item, index) => {
return (<a href={item.url}>{item.label}</a>)
)}
</Header>
</Layout>
);
}
export async function getStaticProps() {
const primaryMenu = await getPrimaryMenu(); // Get menu via GraphQL
return {
props: { primaryMenu },
};
}
The issue I'm having with this is I am repeating the getStaticProps function on each template and I should be able to use some sort of global query for this, either in the <header/> component itself or another method. I'm unable to find documentation on how to do this and it doesn't work in components.
Any guidance (or examples) on where a global query such as a dynamic Navigation query would live in a NextJS app is appreciated.
There are a couple of ways you can do it:
You can menuItems query with useQuery() from #apollo/client inside the Layout component so that its available to all pages which are wrapped inside the Layout. However the problem with this is that, there will be a load time and the data won't be prefetched and readily available like we can do with getServerSideProps() ( at page level ). Because this will be at component level.
import { useQuery } from "#apollo/client";
export default function Layout () {
const { loading, data } = useQuery( GET_MENU_QUERY )
return {...}
}
You can use swr that uses caching strategy. There is blog that explains how to use it
I battled this for a while (for JD site) with redux and wp rest, but I think theory should be the same for gql + apollo client.
You need to override Next App _app with a custom class that extends App.
And you might need to inject an instance of apollo client into AppContext using a HOC. I used this wrapper for Redux. Would need to be modelled after that.
Edit: (Looks like someone has made it already)
// export default withRedux(makeStore)(MyApp);
export default withApollo(apolloClient)(MyApp); ???
Then in your App getInitialProps, you can make query to get menu. By default apollo client query will grab cached value if it's in the cache store already I believe.
static async getInitialProps(appContext) {
const { isServer, pathname, apollo? } = appContext.ctx;
// do menu query
const menu = apollo.query???
// Redux version
// const state = store.getState();
// let main_menu = state.menu;
// if (!state.menu) {
// const menu = await apiService().getMenu("main");
// main_menu = menu;
// store.dispatch({ type: "SET_MENU", payload: menu });
// }
...
// call the page's `getInitialProps` and fills `appProps.pageProps`
const initialProps = await App.getInitialProps(appContext);
const appProps: any = {
...initialProps,
menu: main_menu
};
return appProps;
}
Now menu is in the page props of the App Component, which can be passed down.
Or you can use apollo client to make the query again in a child component. So when you make the query again, in header or whatever, it will take the cached response provided it's the same query.
I made an endpoint for menus that included the template name + post slug along with the menu items and mapped the wp templates to next routes.
const menu = useSelector((state: any) => state.menu);
const menuItems = menu.map((item: any) => {
const path = getTemplatePath(item.template);
return (
<Link key={item.slug} href={`/${path}`} as={`/${item.slug}`} scroll={false}>
<a>{item.title}</a>
</Link>
);
});

Resources