Linking to a dynamic page from a different dynamic page does not update the content on that page

Hi,

I'm having some trouble with a Content Relationship field that I am using to link to a document.

I have a custom type called 'projects'. In that custom type I have set up a Content Relationship field called 'relatedProject', so I can select another project in the CMS as a linked document at the end of the project page.

Displaying this related project works fine, and fetchLinks is displaying the correct information, but when I click the link to take me to the related project document - which is another project custom type page, on load, the information is getting muddled and the page is displaying a mixture of content of the previous project page and the new project page. When I perform a hard refresh (as using Next JS) the page is rendering correctly with the content of the project page I clicked through to.

I'm unsure whether this is something wrong with Prismic or something wrong with Next JS? Apologies if I have not explained this brilliantly.

This is my code for the [uid].js:

import { useEffect, useRef } from 'react';

import { createClient } from '../../prismic';
import resolver from '../../sm-resolver.js';
import * as prismicH from '@prismicio/helpers';
import { linkResolver } from '../../utils/linkResolver';
import { SliceZone } from '@prismicio/react';

import { gsap } from 'gsap';

import { Layout } from '../../components/Layout';
import ProjectHero from '../../components/Hero/Project';
import RelatedProjectCta from '../../components/RelatedProjectCta';

const ProjectDetail = ({ data, url, lang, layout }) => {

  const seo = {
    metaTitle: data.metaTitle || layout.metaTitle,
    metaDescription: data.metaDescription || layout.metaDescription,
    metaImage: data.metaImage?.url || layout.metaImage?.url,
    url: url,
    article: true,
    lang: lang,
  };

  const pageData = { data };
  
  const relatedProject = { data };

  const revealOverlay = useRef();

  // Hero reveal
  useEffect(() => {
    gsap.to(revealOverlay.current, {
      opacity: 0,
      duration: 2.3,
      ease: "power2.out"
    });
  }, []);

  return (
    <Layout seo={seo} {...layout}>

      <ProjectHero {...pageData} />

      <SliceZone slices={data.slices} resolver={resolver} />

      { 
        prismicH.isFilled.link(data.relatedProject) ? (
          <RelatedProjectCta {...relatedProject}/>
        )
        : null
      }
      
    </Layout>
  );
};

// Fetch content from prismic - previews but doesn't hot reload
export const getStaticProps = async ({ params, previewData }) => {
  const client = createClient({ previewData });

  // Default Layout components reused across the site
  // If a singleton document is missing, `getStaticProps` will throw a PrismicError.
  const seo = await client.getSingle("defaultSeo");
  const header = await client.getSingle("header");
  const footer = await client.getSingle("footer");
  const socials = await client.getSingle("socials");
  const projectDetail = await client.getByUID("project", params.uid, {'fetchLinks': 'project.theme, project.client, project.projectTitle, project.projectIntroduction, project.featuredImage'} );

  return {
    props: {
      layout: {
        seo,
        header,
        footer,
        socials,
      },
      ...projectDetail
    }
  };
};

export const getStaticPaths = async () => {
  const client = createClient();

  const projectDetail = await client.getAllByType("project");

  return {
    paths: projectDetail.map((page) => prismicH.asLink(page, linkResolver)),
    fallback: false,
  };
};

export default ProjectDetail;

I wonder if its to do with getStaticPaths?

Thanks

Hey @jon1, could it be that you're running the project in preview mode?
Sometimes the content gets updated if you have a running preview session in a release.
Can you also show us the RelatedProjectCta component to see what it's doing?

I don't think it is running in Preview mode. The RelatedProjectCta is as follows:

import React from 'react';
import { PrismicText } from '@prismicio/react';
import { PrismicNextImage } from '@prismicio/next';

import { Link } from "../Link";

const RelatedProjectCta = ({ data }) => {

  const relatedProject = {
    uid: data.relatedProject.uid,
    url: data.relatedProject.url,
    theme: data.relatedProject.theme,
    client: data.relatedProject.data.client,
    title: data.relatedProject.data.projectTitle,
    introduction: data.relatedProject.data.projectIntroduction,
    image: data.relatedProject.data.featuredImage,
  }

  return (
    <section className={`component cta-slice ${relatedProject.theme}`} data-header={relatedProject.theme === "light" && (`is-dark`) || relatedProject.theme === "dark" && ('is-light')}>
      <div className="container">
        <div className="cta-slice_text-wrapper">
          <div className="eyebrow-heading">
            Related project
          </div>
          <h2 className="primary-heading">
            <PrismicText field={relatedProject.client}/>
          </h2>
          <div className="description lead-body">
            <PrismicText field={relatedProject.title}/>
          </div>
          <Link
            href={`/work/${relatedProject.uid}`}
            className="btn animated-button">
            View project
          </Link>
        </div>
      </div>
      <div className="cta-slice_background">
        <div className="cta-slice_background_image">
          <PrismicNextImage
            className="block width-100% object-cover"
            field={relatedProject.image}
            imgixParams={{ q: 80 }}
            layout="fill"
          />
        </div>
      </div>
    </section>
  )
};

export default RelatedProjectCta

