Section based routing in nextJS - reactjs

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>
)
}

Related

How to share on social media with logged user content?

I'm new using React, so please excuse me for the basic question. I didn't know where to find the right answer (or even how to ask the proper question). My doubt is: I have a React app where the user will have his content, which is basically a text and an image (using base64). He must be logged in to access it. I'm saving the content inside Firebase, as a JSON file with logged user.id generated by Firebase, as the category, and each user content is composed of an UUID I generate, the image (base64), and a text.
So, main page I list all the content for each logged user, and I want to put a button there so (s)he can share his content on Facebook, for instance. This "content" I'm calling a user story. So, for each listed story I create a Link to load this particular story in another page:
arrayOfStories.map((story) => (
(...)
<Link className="logo-container" to='/loadedstory' state={story}>
))
I'm passing the story object, which is basically the JSON with storyText, image, and uuid properties in a useLocation() state.
Then, from my loadedstory.component.jsx context page, I do this:
const LoadedStory = () => {
const {state} = useLocation();
(...)
return (
<>
<div>
{ state && state.uuid ? (
<div className="story-content"><img alt="Your content!" src={`data:image/png;base64,${state.image}`} className='resize-image'></img></div>
<div className="story-content"><p><span className='stroke'>{state.storyText}</span></p></div>
(...)
):
(<div></div>)}
</div>
(...)
</>
As you can see, if the user wants to share this particular story on Facebook, the base URL link will be /loadedstory and won't load anything without the logged user context from useLocation().
What's the best practices for developing such shareable content within a logged context?
I was thinking about to do something like /loadedstory/:uuid to use the story UUID from each story and query Firebase everytime this context is needed, but the problem is my Firebase category is the user.id not the story.uuid. Would I need to query the whole users in the database then for this UUID? Is this the right approach?
If you 're using NextJS, you could probably keep the data as-is and generate a static page for every user and all their stories, and the pages pointing to those stories.
You could pass the user-id and story-id to the story page. That way the story can fetch the (all) the user's data and sift through it to find the story-id. If you use something like ReactQuery, it will cache so you won't feel so bad about asking for data you already have.
As far as 'best practice', generally you would make a new DB table to store each story as its own record with the user-id as an owner. You use something like GraphQL to fetch only a user, only a story, or a user and their first 25 stories.
You also need a "router" to facilitate navigating around (pages or content) without re-inventing the wheel.
One very popular solution is to build the app using NextJS. It's easy to get up and running and their starter example covers navigation.
Many projects use React Router v6 and it's well worth running through their tutorial whether you'll end up using it or not.
If you want extra type safety, check out the TanStack Router. It's new and built on the shoulders of many great ideas that preceded it.

Next js multiple dynamic pages

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

Routing localization with NextJS

I'm migrating a website developed with Gatsby to NextJS, but something I could achieve using Gatsby's createPage API is localizing the app's routes, but until now I couldn't achieve this with NextJS APIs.
I'm using Next v10.0.1 for this.
As I see in other threads regarding this type of resource, this is actually kinda confusing of what it actually means, so here goes an example of what is the desired result:
User access route /my-data/1234 (where the NextJS equivalent routing would be: /my-data/[dataId].js)
User must be able to access the same page but translated in the URL /pt/meus-dados/1234 (using, for example, portuguese translation).
Some guesses on how to achieve that keeping Next's static optimizations (Static rendering and Incrementing Static Rendering)?
I actually found an answer which is pretty useful for my use case, I'm using NextJS rewrites for that, maybe not the best solution, but fits my needs well.
I use a single file for each route, so the directory structure should be something like this:
pages
-- my-data
-- [id].js
then I'll have some kind of internationalization, in my case I'm using react-i18next, won't think about the aspects of implementations for the library here, it could also be achieved with any other.
Next step is to set a translation somewhere for the pages routes, for example, add an entry for the i18next messages named routes containing a key-value pair for the routes translations. e.g (intl/pt.json):
{
...
"routes": {
"/my-data/:id": "/meus-dados/:id"
}
...
}
and then use NextJS rewrites, so you will have to import the intl messages (e.g: from intl/pt.json) and map them as rewrites in next.config.js:
# next.config.js
...
async rewrites() {
// languages here is a key-value pair object containing the structure { [language]: { routes: {...} } }
// in my case, imported from `intl/pt.json`
const intlRewrites = Object.entries(languages).reduce((rewrites, [ language, { routes } ]) => {
return [
...rewrites,
...Object.entries(pages).map(([ path, translatedPath ]) => {
const source = translatedPath
const destination = path // here you can write a logic to add a language prefix if needed
return { source, destination }
})
]
}, [])
return intlRewrites
}
From my experience, optimizations like ISG work fine. Should work with SSG too, but I haven't tested it.
I've been tackling this exact problem, and whilst I don't yet have a solution that integrates directly into NextJS, this can be achieved fairly simply before your project is compiled.
If you were to organise your pages directory as follows, it should work as you expect:
// Before
pages
-- my-data
-- [id].js
// After
pages
-- pt
-- meus-dados
-- [id].js
-- en
-- my-data
-- [id].js
However the developer experience here isn't nice. So what I have done to solve this currently is written a simple build step that runs before next build. It takes a regular pages directory and converts it to the above format, allowing next build to run against a version that works as intended for the translated paths. This allows SSG and ISG to work as expected.
Ideally I'd like to hook into the Next ecosystem so this works seamlessly for dev and build, but I haven't yet gotten that far

2 levels nested routing in nextjs

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)

Reactjs overide markdown types with react-markdown

I am using contentful to get markdown to a react component that uses react-markdown to parse the markdown
import ReactMarkdown from 'react-markdown';
<Markdown source={text} />
Would I like to do is to override the Renderer so instead of it rendering ## as an h2 render i can pass a custom component to override the default h2 type to my own h2 component. How can i do that and is there and examples?
One of the options to <ReactMarkdown> is renderers.
One of the common renderers handles headings. If you look at the default rendering you'll see this:
heading: function Heading(props) {
return createElement('h' + props.level, getCoreProps(props), props.children);
},
So pass in your own heading handler. Check the level inside, roughly:
function CustomHeading(props) {
if (props.level !== 2) {
return createElement(`h${props.level}`, getCoreProps(props), props.children);
}
return <MyCustomElement {...props} />
}
If you don't have access to the code that commonmark-react-renderer gives you in the context of your function (which you probably won't) then you'd also need to duplicate what createElement gives you (but it's simple).
Unrelated: I've never used <ReactMarkdown> (but will), but this took me about five minutes of research. I'm including my path to encourage others to dig into their own questions and hopefully give some insight into how such things can be researched.
The react-markdown home page
Scanned through the "Options" section to see if custom rendering was trivially supported
Found the renderers option, which sounded promising
Clicked the link provided in that option's docs
Saw that heading was one of those (which made sense; I'd expect a renderer for every major formatting that Markdown supports)
Opened up the src directory to see if the implementation was easy to find
There was only one file, so I opened it
Searched the page for "heading" and found it
Cut and pasted that code here
The ability to read docs and follow trails is really important.

Resources