# เพิ่มระบบ Comment ด้วย Giscus — GitHub Discussions บน Astro Blog
Table of Contents
Giscus คืออะไร?
Giscus เป็น open-source comment system ที่ใช้ GitHub Discussions เป็น backend สำหรับเก็บ comment ทุกอย่างถูกเก็บใน GitHub repo ของเราเอง — searchable, linkable, และเราเป็นเจ้าของข้อมูลทั้งหมด
ข้อดีเมื่อเทียบกับ Disqus หรือ comment system อื่น:
| Giscus | Disqus | |
|---|---|---|
| Privacy | ไม่มี tracking, ไม่เก็บ data ของ user | มี tracking, เก็บ data, แสดง ads |
| ขนาด | Lightweight, โหลดเร็ว | JS bundle ใหญ่ ทำให้ page ช้า |
| ราคา | ฟรี 100% | ฟรีแต่มี ads, ต้องจ่ายถ้าจะเอาออก |
| Data ownership | อยู่ใน GitHub repo ของเรา | อยู่ใน Disqus server |
| Theme | Custom ได้เต็มที่ด้วย CSS | จำกัด |
สำหรับ developer blog แล้ว Giscus เป็นตัวเลือกที่ลงตัวมาก
สิ่งที่ต้องเตรียม
ก่อน integrate Giscus เราต้องเตรียมของพวกนี้ให้พร้อม:
- Public GitHub repository — repo ต้องเป็น public เพราะ Giscus ใช้ GitHub API ซึ่งอ่านได้เฉพาะ public repo
- เปิด Discussions — ไปที่ repo Settings → General → Features แล้วติ๊ก ✅ Discussions
- สร้าง Category สำหรับ Comments — เข้า Discussions tab แล้วสร้าง category ใหม่ชื่อ “Comments”
- ติดตั้ง Giscus GitHub App — ไปที่ github.com/apps/giscus แล้ว install ให้ repo ของเรา
ดึง Config Values จาก Giscus
เข้าไปที่ giscus.app แล้วกรอกข้อมูล:
- ใส่ชื่อ repo เช่น
ne7shii/ne7shii.me - เลือก Discussion Category เป็น “Comments”
- เลือก mapping เป็น
pathname - เลือก features ตามต้องการ (reactions, lazy loading ฯลฯ)
Giscus จะ generate <script> tag ให้หน้าตาประมาณนี้:
<script src="https://giscus.app/client.js" data-repo="ne7shii/ne7shii.me" data-repo-id="R_kgDORo1ibQ" data-category="Comments" data-category-id="DIC_kwDORo1ibc4C434a" data-mapping="pathname" data-strict="0" data-reactions-enabled="1" data-emit-metadata="0" data-input-position="top" data-theme="preferred_color_scheme" data-lang="en" crossorigin="anonymous" async></script>ค่าที่เราต้องเอาไปใช้คือ data-repo-id, data-category, และ data-category-id — ค่าพวกนี้ unique ต่อ repo ของเรา
Implementation ใน Astro
Config
เอา config values ที่ได้มาใส่ไว้ใน site.config.ts รวมกับ config อื่นของ site:
giscus: { repo: 'ne7shii/ne7shii.me', repoId: 'R_kgDORo1ibQ', category: 'Comments', categoryId: 'DIC_kwDORo1ibc4C434a', reactionsEnabled: true,},Origin Config
สร้างไฟล์ giscus.json ที่ root ของ project เพื่อบอก Giscus ว่า origin ไหนที่อนุญาตให้ใช้:
{ "origins": ["https://ne7shii.me", "http://localhost:4321"]}GiscusLoader Component
หัวใจของ integration คือ GiscusLoader.astro — component นี้ทำหน้าที่โหลด Giscus script แบบ lazy loading และจัดการ theme switching:
---import siteConfig from '~/site.config'const origin = Astro.url.originconst giscusConfig = siteConfig.giscusif (!giscusConfig) { throw new Error('Giscus configuration is missing in site.config.ts')}const repo = giscusConfig.repoconst repoId = giscusConfig.repoIdconst category = giscusConfig.categoryconst categoryId = giscusConfig.categoryIdconst reactionsEnabled = giscusConfig.reactionsEnabled ? '1' : '0'---
<div class="giscus" data-origin={origin} data-repo={repo} data-repo-id={repoId} data-category={category} data-category-id={categoryId} data-reactions-enabled={reactionsEnabled}></div>ส่วน client-side script จะอ่าน data attributes แล้วสร้าง <script> tag ของ Giscus แบบ dynamic:
function loadGiscus() { const giscusDiv = document.querySelector('.giscus') const origin = giscusDiv.getAttribute('data-origin') const repo = giscusDiv.getAttribute('data-repo') const repoId = giscusDiv.getAttribute('data-repo-id') const category = giscusDiv.getAttribute('data-category') const categoryId = giscusDiv.getAttribute('data-category-id') const reactionsEnabled = giscusDiv.getAttribute('data-reactions-enabled') const theme = document.documentElement.getAttribute('data-theme')
const script = document.createElement('script') script.src = 'https://giscus.app/client.js' script.setAttribute('data-repo', repo) script.setAttribute('data-repo-id', repoId) script.setAttribute('data-category', category) script.setAttribute('data-category-id', categoryId) script.setAttribute('data-mapping', 'pathname') script.setAttribute('data-strict', '0') script.setAttribute('data-reactions-enabled', reactionsEnabled) script.setAttribute('data-emit-metadata', '0') script.setAttribute('data-input-position', 'top') script.setAttribute('data-theme', `${origin}/giscus/${theme}.css`) script.setAttribute('data-lang', 'en') script.setAttribute('loading', 'lazy') script.crossOrigin = 'anonymous' script.async = true document.body.appendChild(script)}วิธีนี้ทำให้เราส่ง config จาก server-side (Astro frontmatter) ไปให้ client-side ผ่าน data attributes ได้แบบ clean ไม่ต้อง hardcode ค่าในหลายที่
แสดงใน Post Page
เพิ่ม component เข้าไปในหน้า post:
{ siteConfig.giscus && ( <section> <DividerText text="Comments" /> <GiscusLoader /> </section> )}ใช้ conditional render siteConfig.giscus && เพื่อให้ปิด comment ได้ง่ายๆ แค่ลบ giscus config ออกจาก site.config.ts
Theme-Aware Styling
ส่วนนี้คือจุดที่น่าสนใจที่สุด — ทำให้ Giscus เปลี่ยน theme ตาม site ของเราแบบ real-time โดยไม่ต้อง reload หน้า
Custom CSS Endpoint
แทนที่จะใช้ built-in theme ของ Giscus เราสร้าง dynamic CSS endpoint ที่ /giscus/[theme].css เพื่อ generate CSS ที่ตรงกับ theme ของ site:
// Dynamic route ที่ generate CSS ตาม theme name// เช่น /giscus/catppuccin-mocha.css, /giscus/github-dark.css// Map สี theme ของ site ไปเป็น CSS variables ที่ Giscus ใช้Endpoint นี้จะ return CSS พร้อม CORS header ที่อนุญาต https://giscus.app ให้โหลดได้ — เพราะ Giscus render ใน iframe ที่ origin เป็น giscus.app
MutationObserver สำหรับ Theme Change
เมื่อ user เปลี่ยน theme บน site เราใช้ MutationObserver คอย watch attribute data-theme บน <html> element:
function listenForThemeChange() { const observer = new MutationObserver((mutations) => { mutations.forEach(async (mutation) => { if ( mutation.type === 'attributes' && mutation.attributeName === 'data-theme' ) { const newTheme = document.documentElement.getAttribute('data-theme') if (newTheme) { await updateTheme(newTheme) } } }) }) observer.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'], })}PostMessage API อัปเดต Theme
เมื่อจับ theme change ได้ เราใช้ PostMessage API ส่ง message ไปให้ Giscus iframe เปลี่ยน theme โดยไม่ต้อง reload:
async function updateTheme(theme) { const giscusFrame = document.querySelector('iframe.giscus-frame') if (!giscusFrame) return
giscusFrame.contentWindow?.postMessage( { giscus: { setConfig: { theme: `${origin}/giscus/${theme}.css`, }, }, }, 'https://giscus.app', )}flow ทั้งหมด:
- User คลิกเปลี่ยน theme →
data-themeattribute บน<html>เปลี่ยน - MutationObserver จับ change ได้ → เรียก
updateTheme() - PostMessage ส่ง URL ของ CSS ใหม่ไปให้ Giscus iframe
- Giscus โหลด CSS จาก
/giscus/[theme].css→ สีเปลี่ยนทันที
ผลลัพธ์
หลัง integrate เสร็จ ผลที่ได้คือ:
- Comment box แสดงอยู่ท้ายทุก blog post พร้อม reactions
- Theme switching ทำงานแบบ seamless — เปลี่ยน theme บน site แล้ว Giscus เปลี่ยนสีตามทันที
- Comment ทุกอัน ถูกเก็บใน GitHub Discussions ของ repo — เข้าไปดู ตอบ หรือ moderate ได้โดยตรง
- Lazy loading ทำให้ไม่กระทบ performance ตอนโหลดหน้า — Giscus script จะโหลดเมื่อ user scroll ลงมาถึง