7.9 KiB
Coding 3
Link Component Navigation
- To enable client-side navigation Next.js provides us with the Link component.
- The
<Link>component is a React component that extends the HTML<a>element and is primary way for navigation - To use it, we need to import it from 'next/link'. Structure
src/app
blog
page.jsx
products
page.jsx
page.jsx
In src/app/page.jsx
import Link from "next/link";
const Home = () => {
return (
<>
<h1> Hello </h1>
<Link href="/blog">Blog</Link>
<Link href="/products">Products</Link>
</>
);
};
export default Home;
In src/app/blog/page.jsx / path is used to go to root page
import Link from "next/link";
export default function Blog() {
return (
<>
<h1>Blog</h1>
<Link href="/">Home</Link>
</>
);
}
For Dynamic Routes Structure
src/app
blog
page.jsx
products
[productId]
page.jsx
page.jsx
page.jsx
In src/app/products/page.jsx
import Link from "next/link";
export default function ProductList() {
const productId = 100;
return (
<>
<h1>Product List</h1>
<h2>Product 1</h2>
<h2>Product 2</h2>
<Link href={"products/3"} replace>
{" "}
<h2>Product 3</h2>{" "}
</Link>
<Link href={`products/${productId}`}>
{" "}
<h2>Product {productId}</h2>{" "}
</Link>
</>
);
}
- Link for Dynmaic routes is generated just like we access the value of variable.
With
replacekeyword we can clear the url stack history means after navigating tolocalhost:3000/products/3and when we click back button we will navigate tolocalhost:3000notlocalhost:3000/products.
Active Links
- First we need to create links for login, register, forgot-password in layout.jsx page for
(auth). So instead of creating these 3 links for each page ie login, register and forgot-password we used layout.jsx to serve this as template/same for all route groups. Structure
src/app
(auth)
login
password
forgot-password
layout.jsx
page.jsx
In src/app/(auth)/layout.jsx
import Link from "next/link";
const navLinks = [
{ name: "Register", href: "/register" },
{ name: "Login", href: "/login" },
{ name: "Forgot Password", href: "/forgot-password" },
];
export default function OuterAuthLayout({ children }) {
return (
<div className="">
{navLinks.map((link) => {
return <Link href={link.href}>{link.name}</Link>;
})}
{children}
</div>
);
}
- So in order to style it we need to know the path of current url so we need to use the
usePathnamefrom 'next/navigation' hook. As we r using hook we need client side rendering for this and have to typeuse clientat top in jsx file. Structure
src/app
(auth)
login
password
forgot-password
layout.jsx
style.css
page.jsx
In src/app/(auth)/layout.jsx. Use the particular class for active link
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import "./style.css";
const navLinks = [
{ name: "Register", href: "/register" },
{ name: "Login", href: "/login" },
{ name: "Forgot Password", href: "/forgot-password" },
];
export default function OuterAuthLayout({ children }) {
const pathname = usePathname();
return (
<div className="">
{navLinks.map((link) => {
const isActive = pathname.startsWith(link.href);
return (
<Link
href={link.href}
key={link.name}
className={isActive ? "active-link" : ""}
>
{link.name}
</Link>
);
})}
{children}
</div>
);
}
In src/app/(auth)/style.css
a {
margin: 0 1rem;
color: blue;
}
a.active-link {
color: black;
}
Navigating Programitically
- Instead of using jsx, javascript can be used too so for that we can use the hook ie
useRouterfrom 'next/navigation'. Structure
src/app
order-product
page.jsx
page.jsx
In src/app/order-product/page.jsx
"use client";
import { useRouter } from "next/navigation";
export default function OrderProduct() {
const router = useRouter();
const handleClick = () => {
console.log("order placed");
router.push("/");
};
return (
<>
<h1>Order Product</h1>
<button onClick={handleClick}>Place Order</button>
</>
);
}
router.push('/') will push the root route to url stack and when we click on back after going through homepage it will brink back to the page localhost:3000/order-product. In Link coponent there was a attribute named replace which clears the url stack so to do same programitically use router.replace('/'). Not only that but we can go to previous or next url stack too by router.back() and router.forward()
Templates
- Templates are similar to layouts in the way they wrap each child layout or page But with templates when a user navigates between routes that share a template, a new instance of component is mounted, DOM elements are recreated, state is not preserved.
- So to only to be used once we dont have to conserve states and need a template to recreate pages so their states dont conflict with each other Without template
src/app
(auth)
login
password
forgot-password
layout.jsx
style.css
page.jsx
In src/app/(auth)/layout.jsx
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import "./style.css";
import { useState } from "react";
const navLinks = [
{ name: "Register", href: "/register" },
{ name: "Login", href: "/login" },
{ name: "Forgot Password", href: "/forgot-password" },
];
export default function OuterAuthLayout({ children }) {
const pathname = usePathname();
const [input, setInput] = useState("");
return (
<div className="">
<div className="">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
/>
</div>
{navLinks.map((link) => {
const isActive = pathname.startsWith(link.href);
return (
<Link
href={link.href}
key={link.name}
className={isActive ? "active-link" : ""}
>
{link.name}
</Link>
);
})}
{children}
</div>
);
}
When we move to new route input will be same and wont be reset. With templates Just change the file name from layout.jsx to template.jsx and now input will be reset and nothing will be preserved. Some limitations though. If there is a route group(say named inner-auth) like this Structure
src/app
(auth)
(inner-auth)
login
password
forgot-password
template.jsx
style.css
page.jsx
In this case, states between login and password routes will be shared not with forget-password route though.
Loading UI
- Till now different files like
page.jsx,layout.jsx,not-found.jsxandtemplate.jsxhave been used. There is another one ieloading.jsx. Structure
src/app
blog
first
second
loading.jsx
layout.jsx
page.jsx
page.jsx
In src/app/blog/loading.jsx
export default function LoadingBlog() {
return (
<div className="LoadingBlog">
<h1>Loading</h1>
</div>
);
}
To test it, make sure that src/app/blog/page.jsx loads slower due to setTimeout. Make sure that layout.jsx wont load slow as first layout.jsx is loaded then loading.jsx contents then page.jsx.
In src/app/blog/page.jsx
import Link from "next/link";
export const metadata = {
title: {
absolute: "blog",
default: "blog",
},
};
function promiseSetTimeOut(time) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, time);
});
}
export default async function Blog() {
await promiseSetTimeOut(1000);
return (
<>
<h1>Blog</h1>
<Link href="/">Home</Link>
</>
);
}