How to support auto generate slug from title field in collections

default discord avatar
Stupidism
last year
3 4

For example,

If editor input
How to support auto generate slug from title field in collections as the title of the post/article.

I want another field slug to be auto-genrated with slug

how-to-support-auto-generate-slug-from-title-field-incollectionss

  • default discord avatar
    Stupidism
    last year

    A better solution for slug is to add a custom element:

    // Articles:
    
    fields: [
      // ...
      slug({ trackingField: 'title' }),
      // ...
    ]
    
    // fields/slug.ts
    
    import { Field } from 'payload/types';
    import { merge } from 'lodash';
    
    import { getSlugInput } from './ui/SlugInput';
    
    type Slug = (
      options?: { trackingField?: string },
      overrides?: Partial<Field>
    ) => Field;
    
    // By dynamically building fields in code configurations are reusable and concise
    export const slug: Slug = ({ trackingField = 'title' } = {}, overrides) =>
      merge<Field, Partial<Field> | undefined>(
        {
          name: 'slug',
          unique: true,
          type: 'text',
          admin: {
            position: 'sidebar',
            components: {
              Field: getSlugInput({ trackingField })
            }
          },
        },
        overrides
      );
    
    // SlugInput.tsx
    import { kebabCase } from 'lodash';
    import { TextInput, useField, useWatchForm } from 'payload/components/forms';
    import { useEffect, useRef } from 'react';
    
    export const getSlugInput = ({ trackingField }: { trackingField: string }) => () => {
      const { getDataByPath } = useWatchForm();
      const { value: slugValue = '', setValue: setSlugValue } = useField<string>({ path: 'slug' });
      const trackingFieldValue = getDataByPath<string>(trackingField) || '';
      const prevTrackingFieldValueRef = useRef(trackingFieldValue);
      const stopTrackingRef = useRef(false);
    
      useEffect(() => {
        if (!trackingField || stopTrackingRef.current) {
          return;
        }
        if (trackingFieldValue === prevTrackingFieldValueRef.current) {
          return;
        }
        const prevSlugValue = kebabCase(prevTrackingFieldValueRef.current);
        prevTrackingFieldValueRef.current = trackingFieldValue;
        if (prevSlugValue !== slugValue) {
          return;
        }
        setSlugValue(kebabCase(trackingFieldValue));
      }, [trackingFieldValue]);
    
      return (
        <div>
          <TextInput
            name="slug"
            path="slug"
            label="Slug"
            description={slugValue ? `Auto generated based on ${trackingField}` : `Will be auto-generated from ${trackingField} when save`}
            value={slugValue}
            onChange={e => {
              setSlugValue(e.target.value);
              stopTrackingRef.current = true;
            }}
          />
        </div>
      );
    }
    
    
  • default discord avatar
    GeorgeyB
    last year

    I've done a similar thing. I used the beforeChange hook.

    Given the following creator function:

    function generateSlugFrom(titleField: string, slugField = 'slug'): BeforeChangeHook<Product> {
        return ({ data }) => {
            if (!(titleField in data)) {
                return data;
            }
    
            return {
                ...data,
                [slugField]: slugify(data[titleField])
            };
        };
    }
    

    I use it like so:

    const Products: CollectionConfig = {
        slug: 'products',
        hooks: {
            beforeChange: [generateSlugFrom('name')]
        },
        fields: [
            {
                name: 'name',
                label: 'Name',
                type: 'text'
            },
            {
                name: 'slug',
                label: 'Slug',
                type: 'text'
            },
        ]
    };
    

    With help of course, from slugify.

    1 reply
    default discord avatar
    Stupidism
    last year

    Great, that's exactly what I want.

  • default discord avatar
    Stupidism
    last year
  • default discord avatar
    MinSomai
    last year

    what is the use case for this slug? I can't seem to wrap my head around because we can query with document id only.

    4 replies
    default discord avatar
    Stupidism
    last year

    It's more like auto-generate slug before writing to database.
    Are you looking for sth-like slug updates in UI while inputting for name/title?

    default discord avatar
    MinSomai
    last year

    slug is generated fine. I want to use this slug to query document.

    discord user avatar
    jmikrut
    Payload Team
    last year

    Hey @MinSomai — you can totally query via the slug field.

    In REST, it'd be ?where[slug][equals]=slug-here or similar.

    For more, including how to query in GraphQL and the Local API, take a look at the documentation for querying here.

    default discord avatar
    MinSomai
    last year

    I'm dumb. totally makes sense now, thanks.

Open the post
Continue the discussion in GitHub
Like what we're doing?
Star us on GitHub!

Star

Connect with the Payload Community on Discord

Discord

online

Can't find what you're looking for?

Get help straight from the Payload team with an Enterprise License.