Like what we’re doing? Star us on GitHub!

How to support auto generate slug from title field in collections

Stupidism
10 months ago
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

  • Stupidism
    8 months ago

    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>
      );
    }
    
    
  • GeorgeyB
    10 months ago

    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
    Stupidism
    10 months ago

    Great, that's exactly what I want.

  • Stupidism
    9 months ago
  • MinSomai
    9 months ago

    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
    Stupidism
    9 months ago

    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?

    MinSomai
    9 months ago

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

    jmikrut
    Payload Team
    9 months ago

    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.

    MinSomai
    9 months ago

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

Open the post
Continue the discussion in GitHub
Can't find what you're looking for?
Get help straight from the Payload team with an Enterprise License.Learn More