1

I'm using the App router of nextjs 13, with typescript, and I'm trying to create dynamic pages and generate the paths for them with generateStaticParams().

The function generateStaticParams() seems to work, and it collects the correct information. But when I want to use the information in my component, I get a undefined value.

The page is fetchting the data correctly if I hardcode the ID, so I only need to swap this for the ID collected with the generateStaticParams.

How can I make sure that the page is receiving the data from generateStaticParams?

/src/app/coffeeplaces/[coffeeplace]/page.tsx
import fetchCoffeePlace from "@/api/fetchCoffeeplace";
import { db } from "@/helpers/firebaseConfig";
import { collection, getDocs } from "firebase/firestore";
import Image from "next/image";
import { Suspense } from "react";
import ImagesCarousel from "./components/imagesCarousel";
import Link from "next/link";
import CoffeeMainDetails from "./components/coffeeMainDetails";

export default async function Page({ params }: { params: { id: string, slug: string } }) {
  const { id, slug } = params

  // this comes back UNDEFINED
  console.log('id', params.id) // or id
  console.log('slug', params.slug) // or slug

  // This hardcoded option works to fetch the data
  // const id = 'r7CFv3t4Ni5sNl20wE8h'

  const CoffeeplaceData = fetchCoffeePlace(id);
  const CoffeeplaceInfoData = fetchCoffeePlace(id); // 

  const coffeeplace = await CoffeeplaceData;

  return (
    <div>
      <Link href={'/coffeeplaces'} className="text-sm font-light">Back to listing</Link>
      <Suspense fallback={<div>Loading...</div>}>
        <CoffeeplaceInfo promise={CoffeeplaceInfoData} />
      </Suspense>
    </div>
  );
}

async function CoffeeplaceInfo({ promise, }: { promise: Promise<CoffeePlace>; }) {
  const coffeeplaceInfo = await promise;
  return (
      <>
      <div className="pt-6 lg:flex w-full">
        <ImagesCarousel featuredImage={coffeeplaceInfo.featuredImage} />
        <CoffeeMainDetails name={coffeeplaceInfo.name} priceRange={coffeeplaceInfo.priceRange} organic={coffeeplaceInfo.organic} fairTrade={coffeeplaceInfo.fairTrade}/>
      </div>
      </>
    )
}

export async function generateStaticParams() { // tried with this: generateStaticParams({ params: { id, slug }}: any)
  const coffeePlaces = await getDocs(collection(db, "coffeePlaces"));
  const data = coffeePlaces.docs.map((doc) => ({
    id: doc.id,
    ...doc.data()
  }));

  var slugify = require('slugify')

  const result = data.map((item) => {
    const slug = slugify(item.name, {
      replacement: '-',  
      lower: true,  
      strict: true,
    });

    // these are logging the correct values
    console.log(item.id);
    console.log(slug);
    
    return {
      params: {
        id: item.id,
        slug: slug
      }
    };
  }); 

  // this is logging the correct result
  // [{ params: { id: '9ZVOCYsngTksBKXqQQOH', slug: 'coffeeplace-1' } }, { params: { id: 'r7CFv3t4Ni5sNl20wE8h', slug: 'example-2' } } ]

  return result; 
}

Using parallel Data Fetching

Small other thing, might be related, in the generateStaticParams function, when I loop over the result, for the item.name it says: Property 'name' does not exist on type '{ id: string; }')

Edit: Trying out with new environment

I've created a new nextjs project to test this out, and got the same results.

  1. I installed Nextjs 13 app router, typescript and tailwind with npx create-next-app@latest my-project --typescript --eslint
  2. I created a folder structure like so src/app/books/[type]/[book-id]/page.tsx, and used the following example to test it: Example of generateStaticParams

When I log the results I get an unidentified and when I build with npm run build it doesn't show the build of the pages.

Sandbox example

Bart
  • 149
  • 1
  • 16

3 Answers3

1

You have mismatch between dynamic segment name and what you expect in the code. The naming should be consistent between folder name and what you expect in the code.

You have coffeeplace segment (/src/app/coffeeplaces/[coffeeplace]/page.tsx) in the file system, but for some reason you want to pass id and slug keys from the generateStaticParams function. You can't do that, the key that you are passing from generateStaticParams should also be named coffeeplace and in your case it's the single key, you can't pass more info, other keys etc.

Info from the docs:

generateStaticParams should return an array of objects where each object represents the populated dynamic segments of a single route.

