0

I am building a NextJS based site with multiple locales (different domains) where the data comes from storyblok CMS (folder level translation). I am trying to figure out the best approach to statically generate the paginated URLs for the blog and since the data is known at build time, I figured the best approach would be to generate all URLs in getStaticPaths and then fetch the data for each Page in getStaticProps. This works fine for routes without parameters but when returning a page parameter along with the slug parameter in getStaticPaths, I cannot access it in getStaticProps. I know that query params cannot be accessed in getStaticPaths because we cannot know the custom querys at buildtime, but in this specific case, we actually can since these paths are generated in getStaticProps.

pages/[[...slug]].jsx

import {
  useStoryblokState,
  getStoryblokApi,
  StoryblokComponent,
} from "@storyblok/react";

export default function Page({
  story,
  locale,
  locales,
  defaultLocale,
  stories,
}) {
  story = useStoryblokState(story, {
    // language: locale,
  });

  return (
    <div>
      <StoryblokComponent
        blok={story.content}
        storyData={story}
        stories={stories}
      />
    </div>
  );
}

export async function getStaticProps({
  locale,
  locales,
  defaultLocale,
  params,
}) {
  console.log(params.slug); // This logs the slug
  console.log(params.page); // This logs undefined
  console.log(params.query.page); // This logs undefined

  // Empty slug on front page
  // Make sure root element page pr folder are selected in storyblok
  let slug = params.slug ? params.slug.join("/") : "";
  let sbParams = {
    version: "draft",
    resolve_relations: relationsResolvers,
    language: locale,
  };

  let { data } = await getStoryblokApi().get(
    `cdn/stories/${locale}/${slug}`,
    sbParams
  );

  let sbIndexParams = {
    version: "draft",
    resolve_relations: relationsResolvers,
    per_page: 10,
    page: params.page || 1,
    starts_with: `${locale}/${slug}`,
    sort_by: "first_published_at:desc",
    language: locale,
    filter_query: {
      component: {
        in: "page,post,case,template",
      },
    },
  };

  /* fetch an array of stories if page is startpage */
  let storiesData = null;
  if (data.story.is_startpage) {
    storiesData = await getStoryblokApi().get(`cdn/stories`, sbIndexParams);
  }

  return {
    props: {
      story: data ? data.story : false,
      key: data ? data.story.id : false,
      stories:
        data.story.is_startpage && storiesData
          ? storiesData.data.stories
              .filter((story) => story.is_startpage == false)
              .map((story) => {
                return {
                  name: story.name,
                  created_at: story.created_at,
                  published_at: story.published_at,
                  id: story.id,
                  uuid: story.uuid,
                  slug: story.slug,
                  full_slug: story.full_slug,
                  is_startpage: story.is_startpage,
                  content: {
                    cover: story.content.cover ?? null,
                    cover_image: story.content.cover_image ?? null,
                    author: story.content.author ?? null,
                    category: story.content.category ?? null,
                  },
                };
              })
          : false,
      locale,
      locales,
      defaultLocale,
    },
    revalidate: 3600,
  };
}

export async function getStaticPaths({ locales }) {
  let { data } = await getStoryblokApi().get("cdn/links/", {
    is_folder: false,
    filter_query: {
      component: {
        in: "page,post,case,template",
      },
    },
  });

  let paths = [];
  Object.keys(data.links).forEach((linkKey) => {
    if (data.links[linkKey].is_folder) {
      return;
    }

    // get array for slug because of catch all
    const slug = data.links[linkKey].slug;

    let splittedSlug = slug.split("/");
    const linkLocale = splittedSlug[0];
    splittedSlug.shift();
    if (splittedSlug == "") splittedSlug = false;

    // create additional languages
    for (const locale of locales) {
      if (linkLocale === locale) {
        paths.push({ params: { slug: splittedSlug }, locale });
      }
    }
  });

  // pagination route generation on custom post types like posts and cases
  const per_page = 10;

  const startPagesArr = Object.values(data.links)
    .map((obj) => obj)
    .filter((obj) => obj.is_startpage == true)
    .filter((obj) => obj.slug.split("/").length > 2);

  // make a loop that loops through all startpages and fetches all stories that are children of that startpage
  for (const startPage of startPagesArr) {
    let res = await getStoryblokApi().get("cdn/links/", {
      is_folder: false,
      starts_with: startPage.slug,
      paginated: 1,
      page: 1,
      per_page: per_page,
      sort_by: "first_published_at:desc",
      filter_query: {
        component: {
          in: "post,case,template",
        },
      },
    });

    let totalPages = Math.ceil(res.total / per_page);

    let splittedSlug = startPage.slug.split("/");
    const linkLocale = splittedSlug[0];
    splittedSlug.shift();
    if (splittedSlug == "") splittedSlug = false;

    // ... Loop through locales and push the paginated pages to the paths Array
    for (const locale of locales) {
      if (linkLocale === locale) {
        for (let i = 2; i <= totalPages; i++) {
          paths.push({
            params: {
              slug: splittedSlug, // this is passed to the getStaticProps function
              page: i, //this is not passed to the getStaticProps function
            },
            locale,
          });
        }
      }
    }
  }

  return {
    paths: paths,
    fallback: false,
  };
}

Accessing the page query param in getStaticProps would solve the problem since I can pass that value to the API request and get the right blogposts to display on the right paginated pages. Fetching data directly in the component is not preferable for SEO reasons since it will be client-side JS.

All the logic is for the whole site is in the pages/[[...slug.jsx]] file since there are multiple locales, but would it make sense to split it up so I have a dynamic file for the blog itself (across locales)?

I have tried returning the page query param in several different ways, but getStaticProps will only see the param that matches the filename (ex. params.slug will be accessible because the file is called [[...slug]].jsx].

mrued
  • 1
  • 1

0 Answers0