I am using Next Js in my application. I know that we can create dynamic pages using pages folder, but i can not create a certain design for pages structure. What i want to achieve: I need to have the next paths in my application: /user/pageAccordingUserIdWhichIsDymamicPage/userDataWhichIsStaticPage/anotherDynamicPage. My question is, how the folder structure should look? Note: all paths are related to user, so i expect that the pages should be located in one page folder from pages.
This is possible using a feature known as dynamic routing. For more information see: https://nextjs.org/docs/routing/dynamic-routes.
Creating the Routes
For each dynamic route/url you wish to create follow the process below:
Divide the URL into different sections based on the '/' character.
For example: "/users/roger/data" would be split to [users, roger, data].
For each section of the URL, create a folder with the corresponding name. If a certain part of the URL is dynamic, the name of the folder should be wrapped in square brackets- [dynamicData].
Finally, create as many index.js files as you need. These files should be placed in the folders you created.
For example, if you wanted the page /users/roger to work - you would go to your pages directory, users, roger, and put an index.js file there. Remember - that if you want 'roger' to be dynamic, you would create a dynamic folder like [userName] instead of roger.
Retrieving the Data
You could then access the dynamic properties in each index.js file by acceding the router's query property. For example:
import { useRouter } from 'next/router'
const Post = () => {
const router = useRouter()
const {userId, dynamic} = router.query
return <p>
userId: {userId} <br />
dynamic: {dynamic}
</p>
}
export default Post
You would obviously replace the {userId, dynamic} with the names you chose for the dynamic routes (i.e - the names of the folders).
(Example adapted from the link above)
In Next js your route endpoints are defined by the file name thus making it quick and easy to create a new route.
Now let's say that you want to create a route like this "/profile/:id"
with id being your custom parameter.
To create a file with a custom parameter just surround your custom parameter with brackets like this [id]
You can also use the three dot syntax (like this [...id]) to accept infinite unspecified dynamic pages.
I would suggest watching the video linked down below which really helped me get started with Next js.
nextjs crash course
Related
We are about to build a new react based frontend layer for a large and complex news website. We are considering using nextJS, but one thing that puzzles me is the built-in file based routing in nextJS. In our website, content always belong to a section and sections are represented by the path in the URL.
Example:
/regions/region-A
/regions/region-B
…
/area-of-expertise/tax
/area-of-expertise/macro-economics
…
The sections (regions, region-A, region-B, area-of-expertise, tax and macro-economics) are created in our CMS and there could be various type of content in every section.
The “tax”-section (that is a subsection of “area-of-expertise”) could contain different types of content, e.g., regular news articles and reports. The visual representation of a news article and a report is different, but there is no way to tell the difference between them by just looking at the URL. Example:
/area-of-expertise/tax/title-of-news-article_id.html
/area-of-expertise/tax/title-of-report_id.html
Considering the above, would you still consider nextJS to be a good alternative for us? Would you recommend using another routing library?
I don't know if NextJS is the best framework for your needs, but you may want to look into per-page layouts, which would allow each individual page to optionally specify it's layout.
You'd define a NewsArticleLayout and a ReportLayout, then have your components for each route define a getLayout function.
For example, /area-of-expertise/tax/title-of-report_id.html could look something like this:
import Layout from '../../../components/layouts/Layout';
import ReportLayout from '../../../components/layouts/ReportLayout';
export default function Page() {
return (
/** Report content goes here */
)
}
Page.getLayout = function getLayout(page) {
return (
<Layout>
<ReportLayout>{page}</ReportLayout>
</Layout>
)
}
For what do we need the templates folder in Gatsby project file structure? Can I use this folder for storing ordinary components that I reuse on a home page such as "hero" or "info" components ets?
The /src/templates folder doesn't do anything in particular (unlike the /src/pages folder for example), but it's normally used to store templates for pages that you're creating programatically. You could also simply store any page templates in a /src/components folder and remove /src/templates altogether, if you prefer.
The purpose of /templates folder is to store templates (huh?), that is to say, all pages that would be created programmatically via createPage API and will share the same internal structure (components, header, footer, etc) but different content. Ideally, you will have there a post.js, legal.js (for all terms and conditions, terms of service, etc) in order to share the same structure for all your retrieved data from CMS, like:
posts.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/posts.js`),
context: { id: node.id },
})
})
}
It is only used to structure the code and create a more clean structure. For me, rather the Robin Métral answer I would keep it, if you don't use it and remains empty the file won't be committed but is really useful and the programmatic page creation is one of the main advantages of Gatsby, but also is really helpful to keep a clean and clear structure.
I have the need to be able to render any of the pages in my AEM model.json dynamically regardless of the current URL in a SPA React app.
My AEM model.json structure has pages following the /<country>/<language>/rest/of/path format, but I want to be able to strip the country/language and just use the rest of the URL path.
I am able to do this when I initialize the ManagerModel with a the desired path like this:
const path = `/path/to/<my_model>.model.json`
/* initialize the ModelManager with the path to the model.json */
ModelManager.initialize({ path })
/*
grab the desired section of the model and render by calling ReactDOM.render
By doing this I am able to render the section of the model that maps /us/en/user-account` for
example, and render the correct content even though the current browser path is `/`
*/
ModelManager.getData(`/us/en/<page_to_load>`).then(render)
When I handle navigation with history.push (I use react-router), I want to be able to render another page following the same logic. By default, having executed ModelManager.getData("/us/en/<page_to_load>"), every page that I navigate to then renders that same portion of the model.
To fix this, I have tried many variations of ModelManager.getData() calls with no success. The only thing that I have been able to have any success with is dynamically passing the path to the next page to render to a callback function that is defined on the index.js level and passed down as a prop to App.js. The callback triggers another ReactDOM.render call and loads the page correctly regardless of what the actual URL path is. That code looks something like this:
<App
reRender={(path) => {
/* manipulate the path so that it properly maps to the correct AEM model data */
const updatedPath = `/us/en/${path}`
/*
this works, but causes another ReactDOM.render call every time that the current page is
changed
*/
ModelManager.getData(updatedPath).then(render)
}}
/>
There are also cases where the page that has been navigated to doesn't have a corresponding path in the modelStore mapping. I am able to handle that like this:
const pathToInsert = `/<country>/<language>/${window.location.pathname}.model.json`
ModelManager.modelStore.insertData(pathToInsert)
ModelManager.getData(pathToInsert).then(render)
/*
I have been having issues with this, but can get the newly inserted model to load properly by
re-routing back to the current path from the current path
*/
this.props.history.push(window.location.pathname)
I have read and re-read the documentation here and I am unable to figure out the correct way to do what I want to do. The above solutions work for the most part, but are pretty hacky and I would like to find out the proper way to accomplish this. Any help is greatly appreciated!
Yes, I found a work around solution. The AEM library can't handle this requirement out of the box unfortunately. My solution was to make a wrapper for my App component. In that wrapper I initialize the model manager, clone the data and store it in local stage. Then you can conditionally modify the model and pass it along as a prop to your App component. If you have a good understanding of how AEM model data is mapped you should be able to figure out a way to display what you want. You can also fetch and insert models into your master model's ":children" prop (think that is the field name, have not looked in a while).
I created a Gatsby/React instance sourcing from a Wordpress instance. I generate pages based on the page objects that I source from Wordpress. Everything works fine. I know that I have to put my content for the route / into src/pages/index.js. What would be the correct/canonical way to define one of my sourced page routes e.g./start as /?
Thanks in advance.
Basically you just call createPage with the path set to /. How you handle this in your CMS really depends on the structure you've set.
exports.createPages = ({ actions }) =>
actions.createPage({
path: "/",
component: path.resolve("./src/templates/some_template.jsx"),
})
I tend to prefer to require preceding and trailing slashes on slugs (e.g. /about/) in the CMS, which makes it easy to allow a content editor to leave just / for a page that is destined to become the home/root page. In the past I've also used home as a magic value that would be converted to / in gatsby-node.js, but it can cause confusion.
I carefully read the docs of next routing system.
It only mentions that I could achieve dynamic routing like this:
http://localhost:3000/level1/dynamicSlug
But I am trying to achive something like this:
http://localhost:3000/level1/level2/dynamicSlug
And I want level2 to be created dynamic too
Thanks so much !
It is possible to do nested scenarios according to your request in this way.
for example:
pages/
level1/
[dynamicSlug]/
- index.js // will match for /level1/1234
level2/
- index.js // will match for /level1/level2
- [dynamicSlug].js // will match for /level1/level2/1234
Or
pages/
level1/
[dynamicSlug]/
- index.js // will match for /level1/1234
level2/
- index.js // will match for /level1/level2
[dynamicSlug]/
- index.js // will match for /level1/level2/1234
You have 2 choices:
Using v9 Dynamic Routing by calling the folder as [dynSlag] and putting your page file inside.
Using custom server and routing, you will need to define a custom server, map your path to a specific next page.
I know this is a bit of an old post, but I'd just like to share my working response with NextJS v11.
I want dynamic routing at two levels. E.g.:
{siteroot}/dynamicPage
{siteroot}/dynamicUrlSection/dynamicPage
My folder structure is:
/pages/[section]/[page].tsx
/pages/[section]/index.tsx
This way, the "dynamicPage" path at the root is handled by index.tsx, and nested routes are handled by [page].tsx
BONUS INFO: I am working with Contentful as a CMS. I use a single content model for all the pages at both levels.
The model has "section" and "page" properties.
The entries that serve the root dynamic pages (i.e. /pages/[section]/index) have a compound value in the "page" property of {section}-index. I then have to be a bit smart in my client code:
if (!page) {
page = `${section}-index`;
}
await fetchData(section, page);
Using the example right from the NextJs documentation, I use this hack, maybe you could use it.
<Link href="/posts/[id]" as={`/posts/${subFolder}${id}`}>
"as" will have a value like /posts/nested_subfolder_file.md
And in the getPostData function, just do this little change:
const nestedPaths = id.split('_')
const fileName = `${nestedPaths.pop()}.md`
const fullPath = path.join(postsDirectory, ...nestedPaths, fileName)