Introdução
Este post explica em detalhe como configurei o sistema de comentários do blog usando Giscus (frontend) com GitHub Discussions (backend) sobre uma base Hugo + PaperMod. É um complemento prático ao post de setup inicial, com justificativas, pré-requisitos, passos reproduzíveis e a solução adotada para sincronizar o tema do Giscus com o toggle de tema do PaperMod.
O objetivo é que você consiga replicar o mesmo fluxo no seu repositório e entender as decisões e gargalos que enfrentei.
Por que usar Giscus + GitHub Discussions?
- comentários ficam no GitHub (sem banco de dados adicional)
- autenticação via GitHub (menor atrito para desenvolvedores)
- suporte a Markdown, reações e moderação via GitHub
- integração leve e moderna para sites estáticos (Cloudflare Pages compatível)
- histórico e moderação centralizados no repositório/organização
Contra-argumentos e quando não usar:
- se você precisa de perfis anônimos ou autenticação fora do GitHub, Giscus não é ideal;
- se quiser evitar depender do GitHub para comentários (privacidade/lock-in), escolha outra solução;
- para sites com alto tráfego/conteúdo sensível, avalie requisitos de moderação e rate limits.
Pré-requisitos
- repositório GitHub com Discussions habilitado
- ser administrador do repositório (para instalar o GitHub App do Giscus)
- Hugo + PaperMod funcionando (veja
config/_default/hugo.toml)
No seu repositório, ative Discussions em: Settings > Features > Discussions.
Instalar e autorizar o Giscus
- Instale o GitHub App do Giscus: https://github.com/apps/giscus
- Conceda acesso ao repositório onde os comentários devem ser armazenados
- Crie a categoria em Discussions (ex.: “Blog Comments”)
Sem essa etapa o Giscus não consegue criar discussions automaticamente.
Gerar a configuração inicial
Use o configurador oficial em https://giscus.app para gerar os parâmetros iniciais. As opções importantes:
Repository: usuário/repo (ex.:meu-usuario/meu-repo)Mapping:pathname(recomendado: URL estável)Category: a categoria criada em DiscussionsTheme/lang/reactionsconforme necessidade
O configurador retorna um snippet <script> com data-* attributes, vamos parametrizar isso no Hugo em vez de colar direto no template.
Estrutura no Hugo (recomendo)
Centralize a configuração em config/_default/hugo.toml (ou hugo.yaml) dentro de [params.giscus]. Exemplo (já presente neste repositório):
[params]
comments = true
[params.giscus]
repo = "bortoloso/bortolog"
repoId = "R_kgDOSet8pA"
category = "Blog Comments"
categoryId = "DIC_kwDOSet8pM4C9LyN"
mapping = "pathname"
strict = "0"
reactionsEnabled = "1"
emitMetadata = "0"
inputPosition = "bottom"
theme = "light"
lang = "pt"
loading = "lazy"
Essa abordagem facilita manutenção, permite trocar valores por ambiente e evita duplicação nos partials.
Verifique os valores atuais em
config/_default/hugo.toml.
O problema do tema (PaperMod x Giscus)
O PaperMod aplica o tema (dark/light) usando três fontes:
localStorage.getItem("pref-theme")document.documentElement.dataset.themewindow.matchMedia('(prefers-color-scheme: dark)')
Já o Giscus decide o tema somente no momento do carregamento do script. Resultado observado:
- o blog pode estar em dark mode (localStorage), mas o Giscus inicia com o tema do sistema;
- ao trocar o tema do blog, o iframe do Giscus não acompanha automaticamente.
Isso gera inconsistência visual e confusão para usuários.
Solução adotada (partial dinamicamente carregado e atualização por postMessage)
A solução foi mover a renderização do script para um partial Hugo (layouts/partials/comments.html) que:
- lê os parâmetros de
Site.Params.giscus; - determina o tema atual consultando
localStorage,document.documentElement.dataset.themeeprefers-color-scheme; - injeta o
scriptdo Giscus comdata-themecorreto; - adiciona um listener para detectar toggle do PaperMod e enviar
postMessageao iframe do Giscus para atualizar o tema.
Trecho do partial (reduzido), já aplicado no blog:
{{ if and .Site.Params.comments (not .Params.disableComments) }}
<div id="giscus-container"></div>
<script>
(function () {
function getTheme() {
const storedTheme = localStorage.getItem("pref-theme");
if (storedTheme === "dark" || storedTheme === "light") return storedTheme;
const htmlTheme = document.documentElement.dataset.theme;
if (htmlTheme === "dark" || htmlTheme === "light") return htmlTheme;
return window.matchMedia('(prefers-color-scheme: dark)').matches ? "dark" : "light";
}
function renderGiscus() {
const container = document.getElementById("giscus-container");
if (!container || container.hasChildNodes()) return;
const script = document.createElement("script");
script.src = "https://giscus.app/client.js";
script.async = true;
script.crossOrigin = "anonymous";
script.setAttribute("data-repo", "{{ .Site.Params.giscus.repo }}");
script.setAttribute("data-theme", getTheme());
// ... outros atributos ...
container.appendChild(script);
}
function updateGiscusTheme() {
const iframe = document.querySelector("iframe.giscus-frame");
if (!iframe) return;
iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: getTheme() } } }, "https://giscus.app");
}
renderGiscus();
document.addEventListener("click", function (e) {
const toggle = e.target.closest("#theme-toggle");
if (!toggle) return;
setTimeout(updateGiscusTheme, 150);
});
})();
</script>
{{ end }}
Observações:
#theme-toggleé o seletor do botão do PaperMod neste tema; ajustei o timeout para permitir que o PaperMod atualize olocalStorageantes de enviar a mensagem.- o
postMessageparahttps://giscus.appfaz com que o iframe atualize o tema sem precisar recarregar a página.
Configuração detalhada e reprodução passo a passo
- Habilite Discussions e crie a categoria (ex.: “Blog Comments”).
- Instale o GitHub App do Giscus no repositório.
- Copie os valores do configurador do Giscus (
repo,repoId,category,categoryId, etc.) paraconfig/_default/hugo.tomlem[params.giscus]. - Crie/edite
layouts/partials/comments.htmlcom o parcial dinâmico (modelo acima). - Teste localmente:
hugo server -D
# abra http://localhost:1313 e verifique comentários e tema
- Ajuste
themeem[params.giscus]para comportamento padrão (ex.:light), o partial fará override dinâmico.
Testes e validação
- Verifique que a discussion é criada ao visitar um post pela primeira vez.
- Troque o tema no toggle do PaperMod e confirme que o iframe do Giscus muda para
dark/lightsem recarregar a página. - Se usar
data-loading="lazy", teste em um post com muitos comentários para garantir que o script seja injetado apenas uma vez.
Problemas comuns e resolução
- Giscus não cria discussion: verifique se o App Giscus tem permissão e se
repoId/categoryIdestão corretos. - Tema incorreto no Giscus: confirme que o
localStoragearmazenapref-themee que o seletor do toggle (#theme-toggle) existe no tema. - Partial duplicado/carregado duas vezes: verifique se o
containertem child nodes antes de injetar o script. - CORS / postMessage silencioso: assegure que o
targetOriginéhttps://giscus.appao usarpostMessage.
Privacidade e moderação
- Comentários estão no GitHub, política de privacidade do GitHub se aplica.
- Moderação (fechar discussion, excluir comentários) é feita no GitHub por quem tem permissão na categoria.
- Se quiser comentários anônimos, Giscus não é a melhor escolha.
Conclusão e recomendações rápidas
- Giscus + GitHub Discussions é uma solução prática para blogs técnicos hospedados em Pages/Netlify/Vercel.
- Parametrize via
config/_default/hugo.tomle renderize com um partial dinâmico, isso mantém o código limpo e reprodutível. - A sincronização de tema via
localStorage+postMessageresolve a maior dor observada com PaperMod. - Existem diversas outras opções para habilitar comentários em posts, usei o Giscus apenas para permitir, de alguma forma, um meio de interação com as postagens.
