Sanity Fetcher component not working server side

Another unrelated issue I’m having with the Sanity Fetcher component. Is NOT working on server side → :thread:

We have the SanityFetcher component on a page, with this GROQ prop as dynamic value

`

*[_type == 'post' && slug.current == '${$ctx.params.slug}' && vertical-> slug.current == '${$ctx.params.vertical}']{ slug, title, content, pageSEO, vertical->}

as you can see it uses$ctxwe noticed it is not working on server side… server returns

Loading…
` and then in does the fetch and render on client - which is not good as for SEO -

This is something we noticed on the console

ORIG GROQ *[_type == 'post' && slug.current == 'undefined' && vertical-> slug.current == 'undefined']{ slug, title, content, pageSEO, vertical->}

see how ctx stuff is undefined, and then we see this

ORIG GROQ *[_type == 'post' && slug.current == 'what-does-pet-insurance-cover' && vertical-> slug.current == 'pet-insurance']{ slug, title, content, pageSEO, vertical->}

which have the correct values.

Disabling javascript (and also doing a view-source) we can see that the final elements/components with the data are not served by server
see the “loadings”
I traced that to https://github.com/plasmicapp/plasmic/blob/master/plasmicpkgs/plasmic-sanity-io/src/sanity.tsx#L413

now the first part you see (string containing the groq) is actually just a text element with the same dynamic value we use for the SanityFetcher component… this was done to just see if we had a bug passing params to all the way down into the server, but this is actually showing the params are in good shape since the server returns that text correctly filled

Not sure what’s happening really

hardcoding the groq works fine (meaning replacing the ctx for just a hardcoded string on the query)

are we missing anything on the setup? it doesn’t really feel like that since the correct ctx params are returned by the server on those text components… so I’m not sure where to look…

Hey Sebastian! Are you using codegen or the headless API?

Hello Samuel,
headless API

my [[..catcall]].tsx page… might help you also to understand my setup

/**
 * For each page, pre-fetch the data we need to render it
 */
export const getStaticProps: GetStaticProps = async (context) => {
  const { catchall } = context.params ?? {};

  // Convert the catchall param into a path string
  const plasmicPath =
    typeof catchall === 'string'
      ? catchall
      : Array.isArray(catchall)
      ? `/${catchall.join('/')}`
      : '/';
  const plasmicData = await PLASMIC.maybeFetchComponentData(plasmicPath);

  if (!plasmicData) {
    // This is some non-Plasmic catch-all page
    return {
      props: {},
    };
  }

  // This is a path that Plasmic knows about.
  // Cache the necessary data fetched for the page.
  const queryCache = await extractPlasmicQueryData(
    <PlasmicRootProvider
      loader={PLASMIC}
      prefetchedData={plasmicData}
      skipFonts={!!env.NEXT_PUBLIC_GOOGLE_FONT}
      pageParams={context.params}
    >
      <PlasmicComponent component={plasmicData.entryCompMetas[0].displayName} />
    </PlasmicRootProvider>
  );

  // Pass the data in as props.
  return {
    props: { plasmicData, queryCache },
  };
};


/**
 * Actually render the page!
 */
export default function CatchallPage(props: {
  plasmicData?: ComponentRenderData;
  queryCache?: Record<string, any>;
}) {
  const { plasmicData, queryCache } = props;
  const router = useRouter();

  if (!plasmicData || plasmicData.entryCompMetas.length === 0) {
    return <Error statusCode={404} />;
  }
  const pageMeta = plasmicData.entryCompMetas[0];

  return (
    // Pass in the data fetched in getStaticProps as prefetchedData
    <PlasmicRootProvider
      loader={PLASMIC}
      prefetchedData={plasmicData}
      prefetchedQueryData={queryCache}
      pageParams={pageMeta.params}
      pageQuery={router.query}
      skipFonts={!!env.NEXT_PUBLIC_GOOGLE_FONT}
      Link={PlainLink}
    >
      {
        // plasmicData.entryCompMetas[0].displayName contains the name
        // of the component you fetched.
      }
      <PlasmicComponent component={pageMeta.displayName} />
    </PlasmicRootProvider>
  );
}

When you use dynamic pages, if you are depending on SSR, then you should be sure to have a getStaticPaths() to include the paths for all the slugs you use for your dynamic page. For example, it might look like…

export const getStaticPaths: GetStaticPaths = async () => {
  const pageModules = await PLASMIC.fetchPages();
  const productSlugs = await getProductSlugs();

  return {
    paths: [
      ...pageModules.map((mod) => ({
        params: {
          catchall: mod.path.substring(1).split("/"),
        },
      })),
      ...productSlugs.map((slug: any) => ({
        params: {
          catchall: ["product", slug]
        }
      }))
    ],
    fallback: "blocking",
  };
}

You can find more information here https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths

But the paths that are not included are generate on request time and populating the cache after. So it should still be rendered by the server. That’s how nextjs works. Get server paths is not mandatory.
Is that NOT supported by sanity fetcher?

Right. You need to specify at least a getStaticPaths with fallback: 'blocking' to indicate that the routes should be generated lazily.

 export const getStaticPaths: GetStaticPaths = async () => {
  return {
    paths: [],
    fallback: "blocking",
  };
};

https://nextjs.org/docs/pages/building-your-application/data-fetching/get-static-paths#generating-paths-on-demand

Still wouldn’t be the case because when i hardcoded the groq query with the actual string and not using ctx, it worked fine without any change to getstaticprops

@productive_duck can you verify whether things are working from a brand new create-plasmic-app? To help us narrow down the problem

this is my getStaticPaths

export const getStaticPaths: GetStaticPaths = async () => {
  const pages = await PLASMIC.fetchPages();
  return {
    paths: pages.map((page) => ({
      params: { catchall: page.path.substring(1).split('/') },
    })),
    fallback: 'blocking',
  };
};

@yang I could try creating a new app, but how do you want me to configure the catchall page. also I need to port a code component from the other project

The new project should already include a working catch all page

You can ignore the code component, we would just render nothing in its place