Simplify your stack and build anything. Or everything.
Build tomorrow’s web with a modern solution you truly own.
Code-based nature means you can build on top of it to power anything.
It’s time to take back your content infrastructure.

useConfig not working in custom field client component

default discord avatar
cashcodelast year
7

Hey folks!


I am trying to build a custom icon selector component:



'use client';
import type { TextFieldClientComponent } from 'payload';

import { FieldLabel, useConfig, useField } from '@payloadcms/ui';

export const IconFieldComponent: TextFieldClientComponent = ({ path, field }) => {
  const { config } = useConfig();
  console.log('config', config);
  const { setValue, value } = useField({ path });
  console.log('path, value', path, value);

  return (
    <div>
      <FieldLabel htmlFor={path} label={field.label} required={field.required} />
      {/* To be continued ... */}
    </div>
  );
};

export default IconFieldComponent;


And I am trying to use it in the field like this:



export const iconField: Field = {
  name: 'icon',
  type: 'text',
  label: 'Icon',
  required: true,
  admin: {
    components: {
      Field: '@/fields/Icon/IconFieldComponent.tsx',
    },
  },
};


If I remove the

useField

and

useConfig

hook, the field gets rendered and I can see my label. But with the hooks (

useConfig

is the culprit), I get the following error message:


TypeError: Cannot destructure property 'config' of [...] as it is undefined.

Does somebody know what could be wrong here?

  • discord user avatar
    alessiogr
    last year

    In 99% of cases, this happens if you have a different version of

    @payloadcms/ui

    or react installed than what the Payload Admin Panel is using



    If would verify you only have one version installed, e.g. by running

    pnpm why @payloadcms/ui

    Trying different package managers may also help

  • default discord avatar
    cashcodelast year

    Thanks

    @360823574644129795

    ! I created a brand new payload app and copied the dependencies from there.

  • default discord avatar
    skaddictedlast year

    Hej

    @295279398930743307

    - did you manage to implement it? I am having a real hard time and the Docs do not help unfortunately.

  • default discord avatar
    cashcodelast year

    The icon selector?

  • default discord avatar
    skaddictedlast year

    Yes.

  • default discord avatar
    cashcodelast year

    Sure! So this is my field definition:



    import { Field } from 'payload';
    
    export const iconField: Field = {
      name: 'icon',
      type: 'text',
      label: 'Icon',
      required: true,
      admin: {
        components: {
          Field: '@/fields/icon/IconFieldComponent.tsx',
        },
      },
    };


    And this is my custom component whch then renders the selector.


    'use client';
    import type { TextFieldClientComponent } from 'payload';
    
    import { ICONS } from '@/components/Icons/constants';
    import { FieldDescription, FieldLabel, useField } from '@payloadcms/ui';
    import './IconFieldComponent.scss';
    import { cn } from '@/utilities/cn';
    
    export const IconFieldComponent: TextFieldClientComponent = ({ path, field }) => {
      const { setValue, value } = useField({ path });
    
      const appearancePath = [...path.split('.').slice(0, -1), 'appearance'].join('.');
      const { value: appearance } = useField({ path: appearancePath });
    
      return (
        <div className="useTw">
          <FieldLabel htmlFor={path} label={field.label} required={field.required} />
          <div className="flex gap-4">
            // I have all of my icons mapped to their names, so I iterate over the names and get the corresponding component and render it
            {Object.entries(ICONS).map(([iconName, IconComponent]) => (
              <div
                className={cn({
                  'icon-selector': true,
                  'icon-selector--primary': appearance === 'primary',
                  'icon-selector--secondary': appearance === 'secondary',
                })}
                key={`${path}-${iconName}`}
              >
                <input
                  type="radio"
                  id={`${path}-${iconName}`}
                  name={path}
                  checked={value === iconName}
                  onChange={() => setValue(iconName)}
                  style={{
                    appearance: 'none',
                    display: 'none',
                  }}
                />
                <label htmlFor={`${path}-${iconName}`}>
                  <IconComponent />
                </label>
              </div>
            ))}
          </div>
          {field.admin?.description && <FieldDescription path={path} description={field.admin.description} />}
        </div>
      );
    };
    
    export default IconFieldComponent;


    And for the sake of completness, also so styling (not my best work and using tailwind):


    .icon-selector {
      @apply flex h-16 w-16 items-stretch justify-center rounded text-white;
      outline-offset: 2px;
      outline-width: 2px;
      outline-style: solid;
    
      &--primary {
        border-color: var(--color-primary);
        background-color: var(--color-primary);
    
        &:has(input:checked) {
          outline-color: var(--color-secondary);
        }
      }
    
      &--secondary {
        border-color: var(--color-secondary);
        background-color: var(--color-secondary);
    
        &:has(input:checked) {
          outline-color: var(--color-primary);
        }
      }
    
      label {
        @apply flex flex-grow cursor-pointer items-center justify-center p-1;
      }
    }
  • default discord avatar
    skaddictedlast year

    Awesome, thanks!



    Thank you so much for helping me out with this one. I spent way too many hours with setting this up. The docs are kind of lacking right now and this really helped me. Again, thank you so much!

Star on GitHub

Star

Chat on Discord

Discord

online

Can't find what you're looking for?

Get dedicated engineering support directly from the Payload team.