skip to Main Content

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 component:

import React from 'react';
import Image from 'next/image';
import Button from '../components/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 "../../components/HeroScondary";
import PetCard from "../../components/PetCard";
import Button from '../../components/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://653bf08fd5d6790f5ec7a989.mockapi.io/pets')

      url.searchParams.append('completed', 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 components. */}
        <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

2

Answers


  1. 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 component) 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
    │       ├── comp
    │       │   ├── 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 component.

    • 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 :

    All Pets Detail Page: projectNamesrcapppetspage.js

    import Link from "next/link"
    import LinkButton from "../comp/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 : projectNamesrcapppets[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 projectNamesrcappcompLinkButton.js under comp 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:

    1. Goto http://localhost:3000/pets
    2. You will a card like layout, with Link Tag named as View in blue color & Button named as Button.
    3. Click on them you will go on a page ex. 1st pet was clicked http://localhost:3000/pets/1
    4. On this page details are fetched by ID.
    5. Open console & then open Network Tab click on pets, then on right hand side click on preview you will see page is pre-rendered !
    6. 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 computer).

    Login or Signup to reply.
  2. Don’t overcomplicate things you are already passing pet.id in petCard component just link it from there

    import React from 'react';
    import Image from 'next/image';
    import Button from '../components/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>
     );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search