👀 Our most exciting product launch yet 🚀 Join us May 8th for Sanity Connect

Structuring a schema to reference content within another schema's nested array in Next.js

26 replies
Last updated: Sep 29, 2022
Hi all,
I have a bit of a convoluted question about structuring a schema to reference content within another schema's nested array. Bear with me please, I hope to make this as clear as possible!

At the highest level, within
_type == "residences"
, I have an array called
residencesData
which defines attributes associated to a given artist in residence ("person"), such as a title, slug, bio, etc. On
/residences
, I map all of the objects in this array (aka the list of artists).
Once a user navigates to a particular artist's page,
/residences/[artistName]
, I query for this given artist's info on that page using the query below:
export const residenciesPageQuery = groq`
  *[$slug in residencesData[].slug.current][0] {
    _id,
    "person": residencesData[slug.current == $slug][0] {
      _key,
      title, 
      type,
      bio,
      photoCredit,
      contributions[]->{
        "imageUrl": mainImage.asset->url,
        "lqip": mainImage.asset->metadata.lqip,
        slug,
      },
      portfolio, 
      instagram,
      "imageUrl": mainImage.asset->url,
      "lqip": mainImage.asset->metadata.lqip,
      "slug": slug.current,
    }
  }
`;
Crucially, this person (the artist in residence) also contributes to a presentation text,
texteDePresentation
, which I'll need to place on
/residences/[artistName]/[texteDePresentation]


My question is as follows: How can I create a reference to a given
person
within my
texteDePresentation
schema?

    {
      name: "associatedArtist",
      title: "Artiste en résidence associé",
      description:
        "Ceci est nécessaire pour le bouton 'retour' trouvé à la fin du texte",
      type: "array",
      of: [{ type: "reference", to: { type: " ????? " } }],
      validation: (Rule) => Rule.required(),
    },
Sep 28, 2022, 3:48 PM
Do artists only ever belong under a single
residences
or can they be in multiple with different details (title, bio, etc.)? (I ask because I think your life might be easier if you had a
person
like document to handle this)
Sep 28, 2022, 3:57 PM
This is the structure. At the highest level, you define a collection (ie the year and the artists associated with that year)
Sep 28, 2022, 3:58 PM
then within that array you have everything you need to generate pages for that given artist
Sep 28, 2022, 3:58 PM
the artists change every year, if that answers your question - you won't see an artist listed twice
Sep 28, 2022, 3:59 PM
and they need to be organized under a year because there's also an archive section for "non-active" residencies which is organized by year in a drop-down style
Sep 28, 2022, 4:00 PM
I've placed the artists in the same document as the year because I already have 13 schema types, so adding any more would make the dashboard quite busy (especially if i have to create another document just for the presentation texts)
Sep 28, 2022, 4:01 PM
I don’t think what you’re trying to do is possible without some sort of custom component, and even then I’m not sure how well it would work.
Also you can change the Studio structure to clean things up. I wouldn’t let that effect how you’re modelling data.

This is changing your schema, however if it was me: I’d create an
artist
document that takes care of the title, slug, bio, etc. for the person.
Then
residences
has a field with an array of artists, ie.
residencesData
. Anywhere else you need to know about an artist, eg
associatedArtist
you can reference the
artist
.

and they need to be organized under a year because there’s also an archive section for “non-active” residencies which is organized by year in a drop-down style
You can solve this with a groq query to include whether the artists is under an active year or not
Sep 28, 2022, 4:08 PM
fair, that works, but then I guess where I'm stuck here would be how to have the pages follow this structure:
/residences/[artistName]/[texteDePresentation]
Sep 28, 2022, 4:12 PM
let me give this a shot -- I'll let you know if I run into any roadblocks 🙂
Sep 28, 2022, 4:12 PM
Hey so, it's going mostly smoothly but I got stuck with an error on the artist's page that I'm not quite sure how to solve. Any advice?
(commented out is the old query for when I created the artists as an array within residenceData)
Sep 28, 2022, 6:01 PM
this is my
getStaticPaths
Sep 28, 2022, 6:02 PM
btw, is there a way to hide a collection from the dashboard? Considering how you can create an artist entry from within the
residencesData
(year collection group) reference (using the "create new" button), I don't see a reason to also have this displayed on the far left hand side
Sep 28, 2022, 6:13 PM
All of the Sanity side logic should be set up correctly, I'm just struggling now to create a page at the ``/residences/[artistName]/[texteDePresentation]`` route that doesn't 404.
How should I configure my
getStaticPaths
in this case, particularily on line 31 in the screenshot below?
Here is my query:


