for contenxt, I'm building a website for a pet rescue charity, I'm using Next.js. I'm new to Nextjs and can't figure out how to get my button or a link to actually work propperly I am currently getting a 404 error This page could not be found, when I'm trying to get the button to take me to another page I've built in my next app.
I've tried a few things.
Can someone help ??
here is the card ponent:
import React from 'react';
import Image from 'next/image';
import Button from '../ponents/Button';
import Link from 'next/link';
export default function PetCard({ pet, onClick }) {
return (
<div className="flex flex-col border p-4 rounded-lg hover:shadow-lg transition-shadow bg-white" >
<Link href="/[petId]" as={`/adote/${pet.name}`} passHref>
<Button
label="Ver perfil"
onClick={onClick}
className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600 w-32 flex flex-row justify-center items-center"
/></Link>
</div>
)
}
The button is supposed to go to this petId page:
'use server'
import React from 'react';
import { useRouter } from 'next/router';
export default function PetDetailPage() {
const router = useRouter();
const pet = JSON.parse(router.query);
return (
<div className="p-4">
<Image src={pet.avatar} alt={pet.name} width={600} height={600} className="rounded-md" />
<h1 className="text-2xl font-bold mt-4">{pet.name}</h1>
<p className="text-gray-600">{pet.breed}</p>
<p className="text-gray-600">{pet.id}</p>
<p className="text-gray-600">{pet.age}</p>
<p className="text-gray-600">{pet.gender}</p>
<p className="mt-2">{pet.description}</p>
</div>
);
}
I don't mind if its just a placeholder page for now. I have this mock data from a mock api I've made and it fetches with no issues on the main adoption page:
'use client';
import React from 'react';
import CustomizableHero from "../../ponents/HeroScondary";
import PetCard from "../../ponents/PetCard";
import Button from '../../ponents/Button';
import { useState, useEffect } from 'react'
export default function Adote() {
// mock API
const [pets, setPets] = useState([])
const [loading, setLoading] = useState(true)
const [page, setPage] = useState(1)
useEffect(() => {
const fetchPets = async () => {
setLoading(true)
const url = new URL('')
url.searchParams.append('pleted', false)
url.searchParams.append('page', page)
url.searchParams.append('limit', 18)
const res = await fetch(url)
const data = await res.json()
setPets(prevPets => [...prevPets, ...data])
setLoading(false)
}
fetchPets()
},
[])
if (loading) {
return <p>Loading...</p>
}
const handlePetClick = (petId) => {
router.push(`/adote/${petId}`)
}
return (
<div className="relative">
<CustomizableHero
backgroundImage="/images/dois-cachorros.png"
title="Adote"
subtitle="Encontre o seu melhor amigo"
color="white"
/>
{/* search filter */}
<div className="flex flex-row">
</div>
{/* pet cards */}
<div className="absolute w-[100vw] max-w-screen flex flex-col justify-center ">
<div className="flex flex-col text-align-center items-center justify-between m-10">
<h2 className="text-5xl font-bold tracking-tight sm:text-5xl text-gray-500 p-5">Animais buscando uma familia</h2>
<p className="mt-6 mb-3 text-md leading-8 text-gray-500 text-center p-5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nulla ut condimentum maximus, felis enim rhoncus justo, vel venenatis magna tortor nec tortor. Donec vulputate fermentum fermentum. Curabitur odio dui, consectetur vitae ornare eu, ultricies vel risus</p>
</div>
{/* Called displayPetCards() inside JSX code to render the PetCard ponents. */}
<div className="grid grid-cols-3 gap-4 m-10 p-5">
{pets.map(pet => (
<PetCard
key={pet.id}
pet={pet}
onClick={()=> handlePetClick(pet.id)}
/>
))}
</div>
<p className="flex flex-col text-center mt-5">Visualizados 18 de 661 animais de estimação</p>
<div className="flex justify-center m-10">
<Button
label="Mostrar mais"
onClick={() => setPage(page + 1)
} // Attach the click handler here
className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600"
/>
</div>
</div>
</div>
);
};
I've tried useRouter, I've tried getServerSideProps() but nothing works
for contenxt, I'm building a website for a pet rescue charity, I'm using Next.js. I'm new to Nextjs and can't figure out how to get my button or a link to actually work propperly I am currently getting a 404 error This page could not be found, when I'm trying to get the button to take me to another page I've built in my next app.
I've tried a few things.
Can someone help ??
here is the card ponent:
import React from 'react';
import Image from 'next/image';
import Button from '../ponents/Button';
import Link from 'next/link';
export default function PetCard({ pet, onClick }) {
return (
<div className="flex flex-col border p-4 rounded-lg hover:shadow-lg transition-shadow bg-white" >
<Link href="/[petId]" as={`/adote/${pet.name}`} passHref>
<Button
label="Ver perfil"
onClick={onClick}
className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600 w-32 flex flex-row justify-center items-center"
/></Link>
</div>
)
}
The button is supposed to go to this petId page:
'use server'
import React from 'react';
import { useRouter } from 'next/router';
export default function PetDetailPage() {
const router = useRouter();
const pet = JSON.parse(router.query);
return (
<div className="p-4">
<Image src={pet.avatar} alt={pet.name} width={600} height={600} className="rounded-md" />
<h1 className="text-2xl font-bold mt-4">{pet.name}</h1>
<p className="text-gray-600">{pet.breed}</p>
<p className="text-gray-600">{pet.id}</p>
<p className="text-gray-600">{pet.age}</p>
<p className="text-gray-600">{pet.gender}</p>
<p className="mt-2">{pet.description}</p>
</div>
);
}
I don't mind if its just a placeholder page for now. I have this mock data from a mock api I've made and it fetches with no issues on the main adoption page:
'use client';
import React from 'react';
import CustomizableHero from "../../ponents/HeroScondary";
import PetCard from "../../ponents/PetCard";
import Button from '../../ponents/Button';
import { useState, useEffect } from 'react'
export default function Adote() {
// mock API
const [pets, setPets] = useState([])
const [loading, setLoading] = useState(true)
const [page, setPage] = useState(1)
useEffect(() => {
const fetchPets = async () => {
setLoading(true)
const url = new URL('https://mockID.mockapi.io/pets')
url.searchParams.append('pleted', false)
url.searchParams.append('page', page)
url.searchParams.append('limit', 18)
const res = await fetch(url)
const data = await res.json()
setPets(prevPets => [...prevPets, ...data])
setLoading(false)
}
fetchPets()
},
[])
if (loading) {
return <p>Loading...</p>
}
const handlePetClick = (petId) => {
router.push(`/adote/${petId}`)
}
return (
<div className="relative">
<CustomizableHero
backgroundImage="/images/dois-cachorros.png"
title="Adote"
subtitle="Encontre o seu melhor amigo"
color="white"
/>
{/* search filter */}
<div className="flex flex-row">
</div>
{/* pet cards */}
<div className="absolute w-[100vw] max-w-screen flex flex-col justify-center ">
<div className="flex flex-col text-align-center items-center justify-between m-10">
<h2 className="text-5xl font-bold tracking-tight sm:text-5xl text-gray-500 p-5">Animais buscando uma familia</h2>
<p className="mt-6 mb-3 text-md leading-8 text-gray-500 text-center p-5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec euismod, nulla ut condimentum maximus, felis enim rhoncus justo, vel venenatis magna tortor nec tortor. Donec vulputate fermentum fermentum. Curabitur odio dui, consectetur vitae ornare eu, ultricies vel risus</p>
</div>
{/* Called displayPetCards() inside JSX code to render the PetCard ponents. */}
<div className="grid grid-cols-3 gap-4 m-10 p-5">
{pets.map(pet => (
<PetCard
key={pet.id}
pet={pet}
onClick={()=> handlePetClick(pet.id)}
/>
))}
</div>
<p className="flex flex-col text-center mt-5">Visualizados 18 de 661 animais de estimação</p>
<div className="flex justify-center m-10">
<Button
label="Mostrar mais"
onClick={() => setPage(page + 1)
} // Attach the click handler here
className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-indigo-600"
/>
</div>
</div>
</div>
);
};
I've tried useRouter, I've tried getServerSideProps() but nothing works
Share Improve this question edited Oct 31, 2023 at 9:45 alpha_N1618 5605 silver badges20 bronze badges asked Oct 29, 2023 at 18:47 TamaraTamara 231 gold badge1 silver badge5 bronze badges 1- Any reason you're trying to use a button? Generally for accessibility you want a link to be for navigation and a button to be for events. You might style a link to look like a button. – Matt Commented Oct 29, 2023 at 19:18
2 Answers
Reset to default 5Don't overplicate things you are already passing pet.id in petCard ponent just link it from there
import React from 'react';
import Image from 'next/image';
import Button from '../ponents/Button';
import Link from 'next/link';
export default function PetCard({ pet, onClick }) {
return (
<div className="flex flex-col border p-4 rounded-lg hover:shadow-lg
transition-shadow bg-white" >
<Link href={`/adote/${pet.id}`}>
<Button
label="Ver perfil"
className="bg-indigo-500 hover:bg-gray-600 focus-visible:outline-
indigo-600 w-32 flex flex-row justify-center items-center"
/>
</Link>
</div>
)
}
And then search pet for specific id in PetDetailPage by params
'use server'
import React from 'react';
export default function PetDetailPage({ params }) {
const pet = getPetDetails(params.id)
return (
<div className="p-4">
<Image src={pet.avatar} alt={pet.name} width={600} height={600}
className="rounded-md" />
<h1 className="text-2xl font-bold mt-4">{pet.name}</h1>
<p className="text-gray-600">{pet.breed}</p>
<p className="text-gray-600">{pet.id}</p>
<p className="text-gray-600">{pet.age}</p>
<p className="text-gray-600">{pet.gender}</p>
<p className="mt-2">{pet.description}</p>
</div>
);
}
Heres a example code I made : Explaination:
- there is a pets page which shows all pets name,
- It contains 2 elements
1st is Link tag
&2nd is a Button as link
- Take a look at folder structure, focus on
pets folder
- On pets page I have shown pets data (you may show it using ur card ponent) using URL present in your code.
Folder Structure :
projectName
├── .gitignore
├── jsconfig.json
├── next.config.js
├── package-lock.json
├── package.json
├── postcss.config.js
├── public
│ ├── images
│ ├── next.svg
│ └── vercel.svg
├── README.md
├── src
│ └── app
│ ├── api
│ ├── p
│ │ ├── LinkButton.js
│ ├── favicon.ico
│ ├── globals.css
│ ├── layout.js
│ ├── page.js
│ ├── pets
│ │ ├── page.js
│ │ └── [id]
│ │ └── page.js
└── tailwind.config.js
now I will show pets data, & from it i will pass ID to Link tag & Button ponent.
Button Component is client side bcoz of event click.
Now if someone clicks on link or button, they will get navigated to
pets/id
Using url present in code I appended id to it get details of pet by id. Details page is under folder
pets/[id]/page.js
You may read this for more clarity of concepts :
Routing Fundamentals : https://nextjs/docs/app/building-your-application/routing
Dynamic Routes :
https://nextjs/docs/app/building-your-application/routing/dynamic-routesLinking and Navigating :
https://nextjs/docs/app/building-your-application/routing/linking-and-navigatingServer Components :
https://nextjs/docs/app/building-your-application/rendering/server-ponentsClient Components :
https://nextjs/docs/app/building-your-application/rendering/client-ponentsWhen to use Server and Client Components? :
https://nextjs/docs/app/building-your-application/rendering/position-patterns#when-to-use-server-and-client-ponents
All Pets Detail Page: projectName\src\app\pets\page.js
import Link from "next/link"
import LinkButton from "../p/LinkButton"
async function GetPets() {
let data = await fetch('https://653bf08fd5d6790f5ec7a989.mockapi.io/pets')
let PetsData = await data.json()
// console.log(PetsData);
// LOGGED IN TERMINAL BCOZ ITS SERVER-SIDE
return PetsData
}
export default async function Pets() {
const PetsData = await GetPets()
return (
<div>
<h1>All Pets !</h1>
{
PetsData.map((p, i) => {
return (
<div style={{
border: "1px solid black",
padding: "25px",
margin: '25px'
}}
key={i}
>
<img src={p.avatar} width="250" alt="" />
<h3>{p.name}</h3>
<Link style={{ color: "blue" }} href={`/pets/` + p.id}> VIew </Link>
<LinkButton ID={p.id} />
</div>
)
})
}
</div>
)
}
Pet Detail page : projectName\src\app\pets\[id]\page.js
async function GetDetailsByID(ID) {
let data = await fetch('https://653bf08fd5d6790f5ec7a989.mockapi.io/pets/' + ID)
// I GOT THIS URL BY TRYING TO APPEND ID TO URL PROVIDED IN CODE
let PetDetails = await data.json()
// console.log(Object.keys(PetDetails));
// LOGGED IN TERMINAL BCOZ ITS SERVER-SIDE
return PetDetails
}
export default async function DetailsPage({ params }) {
let Details = await GetDetailsByID(params.id)
// AFTER GETTING ID I WILL PASS IT THE API CALL
// THIS PAGE WILL BE SERVER RENDERED
return (
<div>
<h1>Full Pet Deyails :</h1>
<img src={Details.avatar} alt="" />
<p className="text-gray-600">{Details.name}</p>
<p className="text-gray-600">{Details.id}</p>
<p className="text-gray-600">{Details.age}</p>
<p className="text-gray-600">{Details.Gender}</p>
<p className="text-gray-600">{Details.breed}</p>
<p className="text-gray-600">{Details.description}</p>
<p className="text-gray-600">{Details.createdAt}</p>
</div>
)
}
LinkButton.js Component projectName\src\app\p\LinkButton.js
under p folder
'use client'
import { useRouter } from 'next/navigation'
import React from 'react'
const LinkButton = ({ ID }) => {
const MyRouter = useRouter()
// IF YOU WANT TO USE A BUTTON THEN IT COMES UNDER CLIENT SIDE
// RENDERING BECAUSE OF CLICK EVENT
// HERE IM PASSING THE ID TO LINK & USING ROUTER TO NAVIGATE
return (
<button onClick={() => {
MyRouter.push(`/pets/` + ID)
}}>
Button
</button>
)
}
export default LinkButton
Instead of using button you can style that Link tag to look like a button !
Output:
- Goto http://localhost:3000/pets
- You will a card like layout, with Link Tag named as View in blue color & Button named as Button.
- Click on them you will go on a page ex. 1st pet was clicked
http://localhost:3000/pets/1
- On this page details are fetched by ID.
- Open console & then
open Network Tab
click onpets
, then on right hand side click on preview you will see page is pre-rendered ! - Siilarly for details page, you will id in
Network Tab
click on preview you will see page is pre-rendered.
Comment to ask for any doubts.
This code will be deleted from myside(personal puter).