Why NextWhy Next
Voltar à lista
💻 Dev3 min de leitura

Criando um blog MDX do zero com Next.js - como este blog foi feito

O processo de criar um blog que roda só com arquivos markdown, sem CMS, usando o App Router do Next.js 16. Estrutura de pastas, configuração do MDX e SEO, tudo organizado com o fluxo de código real.

Resumo

Coloque os posts como arquivos .mdx em src/content/posts e renderize com @next/mdx: o blog roda sem CMS. O arquivo vira a URL, e um meta export no lugar do frontmatter gera a listagem e os metadados.

Nesta página

Este blog que você está lendo agora não tem banco de dados nem painel de administração. Cada post é apenas um arquivo markdown. Vou mostrar exatamente como ele foi feito.

Por que baseado em arquivos

Existem muitas ferramentas de blog, mas para um dev solo a maioria é exagero.

  • CMS: vem com painel de administração, integrações e mensalidade
  • Integração com Notion: é cômoda, mas trava na renderização, na velocidade e na customização
  • Arquivos markdown: os posts ficam versionados com o código, é gratuito, e git push já é a publicação

Eu escolhi a terceira opção. É a que tem menos atrito.

Estrutura de pastas

Olhando só o essencial, fica assim:

src/
├── app/
│   ├── page.tsx              # 홈 - 글 목록
│   ├── posts/[slug]/page.tsx # 글 상세
│   └── sitemap.ts            # /sitemap.xml
├── content/posts/*.mdx       # ← 글은 여기에만 추가
└── lib/posts.ts              # 목록·메타 로딩

O nome do arquivo vira a URL. hello.mdx abre em /posts/hello.

Configuração do MDX

Para usar MDX no App Router, adicione o @next/mdx. Como o Next 16 usa Turbopack por padrão, os plugins remark/rehype precisam ser passados como nome em string (não dá para passar referências de função).

const withMDX = createMDX({
  options: {
    remarkPlugins: ["remark-gfm"],   // 표·취소선 지원
    rehypePlugins: ["rehype-slug"],  // 제목 앵커
  },
})

meta export em vez de frontmatter

Normalmente o título e a data vão no bloco --- (frontmatter) no topo do markdown, mas como o MDX aceita JavaScript, existe um jeito mais limpo: simplesmente exportar um objeto.

export const meta = {
  title: "글 제목",
  date: "2026-07-06",
  category: "dev",
}

## 본문 시작

Na página de listagem, cada arquivo é importado dinamicamente e apenas esse meta é lido.

const mod = await import(`@/content/posts/${slug}.mdx`)
return mod.meta   // 본문 렌더 없이 메타만

Sem biblioteca de parsing, sem índice separado.

SEO desde o começo

Se o objetivo do blog é atrair visitantes, o SEO precisa entrar no começo, não depois. Eu já deixei de base metadados por post, Open Graph, dados estruturados JSON-LD, além de sitemap.xml, robots.txt e rss.xml. Vou contar essa parte com mais detalhes no próximo post.

Resumo

  • Cada post é um arquivo .mdx, e o nome do arquivo é a URL
  • @next/mdx + plugins em string (Turbopack)
  • export const meta no lugar do frontmatter
  • SEO desde o primeiro commit

Com essa mesma estrutura, você consegue colocar o seu blog no ar em 30 minutos. É desse jeito que eu venho acumulando as histórias dos meus produtos, como o A Onda do Dia.

Perguntas frequentes

Por que usar arquivos MDX em vez de um CMS?

Os posts ficam versionados junto com o código, é gratuito e o deploy já é a publicação, ou seja, é a opção com menos atrito para um dev solo. Além disso, dá para inserir imagens, código e componentes livremente.

Como montar a listagem sem frontmatter?

Basta exportar um objeto meta no topo de cada arquivo .mdx e, na página de listagem, fazer import dinâmico do arquivo lendo apenas esse meta. Não é preciso nenhuma biblioteca de parsing extra.

Artigos relacionados