Skip to content

Instantly share code, notes, and snippets.

@codeflorist
Last active June 19, 2025 02:55
Show Gist options
  • Save codeflorist/63b4be906d03c16c059358d6708f70c8 to your computer and use it in GitHub Desktop.
Save codeflorist/63b4be906d03c16c059358d6708f70c8 to your computer and use it in GitHub Desktop.
Payload CMS Linkfield
import type { GroupField, Option, StaticLabel, LabelFunction, Description } from 'payload'
import { uploadCollections } from '@/collection-arrays/uploadCollections'
import {
array,
blocks,
checkbox,
code,
date,
email,
group,
json,
number,
point,
radio,
relationship,
richText,
select,
tabs,
text,
textarea,
upload,
} from 'payload/shared'
type LinkFieldType = (options?: {
name?: string
label?: false | StaticLabel | LabelFunction | undefined
required?: boolean
allowInternal?: boolean
allowAnchor?: boolean
allowExternal?: boolean
allowEmail?: boolean
allowPhone?: boolean
allowAsset?: boolean
description?: Description | undefined
}) => GroupField
export const linkField: LinkFieldType = ({
name = 'link',
label = {
en: 'Link',
de: 'Link',
},
required = false,
allowInternal = true,
allowAnchor = true,
allowExternal = true,
allowEmail = true,
allowPhone = true,
allowAsset = true,
description = undefined,
} = {}) => {
const typeOptions: Option[] = []
typeOptions.push({
label: {
en: 'No link',
de: 'Kein Link',
},
value: 'none',
})
if (allowInternal) {
typeOptions.push({
label: {
en: 'Internal',
de: 'Intern',
},
value: 'internal',
})
}
if (allowAnchor) {
typeOptions.push({
label: {
en: 'Anchor',
de: 'Anker',
},
value: 'anchor',
})
}
if (allowExternal) {
typeOptions.push({
label: {
en: 'External',
de: 'Extern',
},
value: 'external',
})
}
if (allowEmail) {
typeOptions.push({
label: {
en: 'E-Mail',
de: 'E-Mail',
},
value: 'email',
})
}
if (allowPhone) {
typeOptions.push({
label: {
en: 'Phone',
de: 'Telefon',
},
value: 'phone',
})
}
if (allowAsset) {
typeOptions.push({
label: {
en: 'File',
de: 'Datei',
},
value: 'asset',
})
}
const field: GroupField = {
name: name,
type: 'group',
label: label,
admin: {
hideGutter: true,
},
fields: [
{
name: 'isLink',
type: 'checkbox',
defaultValue: true,
hidden: true,
},
{
name: 'type',
type: 'select',
label: {
en: 'Type',
de: 'Typ',
},
defaultValue: 'none',
options: typeOptions,
required,
admin: {
description: description,
},
validate: (value, { data, path, req: { t } }) =>
!required ||
value !== 'none' ||
t('validation:required'),
},
{
name: 'internalPage',
type: 'relationship',
admin: {
condition: (_, siblingData) => siblingData?.type === 'internal',
},
hooks: {
beforeChange: [
({ value, siblingData }) => (siblingData?.type !== 'internal' ? {} : value),
],
},
label: {
en: 'Page',
de: 'Seite',
},
relationTo: ['pages'],
required: true,
},
{
name: 'internalAnchor',
type: 'text',
admin: {
condition: (_, siblingData) => siblingData?.type === 'internal',
description: {
en: 'Enter the ID of the element you want to link to on the target page. (Example: form)',
de: 'Geben Sie die ID des Elements an, zu dem Sie auf der Zielseite verlinken möchten. (Beispiel: formular)',
},
},
hooks: {
beforeChange: [
({ value, siblingData }) => (siblingData?.type !== 'internal' ? '' : value),
],
},
label: {
en: 'Anchor ID',
de: 'Anker ID',
},
required: false,
},
{
name: 'anchor',
type: 'text',
admin: {
condition: (_, siblingData) => siblingData?.type === 'anchor',
description: {
en: 'Enter the ID of the element you want to link to on the current page. (Example: form)',
de: 'Geben Sie die ID des Elements an, zu dem Sie auf der aktuellen Seite verlinken möchten. (Beispiel: formular)',
},
},
hooks: {
beforeChange: [({ value, siblingData }) => (siblingData?.type !== 'anchor' ? '' : value)],
},
label: {
en: 'Anchor ID',
de: 'Anker ID',
},
required: true,
},
{
name: 'externalUrl',
type: 'text',
admin: {
condition: (_, siblingData) => siblingData?.type === 'external',
description: 'Format: https://www.example.com',
},
hooks: {
beforeChange: [
({ value, siblingData }) => (siblingData?.type !== 'external' ? '' : value),
],
},
label: 'URL',
required: true,
},
{
name: 'email',
type: 'email',
admin: {
condition: (_, siblingData) => siblingData?.type === 'email',
},
hooks: {
beforeChange: [({ value, siblingData }) => (siblingData?.type !== 'email' ? '' : value)],
},
label: {
en: 'E-Mail',
de: 'E-Mail',
},
required: true,
},
{
name: 'phone',
type: 'text',
admin: {
condition: (_, siblingData) => siblingData?.type === 'phone',
description: 'Format: +49 123 4567890',
},
hooks: {
beforeChange: [({ value, siblingData }) => (siblingData?.type !== 'phone' ? '' : value)],
},
label: {
en: 'Phone',
de: 'Telefon',
},
required: true,
},
{
name: 'asset',
type: 'relationship',
admin: {
condition: (_, siblingData) => siblingData?.type === 'asset',
},
hooks: {
beforeChange: [({ value, siblingData }) => (siblingData?.type !== 'asset' ? {} : value)],
},
label: {
en: 'File',
de: 'Datei',
},
relationTo: uploadCollections,
required: true,
},
{
name: 'newTab',
type: 'checkbox',
admin: {
condition: (_, siblingData) =>
['internal', 'external', 'asset'].includes(siblingData?.type),
},
label: {
en: 'Open in new tab',
de: 'In neuem Tab öffnen',
},
},
],
}
return field
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment