Files
ipal-kit/docs/seo.md
T

3.0 KiB

seo

Wpina @payloadcms/plugin-seo (pola meta w kolekcjach) i dodaje warstwę logiki: składanie tytułów, budowanie Metadata dla Next.js z canonical i hreflang, auto-fill pustych meta z treści dokumentu.

Zależność

Dodaj do dependencies (pin do wersji payload):

"dependencies": { "@payloadcms/plugin-seo": "3.84.1" }

Config (payload.config.ts)

ipalKit({
  seo: {
    collections: ['pages', 'posts'],   // które kolekcje dostają meta
    // generateTitle: ({ doc }) => `${doc.title}`,          // opcjonalne
    // generateDescription: ({ doc }) => doc.excerpt ?? '',
    // fields: [...],                                        // extra pola w grupie SEO
    // autoFill: { title: 'title', description: 'excerpt' }, // mapowanie auto-fill
    // autoFill: false,                                      // wyłącz auto-fill
  },
})

Dodaje tab SEO (title, description, image) do wskazanych kolekcji. Auto-fill: hook beforeChange wypełnia puste meta.title z pola dokumentu (domyślnie title). Nigdy nie nadpisuje tego, co edytor wpisał ręcznie.

Front — generateMetadata (factory)

Najprościej: factory redukuje boilerplate. Klient podaje resolvery (bo zna swoje kolekcje/routing), plugin składa metadata.

// app/(frontend)/[locale]/[[...segments]]/page.tsx
import { createMetadataGenerator } from 'ipal-kit'
import { getSiteSettings } from 'ipal-kit'

const gen = createMetadataGenerator({
  config: i18nConfig,
  baseUrl: process.env.NEXT_PUBLIC_SERVER_URL,
  resolveDocument: async ({ payload, params, locale }) => {
    const slug = /* z params */ ''
    const res = await payload.find({
      collection: 'pages',
      where: { slug: { equals: slug } },
      locale: 'all', depth: 1, limit: 1,
    })
    return res.docs[0] ?? null   // musi mieć .meta i .slug (locale:'all')
  },
  resolveSiteName: async ({ payload, locale }) =>
    (await getSiteSettings(payload, { locale })).siteName ?? null,
})

export async function generateMetadata({ params }) {
  const payload = await getPayload({ config })
  const { locale } = await params
  return gen({ payload, params: await params, locale })
}

Front — niżej: buildMetadata bezpośrednio

Jeśli chcesz pełną kontrolę zamiast factory:

import { buildMetadata, getLocalizedSlugs } from 'ipal-kit'

const doc = await payload.findByID({ collection: 'pages', id, locale: 'all', depth: 1 })

return buildMetadata({
  meta: doc.meta,                                    // z plugin-seo
  siteName: settings.siteName,
  imageUrl: /* url OG image */,
  locale: 'pl',
  slugs: getLocalizedSlugs({ slugField: doc.slug, config }),
  config,
  baseUrl: 'https://example.com',
})
// → { title, description, openGraph, alternates: { canonical, languages } }

Pomocnicze

import { composeTitle, buildHreflangAlternates } from 'ipal-kit'

composeTitle({ pageTitle: 'O nas', siteName: 'Acme' })   // 'O nas | Acme'
buildHreflangAlternates({ slugs, config, baseUrl })      // { pl: '...', en: '...' }