export const residenciesPageQuery = groq`
  *[_type == "artistesEnResidence" && slug.current == $slug][0] {
    _id,
    title, 
    bio,
    texteDePresentationData[0]->{
      _id,
      title,
      author,
      body,
      "slug": slug.current,
    },
    "imageUrl": mainImage.asset->url,
    "slug": slug.current,
  }`
Sep 28, 2022, 6:46 PM
This is the shape of the output, and since it's an object rather than an array i'm sure I'd have to change the
paths: data.map((slug) => ({ params: { slug } })),
line as well, but I haven't been able to figure it out
Sep 28, 2022, 8:10 PM
I think you need to provide the
[artistName]
and the
[texteDePresentation]
for this to work. There's a good explanation here .
Sep 28, 2022, 9:28 PM
I'm not sure I follow - why would I need both when my query typed above contains a reference to the
texteDePresentation
?
Sep 28, 2022, 9:42 PM
The only thing I need to render on this page is the contents of
texteDePresentation
Sep 28, 2022, 9:43 PM
That's how dynamic routing works in Next. You're providing the path to that site page, which includes
[artistName]
.
Sep 28, 2022, 9:44 PM
Right, but I'm accessing /texteDePresentation from the [artistName] route, does that change things?
Sep 28, 2022, 9:53 PM
I'm able to navigate to the route properly using the anchor tag seen below, but once I get there it 404's.
// [artistName]

<Link
  scroll={false}
  href={`/residences/${data.slug}/${data.texteDePresentationData.slug}`}
>
  <small>Texte de présentation</small>
</Link>
Are you suggesting that I need to provide both to
getStaticPaths
at the
residences/[artistName]/[texteDePresentation]
route ?
Sep 28, 2022, 9:57 PM
That is precisely what I'm saying. There's a pretty good explanation in the link I shared.
Sep 28, 2022, 9:59 PM
Hey RD! Thanks so much again for the link you sent yesterday - It really helped set me on the right path.
I looked at it again this morning and got it to work by hard-coding the array data! However with my provided query (that should in theory follow the correct structure) it still fails.

I'm attaching a screenshot of my query output. Based on the link you sent, it seems like
getStaticPaths
is complaining because
texteDePresentation
is null in some cases (
TypeError: Cannot read property 'slug' of null
)
Not every
artisteEnResidence
will have a
texteDePresentation
, so is this error something that I can fix with a groq query?

export async function getStaticPaths() {
  const data = await client.fetch(
    groq`*[_type == "artistesEnResidence"] {
      "slug": slug.current,
      texteDePresentationData[0]->{
        "slug": slug.current,
      },
    }`
  );

  // const data = [
  //   {
  //     slug: "manuel-mathieu",
  //     texte: "le-gout-des-contradictions",
  //   },
  // ];

  return {
    paths: data.map((item) => {
      return {
        params: {
          slug: item.slug,
          texte: item.texteDePresentationData.slug,
          // slug: item.slug,
          // texte: item.texte,
        },
      };
    }),
    fallback: true,
  };
}
Sep 29, 2022, 1:32 PM
at the moment the only solution I managed to find was this, using a ternary operation (which doesn't feel ideal):

export async function getStaticPaths() {
  const data = await client.fetch(
    groq`*[_type == "artistesEnResidence"] {
      "slug": slug.current,
      texteDePresentationData[0]->{
        "slug": slug.current,
      },
    }`
  );  

 return {
    paths: data.map((item) => {
      return {
        params: {
          slug: item.slug,
          texte: item.texteDePresentationData?.slug
            ? item.texteDePresentationData.slug
            : "invalid-url",
        },
      };
    }),
    fallback: true,
  };
}
Sep 29, 2022, 1:53 PM
You could also filter it in js when constructing the paths, something like this:
return {
  paths: data
    .filter(item => item.texteDePresentationData)
    .map(item => {
      return {
        params: {
          slug: item.slug,
          texte: item.texteDePresentationData.slug,
        },
      };
    }),
  fallback: true,
};

Sep 29, 2022, 1:57 PM
Nice! Everything works as intended now. Your help is invaluable:)
Sep 29, 2022, 2:07 PM
Awesome! Glad we got it sorted out together! 🤜 🤛
Sep 29, 2022, 5:55 PM

Sanity– build remarkable experiences at scale

Sanity is a modern headless CMS that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Was this answer helpful?