--- name: payload-lexical-integration description: แนวทางการรวม Payload CMS Lexical richText content กับ design system components — อธิบายว่าทำไม design skill output กับ Payload content ถึงอยู่คนละ layer และวิธี integrate มันเข้าด้วยกัน category: software-development --- # Payload Lexical Integration ## ปัญหา เวลาใช้ design skill (ui-ux-pro-max) กับ Payload CMS มักเกิดความสับสน: - Design skill ให้โค้ดแบบไหน? - Payload Lexical เก็บ content ยังไง? - ทำไม content ไม่แสดงหลังสร้าง fields เสร็จ? ## สิ่งที่ต้องเข้าใจก่อน ### Two Layers — แยกกันทำ ``` ┌─────────────────────────────────────────────────────────┐ │ DESIGN LAYER (ui-ux-pro-max, ckm:design, ckm:ui-styling)│ │ • Component structure (Hero, Card, Navbar) │ │ • Color tokens, typography, spacing │ │ • Animation specs (150-300ms, ease-out) │ │ • Layout grid, responsive breakpoints │ │ • Interaction states │ │ │ │ Output: React + Tailwind code — "ภาชนะ" ไม่ใช่ "เนื้อหา"│ └─────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────┐ │ CONTENT LAYER (Payload CMS) │ │ • ข้อความ + format (bold, italic, link) │ │ • Headings (H1-H6) │ │ • Lists, blockquotes, code blocks │ │ • Images, links │ │ • Tables │ │ │ │ Output: Lexical JSON — "เนื้อหา" ไม่ใช่ "ภาชนะ" │ └─────────────────────────────────────────────────────────┘ ``` **Design skill สร้าง "ภาชนะ" — Payload สร้าง "เนื้อหา" — ต้องรวมกันตอน render** --- ## ขั้นตอน ``` [1] Design Phase ui-ux-pro-max → Component structure, tokens, animations Output: Component skeleton (ไม่มี content) ↓ [2] Payload Phase สร้าง Collections + richText Fields Output: Content structure ใน Payload ↓ [3] Content Phase พิมพ์ content ใน /admin (Lexical visual editor) Output: Lexical JSON ↓ [4] Integration Phase ครอบ Payload content ด้วย Design components ``` --- ## Step 1: Payload Collection กำหนด content fields ตาม section: ```ts // src/collections/Posts.ts const Posts: CollectionConfig = { slug: 'posts', fields: [ { name: 'title', type: 'text', required: true }, { name: 'slug', type: 'text', required: true }, { name: 'heroContent', type: 'richText' }, // content สำหรับ Hero { name: 'features', type: 'array', fields: [ { name: 'heading', type: 'text' }, { name: 'content', type: 'richText' }, // content ในแต่ละ card ] }, { name: 'testimonial', type: 'richText' }, { name: 'featuredImage', type: 'upload', relationTo: 'media' }, { name: 'status', type: 'select', options: [...], defaultValue: 'draft' }, ], } ``` --- ## Step 2: สร้าง Payload Helpers ```ts // src/lib/payload-helpers.ts import { getPayload } from 'payload' import config from '@/payload.config' export async function getPost(slug: string) { const p = await getPayload({ config }) const { docs } = await p.find({ collection: 'posts', where: { slug: { equals: slug } }, depth: 2, }) return docs[0] ?? null } export async function getAllPosts() { const p = await getPayload({ config }) return p.find({ collection: 'posts', where: { status: { equals: 'published' } }, depth: 1, }) } ``` --- ## Step 3: Integration — Design Component + RichText ```tsx // src/app/(frontend)/posts/[slug]/page.tsx import { getPost } from '@/lib/payload-helpers' import { RichText } from '@payloadcms/richtext-lexical' // Design tokens จาก ui-ux-pro-max const tokens = { hero: 'text-5xl md:text-7xl font-bold tracking-tight', section: 'py-20 px-6 max-w-7xl mx-auto', card: 'rounded-2xl border border-slate-200 p-6 shadow-sm', animate: 'animate-fade-in duration-300 ease-out', } // Design component ครอบ Payload richText function HeroSection({ title, content }: { title: string; content: any }) { return (

{title}

{content && (
{/* Payload content → RichText → design wrapper */}
)}
) } function FeatureCard({ heading, content }: { heading: string; content: any }) { return (

{heading}

{content && }
) } export default async function PostPage({ params }: { params: { slug: string } }) { const post = await getPost(params.slug) if (!post) return
Not found
return (
{post.features?.length > 0 && (
{post.features.map((f: any, i: number) => ( ))}
)}
) } ``` --- ## Animation Animation apply ที่ **wrapper element** ไม่ใช่ที่ content — เพราะ Lexical JSON เก็บแค่ content structure ไม่เก็บ animation metadata ```tsx // ✅ ถูก — animation ที่ wrapper
// ❌ ผิด — พยายามใส่ animation ใน Lexical JSON ``` Design skill จะให้ animation spec เป็น CSS class — แค่ apply ที่ element ที่ wrap `` --- ## Tailwind Typography Setup ```bash pnpm add @tailwindcss/typography ``` ```ts // tailwind.config.ts plugins: [require('@tailwindcss/typography')], ``` ใช้ class `prose` กับ ``: ```tsx ``` --- ## Payload Config: เปิด Lexical Editor ```ts // payload.config.ts import { lexicalEditor } from '@payloadcms/richtext-lexical' export default buildConfig({ editor: lexicalEditor(), // ← ต้องมีถึงจะใช้ visual editor ได้ // ... }) ``` --- ## Common Mistakes ### 1. Design skill ให้ hardcode content Design skill อาจให้แบบนี้: ```tsx // ❌ สิ่งที่ design skill อาจให้มา

Welcome to Our Site

// hardcode

Amazing content here...

// hardcode
``` ต้องแปลงเป็น: ```tsx // ✅

{post.title}

{post.heroContent && ( )}
``` ### 2. ลืม lexicalEditor() ใน payload.config ถ้าไม่มี `editor: lexicalEditor()` → visual editor จะไม่ขึ้น ### 3. ลืม Tailwind typography plugin ถ้าไม่มี `@tailwindcss/typography` → richText output จะไม่มี styling --- ## สรุป: ใครทำอะไร | Design Layer ทำ | Payload Layer ทำ | Integration ทำ | |-----------------|------------------|----------------| | Component structure | Content storage | ครอบ `RichText` ด้วย design component | | Color/tokens | richText fields | Apply design tokens กับ Payload output | | Typography system | Visual editor (/admin) | Style richText output ด้วย prose class | | Animation specs | Content rendering | Wrap output ด้วย animation classes | | Layout grid | SEO fields (via plugin) | Layout คงที่ + content จาก Payload | --- ## Related - `website-creator` — workflow หลักในการสร้างเว็บด้วย Next.js + Payload - `payload` — Payload CMS skill (fields, hooks, queries, plugins)