Each property in the object is a dynamic segment to be filled in for the route. The properties name is the segment's name, and the properties value is what that segment should be filled in with.

/product/[id] -> { id: string }[]

/products/[category]/[product] -> { category: string, product: string }[]

/products/[...slug] -> { slug: string[] }[]

More in the docs

Danila
  • 15,606
  • 2
  • 35
  • 67
  • Hi Danila, thank you very much! A small thing, with a big impact, that I completely overlooked. But does this mean that I can only use the key as param? It builds the pages now, but with the ID as url. But I would like to have the slug as url, but still use the ID to fetch the correct info for that page - hence the 2 params. Think that's possible? – Bart Jul 02 '23 at 18:41
  • @Bart it's not possible right now as far as I know. – Danila Jul 02 '23 at 19:05
0

Bart. I am glad to answer your question. In my humble opinion, there's some errors to fix. This is my code snippets that corrected yours.

First part.

export default async function Page({ params }: { params: { id: string, slug: 
string } }) {
    const { id, slug } = params;    // this was commented.

    // ...
}

Second part.

export async function generateStaticParams() {
  // ...

  const result = data.map((item) => {
    const slug = slugify(item.name, {
      replacement: '-',
      lower: true,
      strict: true,
    });

    return {
      params: {                  // you should return object with 'params' key
        id: item.id,
        slug: slug
      }
    };
  });

  return result;
}

I wonder this would be okay but please try it once.

M108
  • 62
  • 5
  • Hi David, thanks for your help and answer! Unfortunately, I still end up with **undefined**. I've updated my code with the `params` as key for the return object. The part I commented out should't have any effect, but I uncommented it to be closer how it will be. Cheers! – Bart Jul 01 '23 at 16:03
  • Remove the line where you hardcoded the id value, as you want to use the dynamic value from params : jsx // Remove this line // const id = 'r7CFv3t4Ni5sNl20wE8h' Make sure that the fetchCoffeePlace function accepts the id parameter correctly and returns a promise that resolves to the coffee place data. – M108 Jul 01 '23 at 16:28
  • Also, make sure that the fetchCoffeePlace function is working correctly and returns the expected data. You can console.log the coffeeplace variable to check if it contains the fetched data. If the fetchCoffeePlace function is asynchronous, you may need to use the await keyword when calling it: const coffeeplace = await fetchCoffeePlace(id); – M108 Jul 01 '23 at 16:29
  • Additionally, make sure that the CoffeeplaceInfo component receives the correct promise for the CoffeeplaceInfoData prop. In your code, you are passing CoffeeplaceInfoData as a prop, but it seems like CoffeeplaceInfoData is also a promise returned from fetchCoffeePlace . Make sure that fetchCoffeePlace returns the correct promise for the info data. – M108 Jul 01 '23 at 16:39
  • 2
    Welcome to Stack Overflow! Most or all of your 9 answers so far here today appear likely to have been entirely or partially written by AI (e.g., ChatGPT). For example, I've run one of the questions you answered through ChatGPT and received a nearly identical response. Please be aware that [posting AI-generated content is not allowed here](//meta.stackoverflow.com/q/421831). If you used an AI tool to assist with any answer, I would encourage you to delete it. We do hope you'll stick around and become a valuable part of our community by posting quality, non-AI content. Thanks! – NotTheDr01ds Jul 01 '23 at 20:42
  • 2
    Again, even these comments appear to be from ChatGPT. – NotTheDr01ds Jul 01 '23 at 20:42
  • 2
    **Readers should review this answer carefully and critically, as AI-generated information often contains fundamental errors and misinformation.** If you observe quality issues and/or have reason to believe that this answer was generated by AI, please leave feedback accordingly. The moderation team can use your help to identify quality issues. – NotTheDr01ds Jul 01 '23 at 20:42
0

your result array has this format

result=[params: {id: "id_1",slug: "slug_1"},params: {id: "id_2",slug: "slug_2"},.....]

when you reach params in Page component, you should be using,

const { id, slug } = params.params

because params in each dynamic page is

 params=params: {id: "id_1",slug: "slug_1"}
Yilmaz
  • 35,338
  • 10
  • 157
  • 202
  • Hi @Yilmaz, thanks for your help! I've played around with this, but it also shows a **undefined** unfortunately. When I console log just `params`, without .params, .id, or .slug, it logs whatever I have currently written in the url. Cheers! – Bart Jul 02 '23 at 07:58