Guia Prático: Renderizando Markdown e JSON no Front-end com Markedjs

Introdução
A separação eficiente entre conteúdo e estrutura é uma premissa fundamental no desenvolvimento front-end moderno. Neste guia prático, você aprenderá a construir um site dinâmico que consome uma lista de conteúdos estruturada em JSON e renderiza arquivos Markdown de forma assíncrona, sem a necessidade de frameworks pesados.
Utilizando apenas HTML, CSS, Vanilla JavaScript e a biblioteca marked.js, desenvolveremos uma solução leve, modular e segura. Detalharemos cada etapa da implementação: desde a estruturação dos dados e requisições assíncronas com a Fetch API, até a geração da navegação dinâmica, captura de eventos de clique via delegação e a conversão do Markdown para HTML em tempo real. O resultado é uma arquitetura prática, ideal para documentações, portfólios ou blogs baseados em arquivos estáticos.
Conceitos Fundamentais
Antes de iniciarmos o código, é importante alinhar os pilares desta arquitetura:
- JSON como Banco de Dados: Atuará como o índice do nosso site, armazenando metadados (título, descrição, data e caminho) de cada publicação.
- Markdown para Conteúdo: Arquivos
.mdarmazenarão o texto bruto, facilitando a escrita e a manutenção sem misturar formatação com código HTML. - Renderização Sob Demanda (Lazy Loading lógico): O conteúdo de um artigo só é baixado e processado pelo navegador quando o usuário clica no link, economizando banda e processamento na carga inicial da página.
Pré-requisitos
Para acompanhar este manual, você precisará de:
- Conhecimento básico de HTML e JavaScript (ES6+).
- Um editor de código (como o VS Code).
- Um servidor web local (como a extensão Live Server do VS Code) para evitar bloqueios de CORS ao realizar requisições
fetchem arquivos locais.
Passo a Passo da Implementação
Passo 1: Estrutura do Projeto
Organize os arquivos do seu projeto com a seguinte hierarquia para manter a separação clara entre lógica, dados e conteúdo:
/meu-projeto
├── index.html
├── app.js
├── dados/
│ └── artigos.json
└── conteudo/
├── primeiro-artigo.md
└── segundo-artigo.md
Passo 2: O Arquivo de Índice (JSON) e o Conteúdo (Markdown)
Crie o arquivo dados/artigos.json. Ele será o array de objetos que alimentará nossa interface:
[
{
"titulo": "Arquitetura Front-end",
"descricao": "Como separar dados de marcação de forma eficiente.",
"data": "2026-08-10",
"link": "../conteudo/primeiro-artigo.md"
},
{
"titulo": "Segurança no DOM",
"descricao": "Prevenindo ataques XSS ao manipular o DOM.",
"data": "2026-08-15",
"link": "../conteudo/segundo-artigo.md"
}
]
Em seguida, crie um arquivo de exemplo em conteudo/primeiro-artigo.md:
# Arquitetura Front-end
Esta é uma demonstração de como o **marked.js** processa listas e formatações:
* Item 1
* Item 2
Passo 3: Estrutura Base em HTML
No arquivo index.html, vamos importar a biblioteca marked.js via CDN, referenciar nosso script e criar os contêineres onde o JavaScript injetará os dados.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Blog Dinâmico com Markdown</title>
<!-- Importação da biblioteca marked.js -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; padding: 20px; max-width: 800px; margin: auto; }
.artigo-card { border: 1px solid #ddd; padding: 15px; margin-bottom: 15px; border-radius: 5px; }
#markdown { margin-top: 30px; padding-top: 20px; border-top: 2px solid #333; }
</style>
</head>
<body>
<h1>Publicações Recentes</h1>
<!-- Área onde a lista do JSON será renderizada -->
<div id="conteudo"></div>
<!-- Área onde o conteúdo do Markdown será exibido -->
<div id="markdown"></div>
<!-- Importação da nossa lógica -->
<script src="app.js"></script>
</body>
</html>
Passo 4: Lógica de Aplicação (JavaScript)
No arquivo app.js, vamos construir a lógica em blocos funcionais.
1. Configuração e Inicialização: Definimos o caminho do nosso “banco de dados” e iniciamos o consumo.
const arquivoIndice = "dados/artigos.json";
// Inicia a aplicação buscando o JSON
fetch(arquivoIndice)
.then((response) => {
if (!response.ok) throw new Error("Erro ao carregar o JSON");
return response.text();
})
.then((resultado) => listar(resultado))
.catch((error) => console.error("Falha na inicialização:", error));
2. Conversão de Caminhos Relativos para Absolutos:
Para garantir que o fetch encontre os arquivos .md independentemente de onde a página foi aberta, criamos uma função utilitária:
const obterCaminhoCompleto = (caminhoRelativo) => {
const caminhoPaginaAtual = window.location.href;
return new URL(caminhoRelativo, caminhoPaginaAtual).href;
};
3. Renderização Segura da Lista (Mitigação de XSS): Esta função recebe a string JSON, converte em objeto e cria os elementos HTML.
function listar(jsonString) {
const content = document.querySelector("#conteudo");
const lista = JSON.parse(jsonString);
lista.forEach((item) => {
// Criação dos elementos
const container = document.createElement("div");
container.className = "artigo-card";
const titulo = document.createElement("h4");
titulo.textContent = item.titulo;
const descricao = document.createElement("p");
descricao.textContent = item.descricao;
const data = document.createElement("p");
data.textContent = "Publicado em: " + item.data;
const link = document.createElement("a");
link.href = obterCaminhoCompleto(item.link);
link.textContent = "Ler artigo completo";
// Montagem da estrutura
container.append(titulo, descricao, data, link);
content.append(container);
});
}
Nota Arquitetural de Segurança: Optamos pelo uso de
document.createElemente.textContentem vez de concatenar strings com.innerHTML +=. Se os dados do JSON vierem de fontes externas (como uma API), o uso de.innerHTMLabre margem para ataques de XSS (Cross-Site Scripting). OtextContentgarante que o conteúdo seja interpretado estritamente como texto puro. Além disso, o método.append()oferece melhor performance na renderização de listas longas.
4. Consumo e Conversão do Markdown:
Quando o usuário clicar em um link, esta função buscará o arquivo físico e delegará a conversão para a biblioteca marked.js.
function mostrarMark(caminhoMarkdown) {
const visual = document.querySelector("#markdown");
visual.innerHTML = "<p>Carregando...</p>"; // Feedback visual
fetch(caminhoMarkdown)
.then((response) => {
if (!response.ok) throw new Error("Erro ao carregar o arquivo Markdown");
return response.text();
})
.then((resultado) => {
// O marked.parse converte a string markdown para tags HTML válidas
visual.innerHTML = marked.parse(resultado);
})
.catch((error) => {
console.error(error);
visual.innerHTML = "<p>Erro ao carregar o conteúdo.</p>";
});
}
Atenção: Ao contrário da função listar, aqui utilizamos .innerHTML intencionalmente, pois o objetivo do marked.parse() é gerar marcação HTML. Caso os arquivos Markdown sejam gerados por usuários (UGC), é imperativo sanitizar a saída com bibliotecas como o DOMPurify antes da injeção no DOM.
5. Interceptação de Cliques via Delegação de Eventos:
Em vez de atrelar um evento de clique (addEventListener) a cada link individualmente durante a criação, aplicamos o conceito de Delegação de Eventos no documento global.
document.addEventListener('click', function (event) {
// Verifica se o elemento clicado é um link DENTRO da nossa div de conteúdo
if (event.target.tagName === 'A' && event.target.closest('#conteudo')) {
event.preventDefault(); // Impede o redirecionamento padrão da página
const urlAlvo = event.target.getAttribute('href');
mostrarMark(urlAlvo);
}
});
Por que delegar? A delegação garante que a interceptação funcione para elementos que foram injetados no DOM dinamicamente após o carregamento inicial da página. Além de manter o código mais limpo, exige menos memória do navegador, pois requer apenas um listener ativo.
Erros Comuns e Validações
- Bloqueio por CORS (
Cross-Origin Resource Sharing): Se você abrir o arquivoindex.htmlcom um duplo clique direto no navegador (usando o protocolofile://), o métodofetch()falhará por questões de segurança do navegador. Solução: Execute o projeto através de um servidor local. No VS Code, instale a extensão “Live Server” e clique em “Go Live”. - Captura de links indesejados: O uso estrito da condição
event.target.closest('#conteudo')na delegação de eventos é vital. Sem ele, a lógica interceptaria cliques em links do rodapé ou do menu principal de navegação, quebrando a usabilidade do restante do site. - Caminhos de Arquivo Inválidos: Certifique-se de que a relação entre o local do
index.htmle os caminhos declarados dentro do seuartigos.jsonesteja perfeitamente alinhada. A funçãoobterCaminhoCompletobaseia-se na URL atual para fazer essa resolução.
Conclusão
Neste tutorial, aprendemos a estabelecer uma arquitetura front-end robusta e livre de dependências pesadas, listando informações a partir de uma base de dados JSON e exibindo-as de forma segura no DOM. Ao combinar a delegação de eventos com requisições assíncronas e o poder de conversão da biblioteca marked.js, você implementou o núcleo funcional de um gerador de sites estáticos. Esse padrão arquitetural (dados estruturados + conteúdo formatado + renderização sob demanda) proporciona alta performance, facilita a manutenção a longo prazo e pode ser facilmente escalado para projetos maiores, integrações com APIs reais ou sistemas de gestão de conteúdo headless (Headless CMS).
Comentários
Seja o primeiro a comentar!
Deixe seu comentário