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

Discussion on creating custom slugs for documents in Sanity and simplifying the process of turning references into links.

19 replies
Last updated: Apr 2, 2021
Curious 2: Do you store the full path in the Sanity slug – or just the slug of that document’s title?
I’m tired of constantly piecing together the slug logic on front and back ends -- so I’m writing a custom function/validator in Sanity to produce document slugs like
/office/sydney

They’re still valid in Sanity and it’ll make turning references into links simpler.
Mar 22, 2021, 8:48 AM
In 90% of the cases, I tend to translate
_type
 (or some other property [e.g.
category
]) to the first segment. However, in the community studio you can find a custom slug component that supports the
basePath
 option: https://github.com/sanity-io/community-studio/blob/dc1ed97818f2a5397e982bac99dbf8acb8cf9128/schemas/components/PathInput.js
Mar 22, 2021, 8:57 AM
That’s really cool. For now I’m getting that sort of behaviour just using a function to wrap a normal
slug
field:

slugWithType([`contact`], `title`),

Mar 22, 2021, 9:00 AM
…which is rendering something like this
Mar 22, 2021, 9:01 AM
That slug input is now a plugin using
@sanity/ui
w/ support for the generate function: https://www.sanity.io/plugins/better-slug 🙌
Mar 22, 2021, 1:07 PM
…bringing this discussion back here
I’ve settled on this, for now
😄

https://gist.github.com/SimeonGriggs/c4c66bc5f6aacea96788248d09862926
Mar 22, 2021, 6:04 PM
Main benefit I see is slugs === links
Mar 22, 2021, 6:04 PM
I'm curious: how do you fetch those in the front-end? As you're using next, it returns the slug with the slash...Do you prepend a leading slash to the GROQ query when looking for the document related to that slug? (aka: `client.fetch('*[slug == $slug]', slug:
/${slug}
)`)
Mar 22, 2021, 6:11 PM
😄
Mar 22, 2021, 6:13 PM
That bottom section before was a 4-step switch statement to generate the right paths – which then need to be reassembled and disassembled all over again.
Now the fetching is simpler, as is every instance of creating a link to a page throughout the site.
Mar 22, 2021, 6:14 PM
…and actually, the query no longer needs to be a keyed object either, because the only reason I was doing that was to generate paths in the switch statement. It could be even simpler!!
Mar 22, 2021, 6:16 PM
But now, without a
.reduce
or a switch statement how will anyone know how cool I am? 😂
Mar 22, 2021, 6:19 PM
user S
you're doing what I always want to do but never have the courage with Next: using a single
[..slug]
root-level route and connecting it to multiple templates, right?
My issue with this is that it usually leads to including a lot of JS & CSS for other templates which aren't used by a given page, hurting performance. Have you found a way to go beyond that? Would love to learn
😀
Mar 22, 2021, 7:38 PM
I’m not at the perf optimisation stage yet … maybe ask me later 😄
Mar 22, 2021, 7:40 PM
user B
If you’re using something like Emotion, you shouldn’t wind up with any extra CSS being built into the page for components that don’t wind up producing HTML markup. As for the extra JS from unused components, can you not use dynamic imports ?
Mar 22, 2021, 8:01 PM
Actually yeah I use tailwind so it’s only the one css file for the whole project.
And I’m meaning to look at dynamic imports in my page builder array...eventually
Mar 22, 2021, 8:14 PM
You can do something like this, guys.

import dynamic from "next/dynamic";

const modules = {
  HelloWorldModule: dynamic(() => import("./hello-world")),
};

export default modules;
And then:


const module = modules[moduleName]
Mar 31, 2021, 1:06 PM
How do you handle props in this instance
user L
?
Apr 2, 2021, 11:08 AM
Actually, got it!

import Hero from './Hero'

const dynamicBlocks = {
  pageBuilderQuote: dynamic(() => import('./Quote')),
  /// ...etc
}

const staticBlocks = {
  pageBuilderHero: (block) => <Hero {...block} />,
}

export default function PageBuilder({ blocks }) {
  return blocks
    .map((block) => ({ key: block._key, ...block }))
    .map((block) =>
      dynamicBlocks[block._type]
        ? React.createElement(dynamicBlocks[block._type], block)
        : staticBlocks[block._type](block)
    )
}
Apr 2, 2021, 11:15 AM
user S
It is easier than that, actually. (This is a minimal version. Probably, you could want to check for more things including but not limited to environment, check if module exits, etc.)

import dynamic from "next/dynamic";

const modules = {
  HelloWorldModule: dynamic(() => import("./hello-world")),
};

export default function Builder({ modules = [] }) {
  return modules.map(({ __typename, ...moduleProps }) => {
    const Module = modules[__typename];
    return <Module {...moduleProps} />;
  });
}
Apr 2, 2021, 7:20 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?