Розробка кастомних полів (Custom Fields) Payload CMS

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка кастомних полів (Custom Fields) Payload CMS
Середня
~2-3 робочих дні
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Кастомні поля Payload CMS

Payload надає 20+ вбудованих типів полів. Для нестандартних випадків — кастомні компоненти React для admin panel при повному збереженні логіки валідації та зберігання на стороні сервера.

Вбудовані поля з розширеною конфігурацією

Поле з умовною видимістю:

{
  name: 'discountPrice',
  type: 'number',
  admin: {
    condition: (data, siblingData) => siblingData.hasDiscount === true,
    description: 'Ціна зі знижкою',
  },
}

Поле з валідацією:

{
  name: 'phone',
  type: 'text',
  validate: (value) => {
    if (!value) return true
    const phoneRegex = /^\+380\d{9}$/
    if (!phoneRegex.test(value)) {
      return 'Формат: +380XXXXXXXXX'
    }
    return true
  },
}

Richtext з кастомними функціями:

import { lexicalEditor, LinkFeature, UploadFeature } from '@payloadcms/richtext-lexical'

{
  name: 'content',
  type: 'richText',
  editor: lexicalEditor({
    features: ({ defaultFeatures }) => [
      ...defaultFeatures,
      LinkFeature({ enabledCollections: ['pages', 'posts'] }),
      UploadFeature({ collections: { media: { fields: [{ name: 'caption', type: 'text' }] } } }),
    ],
  }),
}

Кастомний компонент поля

Для відображення нестандартного UI в admin panel, при цьому дані зберігаються як звичайно:

// fields/ColorPicker/index.tsx (admin компонент)
'use client'
import { useField } from 'payload/components/forms'

const ColorPickerField = ({ path }: { path: string }) => {
  const { value, setValue } = useField<string>({ path })

  const colors = ['#FF5733', '#33FF57', '#3357FF', '#FF33A8', '#33A8FF']

  return (
    <div className="field-type">
      <label className="field-label">Колір</label>
      <div style={{ display: 'flex', gap: 8 }}>
        {colors.map(color => (
          <div
            key={color}
            onClick={() => setValue(color)}
            style={{
              width: 32,
              height: 32,
              borderRadius: '50%',
              background: color,
              cursor: 'pointer',
              border: value === color ? '3px solid #000' : '2px solid transparent',
            }}
          />
        ))}
      </div>
      <input
        type="text"
        value={value || ''}
        onChange={e => setValue(e.target.value)}
        placeholder="#000000"
        style={{ marginTop: 8 }}
      />
    </div>
  )
}

export default ColorPickerField
// collections/Products.ts — підключення кастомного компонента
{
  name: 'brandColor',
  type: 'text',
  admin: {
    components: {
      Field: '/fields/ColorPicker/index#ColorPickerField',
    },
  },
}

Поле Blocks (гнучкий конструктор сторінок)

// blocks/TextBlock.ts
import { Block } from 'payload/types'

const TextBlock: Block = {
  slug: 'textBlock',
  labels: { singular: 'Текстовий блок', plural: 'Текстові блоки' },
  fields: [
    { name: 'content', type: 'richText' },
    {
      name: 'columns',
      type: 'select',
      options: [
        { label: '1 колонка', value: '1' },
        { label: '2 колонки', value: '2' },
      ],
      defaultValue: '1',
    },
  ],
}

const ImageBlock: Block = {
  slug: 'imageBlock',
  fields: [
    { name: 'image', type: 'upload', relationTo: 'media', required: true },
    { name: 'caption', type: 'text' },
    { name: 'fullWidth', type: 'checkbox', defaultValue: false },
  ],
}

// У колекції Pages:
{
  name: 'sections',
  type: 'blocks',
  blocks: [TextBlock, ImageBlock, CTABlock, GalleryBlock],
  minRows: 1,
}

Рендеринг Blocks у фронтенді

// components/Blocks.tsx
import type { Page } from '@/payload-types'

type BlockComponent = {
  textBlock: React.FC<{ content: any; columns: string }>
  imageBlock: React.FC<{ image: any; caption?: string; fullWidth: boolean }>
}

const blockComponents: BlockComponent = {
  textBlock: ({ content, columns }) => (
    <div className={`columns-${columns}`}>
      <RichText content={content} />
    </div>
  ),
  imageBlock: ({ image, caption, fullWidth }) => (
    <figure className={fullWidth ? 'full-width' : ''}>
      <img src={image.url} alt={image.alt} />
      {caption && <figcaption>{caption}</figcaption>}
    </figure>
  ),
}

export const Blocks = ({ sections }: { sections: Page['sections'] }) => {
  return (
    <>
      {sections?.map((block, i) => {
        const Component = blockComponents[block.blockType as keyof BlockComponent]
        if (!Component) return null
        return <Component key={i} {...(block as any)} />
      })}
    </>
  )
}

Віртуальні поля (тільки admin)

{
  name: 'fullName',
  type: 'text',
  admin: {
    readOnly: true,
    description: 'Вичисляється автоматично',
  },
  hooks: {
    afterRead: [
      ({ data }) => `${data?.firstName} ${data?.lastName}`.trim(),
    ],
  },
}

Часові рамки

Розробка набору кастомних полів (3–5 нестандартних типів з компонентами) — 2–3 дні.