I have a Nextjs application. The app will grow to a nice size (200 pages)
What is the best way to manage links. Some pages will have navigation buttons and and hrefs, hyperlinks.
I am looking for solution to manage
Example: pages -> about, contact, support, home, blog. But when you have 200 pages and you are hardcoding if I delete or change the about page name I have to go in all 200 pages and update this
Navigate with click :
import { useRouter } from "next/router";
//...
const router = useRouter()
<button
onClick={() => {
router.push('/url')
}}
>Navigate</button>
Navigate with Link :
import Link from 'next/link';
//...
<Link href="/url">Navigate</Link>
And if you have 200 url you should make 200 links you cannot optimize that, but you can create a file for all your link then import it everywhere and when you have to update one path you do it inside this file :
import Link from "next/link";
export const HomeLink = () => {
return <Link href="/">Navigate To Home</Link>;
};
export const SettingsLink= () => {
return <Link href="/settings">Navigate To Settings</Link>;
};
//...
and from the file in which you want to navigate :
import { HomeLink, SettingsLink } from "../your_path";
//...
<HomeLink />
<SettingsLink/>
I create a routes file with all my hard-coded routes and use the const throughout my app. I use trailing slash, so they need to be appended to all routes.
This setup works well for me, and I've used it on numerous projects with hundreds of routes. It saved a lot of time over the years when inevitably, I needed to change a route's href or name.
My routes file is similar to this:
export const ROUTE_HOME = {
href: "/",
name: "Home",
};
export const ROUTE_ABOUT = {
href: "/about/",
name: "About",
};
I use them like
import Link from 'next/link';
import {ROUTE_HOME, ROUTE_ABOUT} from 'your/path/to/routes';
<Link href={ROUTE_HOME}>{ROUTE_HOME.name}</Link>
<Link href={ROUTE_ABOUT}>{ROUTE_ABOUT.name}</Link>
On larger projects, I break the routes out into separate files.
src > const > routes.ts // top-level routes
src > modules > auth > const > routes.ts // only auth routes
Related
I made a simple site with Gatsby.js and can't configure dynamic routes.
I have index.js page (was automatically created by react), that looks like:
import * as React from 'react'
const IndexPage = () => {
return (
<Layout
pageTitle="Home Page"
>
Some text for my main page
</Layout>
)
}
export const Head = () => <title>Home Page</title>
export default IndexPage
Layout components includes Header that looks like:
import React, { useState, useEffect } from 'react';
import { Link } from 'gatsby';
const Header = () => {
return (
<Wrapper style={{ *some styles* }}>
<Link to="/">Index</Link>
<Link to="/projects">Projects</Link >
</Wrapper>
)
};
export default Header;
I have my Projects page that looks like this:
import * as React from 'react'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
import Layout from '../layout'
const Projects = () => {
return (
<BrowserRouter>
<Layout>
<Wrapper>
<Routes>
<Route path="projects/:projectID/" component={ProjectDetails} />
</Routes>
<MyProjectLink to="/projects/1">
Project 1
</MyProjectLink>
<MyProjectLink to="/projects/2">
Project 2
</MyProjectLink>
</Wrapper>
</Layout>
</BrowserRouter>
)
}
export const Head = () => <title>Our Projects</title>
export default Projects
And I have my ProjectDetails component:
import React from 'react';
import { useParams } from 'react-router';
import Layout from '../../pages/layout';
const ProjectDetails = () => {
const { projectID } = useParams();
return (
<Layout>
<Wrapper>
<h2>Project {projectID}</h2>
</Wrapper>
</Layout>
);
}
export default ProjectDetails;
The problem is that when I navigate to localhost:8000/projects/1 (or "2", or "3", or "100500") I see a 404 page instead of my ProjectDetails component.
I've tried wrapping the index page to BrowserRouter and move the routes with my route there, but that's a dumb idea in my opinion (and it doesnt work).
Did I miss something? Some features of Gatsby (v5) that I don't know about? I'm new to Getsby and to be honest I thought that dynamic routes here work the same way as in React Router.
Gatsby extends its routing from React, however, the way to create routes is slightly different.
As far as I understand your code, you are trying to create a template page for projects: this can be simply done by creating a file inside /templates folder. A simple component like this should work:
const Projects = ({ data }) => {
return (
<Layout>
<Wrapper></Wrapper>
</Layout>
);
};
export const Head = () => <title>Our Projects</title>
export default Projects
This template, as long as you use it when creating pages (using either gatsby-node.js or File System Route API) will be used to hold each specific project data.
Each project data will be queried using GraphQL and held inside props.data but without knowing your source (can be markdown, JSON, CMS, etc) I can't provide a sample query.
Once Gatsby infers its GraphQL nodes from your data source, you can use it to get all projects, a specific project, or any other GraphQL data you need on any page/template (page query) or even using static queries.
The idea should be similar to:
// gatsby-node.js
projects.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/templates/project.js`),
context: {
title: node.title,
},
})
})
In your gatsby-node.js (or File System Route API) you get all projects, loop through them and createPage for each project. The path (URL) for each project will be the slug field (node.fields.slug) but you can use whatever you want. Gatsby will create dynamic pages based on this field.
Then you decide which component will be used as a template: path.resolve(`./src/templates/project.js`) in this case and finally, you populate the context to add a unique value (title in this case: again, this can be an id, the slug, etc). This value will be used to filter the node in the template.
In your Project template:
export const query = graphql`
query ($title: String) {
mdx(title: {eq: $title}) {
id
title
}
}
`
In this case, I'm using markdown-based sources (that's why the mdx node) and this node is filtered by the title ($title) using the context value. The data will be inside props.data of the template. Again, if you want to fetch all projects you will have available an allMarkdown or allMdx (or allJSON...) depending on the source node)
The question is clear but I would like to give my use case for better understanding.
I'm creating a personal blog app and my blog posts will in .mdx format. These blog post .mdx files reside in a directory in the code base called posts. I can render any .mdx file in the following BlogPost component for each url like /posts/post1.
import React, { Suspense } from 'react'
import { useParams } from 'react-router-dom'
export const BlogPost = () => {
let params = useParams()
const Post = React.lazy(() => import(`../../posts/${params.postId}.mdx`))
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<Post />
</Suspense>
</div>
)
}
And my file structure is like this:
/posts
--post1.mdx
/src
--/views
-----BlogPost.tsx
-----BlogList.tsx
So, whenever I want to add a new blog post I basically add a new .mdx file to the repository. However I don't want to edit any component. I mean, I don't want to have a variable that keeps list of the list of blog posts like following.
export const listOfBlogPosts = ["post1", "post2", ...]
Because it will need to be updated every time I add a new post. Instead I would like to get list of files dynamically in BlogList component.
import { Link } from 'react-router-dom'
import style from './BlogList.module.css'
export const BlogList = () => {
const listOfBlogPosts = ['post1', 'post2'] // get somehow dynamically
return (
<div className={style}>
{listOfBlogPosts.map((post) => {
return <Link to={post}> {post}</Link>
})}
</div>
)
}
How can achieve this? Is there a logical problem with my approach(i.e keeping dynamic files in the repo)? Any suggestion or comment would be appreciated
I made one react app. My app works as expected. This app's target is practice AWS-COGNITO. For Cognito validation I am using amazon-cognito-identity-js package. I made one helper function where I validate the Congnito. and reuse it in different component. I split my Nav bar into two components. From Congnito current user I made one callback function and use it in useEffect, and dependencies put the callback function, by default getAuthenticatedUser is null. I add condition where it fetch the data, if getAuthenticatedUser then redirect to signin and signup page. I deployed my app to s3 bucket and this the link. This app runs first time, When I refresh it then got error: 404 Not Found. I really don't know what is the issue and somehow the path react path get disappear. I share my code in code-sandbox.
This is my conditional path
import React from "react";
import SigninLinks from './SigninLinks';
import SignoutLinks from './SignoutLinks';
import useHandlder from '../configHandler/useHandler';
const Nav = () => {
const { getAuthenticatedUser } = useHandlder();
const Links = getAuthenticatedUser() === null ? <SignoutLinks /> : <SigninLinks />
return (
<nav className="nav-wrapper grey darken-3">
<div className="container">
<h2 className="brand-logo">Logo</h2>
{
Links
}
</div>
</nav>
);
};
export default Nav;
This is my handler functions
import React, { useCallback, useEffect } from 'react';
import { CognitoUserPool } from 'amazon-cognito-identity-js';
const Pool_Data = {
UserPoolId: "us-east-1_9gLKIVCjP",
ClientId: "629n5o7ahjrpv6oau9reo669gv"
};
export default function useHandler() {
const userPool = new CognitoUserPool(Pool_Data)
const getAuthenticatedUser = useCallback(() => {
return userPool.getCurrentUser();
},
[],
);
useEffect(() => {
getAuthenticatedUser()
}, [getAuthenticatedUser])
const signOut = () => {
return userPool.getCurrentUser()?.signOut()
}
return {
userPool,
getAuthenticatedUser,
signOut
}
};
It's paths issue. You get 404 on /path not in root /. Check S3 settings for hosting static sites. On S3 make sure static website hosting is enabled:
You react app loads on /index.html JavaScript then redirects and takes over the path. You need S3 to resolve path to index.html, then it will work.
How to change base url in create-react-app? So that the application does not open with localhost:3000/, but localhost:3000/myurl?
You will need to do a lot of adjusting if you want react to launch in a subdir. like changing path of assets, components etc. This Guide is a good starting point
on how to accomplish your needs. Again Its not recommended, if you want like to switch to /myUrl as soons as it launches, you could do a componentDidMount() and force it there.
Assuming you are using react-router, you can achieve that by using a Redirect to navigate your app from / to /myurl.
First, declare a route config like this:
// routers.js
import React from "react";
import { Redirect } from "react-router-dom";
export const redirectRoutes = [
{
path: "/",
exact: true,
// handle redirect case: / -> /myurl/
render: () => <Redirect to={`/myurl`} />
}
]
Then, inside your App.js:
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import { redirectRoutes } from "./routers.js";
// generate app reidrect routes
const redirectRouteComponents = redirectRoutes.map(route => (
<Route
key={route.path}
path={route.path}
exact={route.exact}
render={route.render}
/>
));
const App = () => <Router>{redirectRouteComponents}</Router>;
I have a VPS with Apache + Cpanel.
I can't configure Nginx over it, so the only way, as far as I know, is to 'static export' first then deploy it.
Turns out I can't access the product page by link pasted on url bar directly (not by click a link text).
The link is look like this : www.example.com/products/4 or www.example.com/products/213
My first suggestion is because I 'static export' the project.
I use next-routes with <Link />
My code
import React, { Component } from 'react';
import { withRouter } from 'next/router';
import { connect } from 'react-redux';
import fetch from 'isomorphic-fetch';
import Navbar from '../components/Navbar';
import Footer from '../components/Footer';
import CheckoutBody from '../components/CheckoutBody';
class Product extends Component {
static async getInitialProps({ query }) {
let { id } = { ...query };
if (id === undefined) id = 14;
const res = await fetch(`http://www.example.com/api/product?id=${id}`);
const data = await res.json();
return { campaignDetail: data };
}
render() {
let { lang } = this.props;
return (
<React.Fragment>
<Navbar />
<CheckoutBody
key={this.props.productDetail.id}
productDetail={this.props.productDetail}
lang={lang}
/>
<Footer />
</React.Fragment>
);
}
}
export default Product ;
Same question but different problem: https://github.com/zeit/next.js/issues/9893
I have tried this to .htaccess. It is not working. I am very newbie to regex and htaccess.
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule !.*\.html$ %{REQUEST_FILENAME}.html [L]
What should I do?
Is it what it's called dynamic routes?
The issue might be related to using next export rather than rewrite rule configuration. What we found is nextjs router pathname is not populated with the expected route on first hit if you use next export. Until this issue is fixed within nextjs, you can use a provider in _app.js that wraps your components and adjusts the route or put this before the return statement inside you _app.js default function:
import { useRouter } from 'next/router'
const { asPath, push, pathname } = useRouter()
if (asPath.split('?')[0] != pathname.split('?')[0] && !pathname.includes('[')) {
// Work around for next export breaking SPA routing on first hit
console.log('Browser route ' + asPath + ' did not match nextjs router route ' + pathname)
push(asPath)
return <div>Loading...</div>
}