I am also using a Link component for links:

import NextLink from "next/link";
import { asLink } from "@prismicio/helpers";
import { linkResolver } from "../utils/linkResolver";

export const Link = ({
  href: link,
  target,
  disabled,
  children,
  className,
  ...rest
}) => {
  if (disabled) {
    return <span {...rest}>{children}</span>;
  }

  //Standard link
  if (typeof link === "string") {
    if (link[0] === "/") {
      return (
        <NextLink href={link}>
          <a className={className} {...rest}>
            {children}
          </a>
        </NextLink>
      );
    }

    return (
      <a
        href={link}
        target={target ?? "_blank"}
        className={className}
        {...rest}
        rel="noopener noreferrer"
      >
        {children}
      </a>
    );
  }

  //Unknown link
  if (link.link_type === "Any") return null;

  //Prismic Link
  if (link.link_type === "Web") {
    if (!link.url) return null;

    //Same page anchor links
    if (link.url.includes("://#")) {
      const anchor = link.url.split("://")[1];
      return (
        <a href={anchor} className={className} {...rest}>
          {children}
        </a>
      );
    }

    return (
      <a
        href={asLink(link, linkResolver)}
        target={target ?? "_blank"}
        className={className}
        {...rest}
        rel="noopener noreferrer"
      >
        {children}
      </a>
    );
  }

  if (link.link_type === "Document") {
    return (
      <NextLink href={asLink(link, linkResolver)}>
        <a className={className} {...rest}>
          {children}
        </a>
      </NextLink>
    );
  }

  if (link.link_type === "Image") {
    return (
      <a
        href={asLink(link, linkResolver)}
        className={className}
        {...rest}
        rel="noopener noreferrer"
      >
        {children}
      </a>
    );
  }

  return null;
};

Thanks

I can see tht in your link component you're passing a hardcoded path href={/work/${relatedProject.uid}}.

I can recommend you use a global PrismicLink component that takes care of all your internal links. You can check out how to build it here:

Hi @Pau, I have followed setting up the PrismicProvider with the internalLinkComponent and still getting this behaviour. The issue is not that the site isn't linking to the correct url, but the content in that new url is the content from the old url, the url I've just come from. I've even asked on StackOverflow, and some have mentioned that it looks like getStaticProps is configured incorrectly, but it's not because at build time the correct urls/pages are being generated.

I see. Then we need to go step by step to catch the error.
What happens if you log the content of the linked document in the console, what do you get?
You need to make sure you're passing the correct document to the link before rendering it.

So logging the content of the linked document gives me this:

{
  id: 'Ytb2jBAAACIAnuJ5',
  type: 'project',
  tags: [ 'Featured' ],
  lang: 'en-gb',
  slug: 'bespoke-brand-design-for-boutique-wedding-florist',
  first_publication_date: '2022-07-19T18:23:12+0000',
  last_publication_date: '2022-07-19T18:23:22+0000',
  uid: 'lucinda-wild-floristry-brand-design',
  url: '/work/lucinda-wild-floristry-brand-design',
  data: { title: [Array] },
  link_type: 'Document',
  isBroken: false
}

So it looks to me like this is fine. I can display the title field from the linked document, so the data is getting fetched correctly.

When I click the link I am now getting the error"

Unhandled Runtime Error

NotFoundError: The object can not be found here.

Whereas previously, the site would go to the linked document's URL, but the content would be of the document I have just come from. Interestingly, that is when I am linking to a document of the same custom type. When I link to a different custom type, the page is displayed correctly, with the correct data BUT the page is scrolled down to the bottom and I would expect the page to load at the top.

It seems there a few things going wrong here and as far as I can see I have followed the guides correctly, I'm even using a Prismic starter to make sure everything was setup correctly.

A bit of an update:

I'm not getting the error message when clicking on the link now. I haven't changed anything, but after restarting the Next server it works, in that it goes to the URL, but the content is still incorrect.

Clicking the related link on the page at the new URL (which I have set to the previous project document, so I'm navigating from one to the other) then works fine with the correct content and the correct URL. It seems to be occurring on the first custom type document that I click on from any other page in the site. Can be either of the two projects I have added in for testing. Hopefully that makes sense!

Still stumped as to why this is happening :joy:

@jon1 I have not read the whole thread but have you tried adding a key to your return props in getStaticProps?

  return {
    props: {
      key: `${params.uid}-${Number(new Date())}`,
      layout: {
        seo,
        header,
        footer,
        socials,
      },
      ...projectDetail
    }
  };

You are a life saver @kris, that works! I don't quite understand how adding a key prop in has worked though? Would you mind explaining to help me understand?

@jon1 Glad that it worked! I had a similar problem to you and I discovered this solution from a Github issue

Since then I add the key prop to any dynamic pages within my website.

I'm not 100% sure on the technical explanation, but from what I understand, it seems that since you haven't provided a unique key for every page, Next.js doesn't realise that you are rendering a new page (when redirecting from one page to another page which both share the same dynamic template). This is fixed when you add a key. I assume that this is very similar to why you should also add key when rendering a list in React

It does makes sense now you have explained it. Thanks again :slight_smile: