Development

TipTap คืออะไร? คู่มือสร้าง Rich Text Editor สำหรับ Next.js และ Laravel 2026

คู่มือสร้าง Rich Text Editor (WYSIWYG) ด้วย TipTap + ProseMirror บน Next.js และ Laravel — ตั้งค่า extension, toolbar, collaboration, image upload พร้อม code ใช้จริง 2026

AF
ADS FIT Team
·8 นาที
Share:
TipTap คืออะไร? คู่มือสร้าง Rich Text Editor สำหรับ Next.js และ Laravel 2026

# TipTap คืออะไร? คู่มือสร้าง Rich Text Editor สำหรับ Next.js และ Laravel 2026

ทุกเว็บแอปพลิเคชันที่ให้ผู้ใช้เขียนเนื้อหา ไม่ว่าจะเป็นระบบ CMS, ระบบ Admin Panel, เครื่องมือ Note-taking, Chat app หรือแพลตฟอร์ม Social Network ล้วนต้องการ Rich Text Editor ที่ทำให้ผู้ใช้จัดรูปแบบตัวอักษรได้ง่าย ไม่ต้องเขียน HTML เอง ในอดีตนักพัฒนาใช้ TinyMCE, CKEditor หรือ Quill แต่เครื่องมือเหล่านี้มักจะใหญ่, ต้องซื้อ license และไม่ยืดหยุ่นพอสำหรับ UI สมัยใหม่

TipTap คือ Rich Text Editor framework รุ่นใหม่ที่สร้างบน ProseMirror ของ Marijn Haverbeke (ผู้เขียน CodeMirror) เน้นการเป็น "Headless" — หมายความว่ามาพร้อมกับ logic ครบ แต่ให้ developer เป็นคนออกแบบ UI เอง ทำให้เข้ากับ design system ทุกแบบ (Tailwind, shadcn/ui, Material UI, Chakra) ได้ทันที บทความนี้จะอธิบายวิธีใช้ TipTap ตั้งแต่ติดตั้ง, ปรับ extension, เชื่อมกับ Next.js 15 App Router และ Laravel 12, จัดการ image upload รวมถึง Real-time collaboration

TipTap คืออะไร ต่างจาก TinyMCE และ CKEditor อย่างไร

TipTap เริ่มต้นจากการเป็น wrapper ของ ProseMirror ที่ทำให้ developer React/Vue ใช้งานง่ายขึ้น ปัจจุบันรองรับทั้ง React, Vue 3, Svelte และ Vanilla JS ส่วนตัว Pro plan เพิ่ม feature เชิงองค์กรเช่น Collaboration, AI autocomplete, Comments

| หัวข้อ | TipTap | TinyMCE | CKEditor 5 | Quill 2 |

|--------|--------|---------|------------|---------|

| Headless UI | ใช่ | ไม่ (มาพร้อม UI) | ไม่ | ไม่ |

| Free tier | Core MIT | ต้องมี API key | Free commercial ลำบาก | MIT |

| React first-class | ใช่ | adapter | adapter | third-party |

| Bundle size | เล็ก (~30KB core) | ใหญ่ (~500KB) | กลาง (~200KB) | เล็ก (~50KB) |

| Real-time collab | ใช่ (Pro + Y.js) | Pro | Pro | ต้อง self-build |

| Custom extension | ยืดหยุ่นมาก | ปานกลาง | ปานกลาง | จำกัด |

| TypeScript support | native | ใช่ | ใช่ | บางส่วน |

สถาปัตยกรรม: ProseMirror + Extension System

TipTap ไม่ใช่ monolith แต่ประกอบจากแนวคิด 3 ชั้น

  • **ProseMirror core**: จัดการ document model, schema, transaction, selection
  • **TipTap extensions**: กำหนด node (heading, paragraph, image), mark (bold, italic, link), functionality (history, keymap)
  • **React renderer**: แปลง editor state เป็น component ที่ update อัตโนมัติ
  • เราเพิ่ม/ปิด extension ได้อิสระ ถ้าไม่ต้องการ bullet list ก็ไม่ต้อง import ทำให้ bundle size เล็กที่สุดเท่าที่จะเป็นไปได้

    ขั้นตอนติดตั้ง TipTap ใน Next.js 15

    Step 1: ติดตั้ง package

    ```bash

    npm install @tiptap/react @tiptap/pm @tiptap/starter-kit

    ```

    StarterKit รวม extension พื้นฐานทั้งหมด: paragraph, heading, bold, italic, bullet list, ordered list, code block, link, history (undo/redo) เหมาะกับการเริ่มต้นอย่างรวดเร็ว

    Step 2: สร้าง Editor component

    สร้างไฟล์ `components/RichEditor.tsx`

    ```tsx

    'use client';

    import { useEditor, EditorContent } from '@tiptap/react';

    import StarterKit from '@tiptap/starter-kit';

    export default function RichEditor({ value, onChange }: {

    value: string;

    onChange: (html: string) => void;

    }) {

    const editor = useEditor({

    extensions: [StarterKit],

    content: value,

    editorProps: {

    attributes: {

    class: 'prose prose-sm max-w-none focus:outline-none min-h-[200px] p-3 border rounded',

    },

    },

    onUpdate: ({ editor }) => onChange(editor.getHTML()),

    immediatelyRender: false,

    });

    return <EditorContent editor={editor} />;

    }

    ```

    ข้อสังเกต: Next.js 15 SSR ต้องใส่ `immediatelyRender: false` เพื่อป้องกัน hydration mismatch

    Step 3: สร้าง Toolbar ด้วย shadcn/ui

    ```tsx

    import { Button } from '@/components/ui/button';

    import { Bold, Italic, List, Heading1 } from 'lucide-react';

    function Toolbar({ editor }) {

    if (!editor) return null;

    return (

    <div className="flex gap-1 border-b pb-2 mb-2">

    <Button size="icon" variant={editor.isActive('bold') ? 'default' : 'ghost'}

    onClick={() => editor.chain().focus().toggleBold().run()}>

    <Bold className="h-4 w-4" />

    </Button>

    <Button size="icon" variant={editor.isActive('italic') ? 'default' : 'ghost'}

    onClick={() => editor.chain().focus().toggleItalic().run()}>

    <Italic className="h-4 w-4" />

    </Button>

    <Button size="icon"

    onClick={() => editor.chain().focus().toggleBulletList().run()}>

    <List className="h-4 w-4" />

    </Button>

    <Button size="icon"

    onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}>

    <Heading1 className="h-4 w-4" />

    </Button>

    </div>

    );

    }

    ```

    Image Upload ด้วย Cloud Storage

    ระบบ CMS มักต้องการให้ผู้ใช้ drag-drop รูปภาพลงใน editor สามารถทำได้ด้วย TipTap `Image` extension ร่วมกับ `@tiptap/extension-file-handler`

    ```tsx

    import Image from '@tiptap/extension-image';

    import FileHandler from '@tiptap/extension-file-handler';

    const editor = useEditor({

    extensions: [

    StarterKit,

    Image,

    FileHandler.configure({

    allowedMimeTypes: ['image/png', 'image/jpeg', 'image/webp'],

    onDrop: async (editor, files, pos) => {

    for (const file of files) {

    const form = new FormData();

    form.append('file', file);

    const res = await fetch('/api/upload', { method: 'POST', body: form });

    const { url } = await res.json();

    editor.commands.insertContentAt(pos, {

    type: 'image',

    attrs: { src: url, alt: file.name },

    });

    }

    },

    }),

    ],

    });

    ```

    API route `app/api/upload/route.ts` อัปโหลดไฟล์ไป S3/R2/Cloudinary แล้วคืน URL กลับ

    ใช้งานกับ Laravel Backend

    ฝั่ง backend เราต้อง sanitize HTML ที่ส่งมาจาก TipTap ก่อนบันทึกลง database เพื่อป้องกัน XSS

    ```bash

    composer require mews/purifier

    ```

    ```php

    use Mews\Purifier\Facades\Purifier;

    public function store(Request $request)

    {

    $clean = Purifier::clean($request->input('content'), [

    'HTML.Allowed' => 'p,br,strong,em,u,h1,h2,h3,h4,ul,ol,li,a[href|title],img[src|alt|title],code,pre,blockquote',

    ]);

    Post::create([

    'title' => $request->title,

    'content' => $clean,

    'user_id' => auth()->id(),

    ]);

    }

    ```

    แนะนำให้เก็บใน column `LONGTEXT` หรือ `JSON` (ถ้าใช้ TipTap JSON output ด้วย `editor.getJSON()`) จะง่ายต่อการ manipulate ภายหลัง

    Real-time Collaboration ด้วย Y.js

    สำหรับแอปประเภท Notion/Google Docs ต้องการ collaborative editing แบบ real-time TipTap รองรับผ่าน extension `@tiptap/extension-collaboration` ที่ใช้ Y.js CRDT

    ```bash

    npm install yjs y-websocket @tiptap/extension-collaboration @tiptap/extension-collaboration-cursor

    ```

    ```tsx

    import * as Y from 'yjs';

    import { WebsocketProvider } from 'y-websocket';

    import Collaboration from '@tiptap/extension-collaboration';

    import CollaborationCursor from '@tiptap/extension-collaboration-cursor';

    const ydoc = new Y.Doc();

    const provider = new WebsocketProvider('wss://collab.example.com', 'room-123', ydoc);

    const editor = useEditor({

    extensions: [

    StarterKit.configure({ history: false }),

    Collaboration.configure({ document: ydoc }),

    CollaborationCursor.configure({

    provider,

    user: { name: user.name, color: '#8b5cf6' },

    }),

    ],

    });

    ```

    สามารถ self-host Y.js WebSocket server ด้วย y-websocket-server หรือใช้ Liveblocks/TipTap Cloud ถ้าไม่อยาก maintain เอง

    Best Practice ด้าน Security

  • **Sanitize HTML ทุกครั้ง**: ฝั่ง server ใช้ HTMLPurifier หรือ DOMPurify ก่อนบันทึก
  • **Content Security Policy**: ปิด `unsafe-inline` แล้ว allow-list script ที่ต้องการเท่านั้น
  • **Rate-limit image upload**: ป้องกันผู้ใช้อัปโหลดจำนวนมาก
  • **Validate file type**: ตรวจ MIME type ฝั่ง server ไม่ใช่แค่ client
  • **Image CDN**: ใช้ Cloudinary, ImageKit หรือ Bunny.net เพื่อ resize + WebP อัตโนมัติ
  • สรุปและ CTA

    TipTap เป็น Rich Text Editor ที่ยืดหยุ่น, bundle size เล็ก, และเข้ากับ ecosystem React/Vue สมัยใหม่ได้ดี เหมาะสำหรับนักพัฒนาที่ต้องการควบคุม UI/UX 100% โดยไม่ต้องเสียเวลากับ editor ที่มี UI แน่นอนอยู่แล้ว

    ประเด็นสำคัญที่ควรจำ:

  • ใช้ `StarterKit` เป็นจุดเริ่มต้นเพื่อให้ได้ extension พื้นฐานครบ
  • ตั้ง `immediatelyRender: false` เมื่อใช้กับ Next.js App Router
  • เก็บ output เป็น JSON ดีกว่า HTML ถ้าต้อง manipulate ทีหลัง
  • Sanitize HTML ฝั่ง backend ทุกครั้งก่อนบันทึก
  • สำหรับ collab ใช้ Y.js + y-websocket หรือ TipTap Cloud
  • อยากสร้างระบบ CMS หรือ Editor เฉพาะธุรกิจของคุณ? ทีม ADS FIT รับออกแบบและพัฒนา Rich Text Editor / CMS ที่ custom workflow ให้เหมาะกับองค์กร ตั้งแต่ drag-drop image upload, collaboration, ไปจนถึง AI autocomplete [ติดต่อเรา](https://www.adsfit.co.th/contact) หรืออ่านบทความที่เกี่ยวข้อง: [shadcn/ui](https://www.adsfit.co.th/blog/shadcn-ui-nextjs-component-guide-sme-thailand-2026), [Next.js 15](https://www.adsfit.co.th/blog/nextjs-15-web-development-guide-2026), [Laravel 12](https://www.adsfit.co.th/blog/laravel-12-new-features-streamlined-structure-upgrade-guide-sme-thailand-2026)

    Tags

    #TipTap#Rich Text Editor#ProseMirror#Next.js#Laravel#WYSIWYG

    สนใจโซลูชันนี้?

    ปรึกษาทีม ADS FIT ฟรี เราพร้อมออกแบบระบบที่ฟิตกับธุรกิจของคุณ

    ติดต่อเรา →

    บทความที่เกี่ยวข้อง