🚀 ¿Quieres añadir comentarios a tu blog sin pagar hosting, sin bases de datos y con una experiencia perfecta
para desarrolladores? En esta guía te muestro cómo integré Giscus en mi blog Hackeruna usando Angular 21.
💡 Giscus convierte GitHub Discussions en un sistema de comentarios. Tus lectores comentan con
su cuenta de GitHub, todo se sincroniza automáticamente, y tú moderas desde GitHub. ¡Gratis!
🎯 ¿Por Qué Giscus?
| Característica | Giscus | Disqus | Comentarios WP |
|---|---|---|---|
| 💰 Precio | Gratis | Freemium | Gratis |
| 🔒 Privacidad | Sin tracking | Ads y tracking | Variable |
| 👨💻 Markdown | Completo + código | Limitado | No |
| 🌙 Tema oscuro | Automático | Manual | Manual |
| 📊 Base de datos | GitHub (gratis) | Disqus servers | Tu DB |
| 🎯 Ideal para | Blogs técnicos | Blogs generales | WordPress |
📋 Requisitos Previos
- ✅ Repositorio público en GitHub
- ✅ GitHub Discussions habilitado en tu repo
- ✅ Proyecto Angular 17+ (usaremos standalone components)
- ✅ Conocimientos básicos de TypeScript
🔧 Paso 1: Configurar GitHub Discussions
- Ve a tu repositorio en GitHub
- Click en Settings → Features
- Activa Discussions
- Crea una categoría llamada
GeneraloComentarios
Mi repositorio: github.com/juanitourquiza/ng-hackeruna
🔧 Paso 2: Obtener Configuración de Giscus
- Visita giscus.app
- Ingresa tu repositorio:
usuario/repositorio - Selecciona la categoría de discussions
- Elige el mapping: pathname (recomendado para blogs)
- Copia los valores generados
Los valores que necesitas son:
data-repo="tu-usuario/tu-repo"
data-repo-id="R_xxxxx"
data-category="General"
data-category-id="DIC_xxxxx"
🚀 Paso 3: Crear el Componente en Angular
Primero, creamos la estructura del componente:
mkdir -p src/app/shared/components/giscus-comments
giscus-comments.component.ts
Este es el corazón de la integración. Nótese cómo manejamos SSR correctamente:
import {
Component,
OnInit,
OnDestroy,
PLATFORM_ID,
inject,
ChangeDetectionStrategy
} from '@angular/core';
import { isPlatformBrowser } from '@angular/common';
@Component({
selector: 'app-giscus-comments',
standalone: true,
imports: [],
templateUrl: './giscus-comments.component.html',
styleUrls: ['./giscus-comments.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class GiscusCommentsComponent implements OnInit, OnDestroy {
private platformId = inject(PLATFORM_ID);
ngOnInit(): void {
// ⚡ Solo cargar en browser, NO durante SSR
if (isPlatformBrowser(this.platformId)) {
this.loadGiscus();
}
}
ngOnDestroy(): void {
// 🧹 Limpiar al destruir el componente
if (isPlatformBrowser(this.platformId)) {
const giscusScript = document.querySelector(
'script[src="https://giscus.app/client.js"]'
);
if (giscusScript) giscusScript.remove();
const giscusFrame = document.querySelector('.giscus');
if (giscusFrame) giscusFrame.remove();
}
}
private loadGiscus(): void {
const script = document.createElement('script');
script.src = 'https://giscus.app/client.js';
// 🔧 Tu configuración de giscus.app
script.setAttribute('data-repo', 'juanitourquiza/ng-hackeruna');
script.setAttribute('data-repo-id', 'R_kgDOQTQzSQ');
script.setAttribute('data-category', 'General');
script.setAttribute('data-category-id', 'DIC_kwDOQTQzSc4C0LRM');
script.setAttribute('data-mapping', 'pathname');
script.setAttribute('data-strict', '0');
script.setAttribute('data-reactions-enabled', '1');
script.setAttribute('data-emit-metadata', '0');
script.setAttribute('data-input-position', 'bottom');
script.setAttribute('data-theme', 'preferred_color_scheme');
script.setAttribute('data-lang', 'es');
script.setAttribute('crossorigin', 'anonymous');
script.async = true;
// 📍 Insertar en el contenedor
const container = document.getElementById('giscus-container');
if (container) {
container.appendChild(script);
}
}
}
giscus-comments.component.html
<div class="giscus-wrapper">
<div id="giscus-container"></div>
</div>
giscus-comments.component.scss
:host {
display: block;
}
.giscus-wrapper {
margin-top: 2rem;
::ng-deep .giscus {
width: 100%;
}
::ng-deep .giscus-frame {
border: none;
width: 100%;
}
}
🔧 Paso 4: Integrar en la Página de Post
Ahora usamos el componente en nuestra página de detalle de post. Usamos @defer para lazy loading:
<!-- post-detail.component.html -->
<!-- ... contenido del post ... -->
<!-- Sección de Comentarios -->
<div class="mt-12 pt-8 not-prose"
style="border-top: 1px solid var(--border-color);">
<h2 class="text-2xl font-bold mb-6"
style="color: var(--text-primary);">
💬 Comentarios
</h2>
<p class="text-sm mb-6"
style="color: var(--text-secondary);">
Comparte tus ideas, preguntas o sugerencias.
Los comentarios se gestionan a través de GitHub Discussions.
</p>
<!-- ⚡ Lazy loading con @defer -->
@defer (on viewport; prefetch on idle) {
<app-giscus-comments></app-giscus-comments>
} @placeholder {
<div class="h-32 bg-gray-200 dark:bg-gray-700
animate-pulse rounded"></div>
}
</div>
No olvides importar el componente en tu post-detail.component.ts:
import { GiscusCommentsComponent } from '../../shared/components/giscus-comments/giscus-comments.component';
@Component({
// ...
imports: [
// ... otros imports
GiscusCommentsComponent
],
})
🔒 Paso 5: Configurar Content Security Policy (CSP)
⚠️ Este paso es CRUCIAL. Sin él, el navegador bloqueará el script de giscus.
En tu index.html, agrega https://giscus.app a las directivas CSP:
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://giscus.app;
style-src 'self' 'unsafe-inline' https://giscus.app;
connect-src 'self' https://giscus.app;
frame-src 'self' https://giscus.app;
">
✅ Resultado Final
Así se ve el sistema de comentarios funcionando en Hackeruna:
- 🌙 Tema automático: Se adapta a light/dark mode
- 👨💻 Markdown completo: Los lectores pueden escribir código
- 🔔 Notificaciones: Te llegan a GitHub cuando alguien comenta
- 🛡️ Moderación: Edita o elimina desde GitHub Discussions
- 📱 Responsive: Funciona perfecto en móviles
🎨 Personalización Adicional
Cambiar el Tema
// Temas disponibles:
'preferred_color_scheme' // Automático según sistema
'light' // Siempre claro
'dark' // Siempre oscuro
'dark_dimmed' // Oscuro atenuado
'dark_high_contrast' // Oscuro alto contraste
'dark_protanopia' // Accesible
'light_high_contrast' // Claro alto contraste
'light_protanopia' // Accesible
'transparent_dark' // Transparente oscuro
'noborder_light' // Sin borde claro
Cambiar el Idioma
script.setAttribute('data-lang', 'es'); // Español
script.setAttribute('data-lang', 'en'); // Inglés
script.setAttribute('data-lang', 'pt'); // Portugués
// Ver más: https://giscus.app
🔗 Repositorio Completo
Todo el código está disponible en mi repositorio:
👉 github.com/juanitourquiza/ng-hackeruna
Archivos relevantes:
src/app/shared/components/giscus-comments/– Componente completosrc/app/features/post/post-detail.component.html– Integraciónsrc/index.html– Configuración CSP
💡 Tips y Mejores Prácticas
- Usa
@defer: El widget solo carga cuando el usuario hace scroll hasta él - Limpia en
ngOnDestroy: Evita memory leaks al navegar entre posts - SSR compatible: Usa
isPlatformBrowserpara evitar errores en server - OnPush: El componente no necesita change detection reactiva
- Mapping por pathname: Cada URL = una discusión única
🤔 Preguntas Frecuentes
¿Funciona sin JavaScript?
No, giscus requiere JavaScript para cargar el widget. Considera mostrar un link a GitHub Discussions como fallback.
¿Puedo usarlo en repos privados?
No, el repositorio debe ser público para que giscus funcione.
¿Afecta el SEO?
Los comentarios se cargan dinámicamente, así que Google no los indexa directamente. Pero las discusiones en GitHub sí
son indexables.
¿Es seguro?
Sí. Giscus usa OAuth de GitHub y no almacena credenciales. Todo el código es open source.
📚 Recursos Adicionales
- Giscus – Sitio oficial
- Giscus – Repositorio GitHub
- Angular @defer – Documentación
oficial - Hackeruna – Ve la implementación en vivo
¿Te fue útil esta guía? ¡Déjame un comentario usando giscus! 👇
Si tienes dudas, también puedes abrir un Discussion en GitHub o contactarme en LinkedIn.