Discussion on how to structure clients and projects in Sanity, including unique slugs and references.

18 replies
Last updated: Nov 22, 2021
Morning everyone 🙂
If I wanted to create a structure like this, how would I best do it?


Clients -> Projects
ie.

Client One
   - A Cool Project
   - Project Example
   - Project X
Client Two
   - Another Project
   - Project X
   - Something Else

Clients
would include a unique slug for the client name, and
Projects
would be nested under
Clients
, again with a unique slug.
The question mark for me is I don't want one
Client
to include projects with the same slug, as the URL would look like
client-one/code-veronica
so would need to be unique. However, another
Client
could include a matching slug, as it would still be unique in this case.
ie.

client-one/project-x
- Valid
client-one/project-x
- Invalid (matches previous)
client-two/project-x
- Valid
Nov 21, 2021, 10:00 AM
It's the slugs thing I'm stuck with 🙂
Nov 21, 2021, 10:01 AM
One thought would be to a custom
slugify
function with
isUnique
to add the client name to the slug, but at the moment I don't know how to directly nest
Projects
beneath
Clients
so I'm not sure how I'd do this
Nov 21, 2021, 10:04 AM
Hi chrish - there are a lot of approaches that could work and I am sure I am missing one or more, but here's my opinion on the simplest approach:

Client
and
Project
exist as documents • Each uses the default
isUnique
option to maintain the uniqueness of their slug among documents of their type.
Client
has a field called
Projects
that is an array of references to existing projects. • Your front end will have the task of building urls like
/client-one/project-x
and those urls will always be unique by virtue of the fact that they are pulling unique pairs of clients and projects. In this way the "nesting" exists in the front end, but in Sanity, they are not nested in any way beyond the fact that a
Client
document can reference any number of
Project
documents. If you need to guarantee that every slug in your dataset is unique among all types, there is a function for that as an example in the docs:
https://www.sanity.io/docs/slug-type#d5066a58b95a
Beyond that, if you wanted to make your
Project
slugs contain data from
Clients
that reference them, I think you might be able to do that using some custom functions, (that would have existing documents of other types as a dependency) but it might add complexity you don't need: https://www.sanity.io/docs/slug-type#e1531d9d041b
Nov 21, 2021, 12:38 PM
Hey
user U
, thanks so much for your reply. This sounds really logical but my only question is how would I allow decks across projects to use the same slug?
example-client/project-x
and
another-example/project-x
— with your approach, I don't think this would be possible as it would have to be
another-example/project-x-2
Nov 21, 2021, 5:47 PM
Hi - so, by design the
slug
field is required to be unique among documents of its type. If you wanted to make sure that information about the referencing
Client
was automatically included in the slug of a
Project
you could put this function in the
source
option for the slug field...
source: (doc) => {
  /* find the title of Clients who reference this Project */
  const query = `*[_type=="client" && references("${doc._id}")]{title}`

  return sanityClient.fetch(query).then((results) => {
    const client = results[0].title
    return `${client}-${doc.title}`
  })
}
...but I don't think it's a good idea. It works, but it adds dependencies and chances for race conditions. (When you click the slug's
generate button you toggle the draft status of the document, and you would have to make sure to click "Publish" before you generate again. If you change a Client's
title
the related slugs would stop matching the pattern until you generated again, unless you went crazy and wrote a webhook to go in and regenerate it...) you get the picture, it's a rabbit hole!
Nov 21, 2021, 9:12 PM
Yeah it does sound like it's gonna cause a lot of issues. I'm just trying to get my mind into the process the client (as in the actual client, not the
Client
document) is gonna be expecting... I feel like they're gonna want to create a
Client
and then within that create `Project`'s, but I know Sanity doesn't necessarily work like that.
Wondering if I need to create an array of `Project`'s inside
Client
documents or something
Nov 21, 2021, 9:15 PM
Yup - you are running into the thing many have run into. You can't -- at the moment -- create a document in place as a reference.
Nov 21, 2021, 9:16 PM
I'm sure it's something sanity will address at some point. I get the logic of separating everything for re-use, but sometimes it makes a lot of sense that things be nested like that
Nov 21, 2021, 9:17 PM
You could, have each
project
be an object and then create them in place, but then your projects would be forever coupled with their
Clients.
Nov 21, 2021, 9:19 PM
I think creating an
array
within a
Client
page might work here. I could do away with the slug I guess. It's mainly so the client has some way of linking particular pages to their own clients
Nov 21, 2021, 9:19 PM
Well, I used projects as an example, but actually they're decks, like slideshows or keynotes, so them being forever associated with a client is okay. Although I can see times when they might want to duplicate a deck from one client to another...
Nov 21, 2021, 9:20 PM
Yeah, my gut says to keep them as documents. They are much more queryable, manageable and relatable that way. The hard part is training the sanity users to create the decks "on their own" and then go back to the client document and add the references to now-existing decks in there. I believe there is a plan to add the create-referenced-doc-in-place feature though, here's the github issue: https://github.com/sanity-io/sanity/issues/507
Nov 21, 2021, 9:26 PM
^I have a project where I convinced myself I could leave something as an embedded object, but I regretted it because unexpectedly the Sanity users now wanted to be able to bulk add hundreds of them and I have a janky-hard-to-remember-what-I-did way to update them now.
Nov 21, 2021, 9:27 PM
Yeah I think you're absolutely right. I'm just thinking as well, by doing it this way (keeping the decks as separate documents, and then referencing them in the client area), you could potentially reference the same deck in multiple client areas if they had something "generic"
Nov 21, 2021, 9:31 PM
Could even do some magic with the decks themselves where you insert the client name into it based on the reference. For example, "Dear <client_name>" and replace it with the
Client
title
Nov 21, 2021, 9:32 PM
Just thinking outloud but you've really helped me nail down the direction for this
user U
— thank you!
Nov 21, 2021, 9:33 PM
Hi - no prob! It also helps me to think this sort of thing through, and yup, if they are separate then you'll have that flexibility, and also a way to query decks across clients if that ever becomes a thing.
Nov 21, 2021, 10:56 PM
Great discussion here guys! If your
project
is a document with a
client
in a reference field, you could generate a unique slug only among the projects from the same client by:
• use the client _id as the source of the slug
• in a custom slugify, fetch all the slug from the same client id & check unique against that
Nov 22, 2021, 6:02 AM

Sanity– build remarkable experiences at scale

The Sanity Composable Content Cloud is the 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?

Categorized in