Este post é um manual de referência rápida. Aqui estão todos os componentes e recursos disponíveis ao escrever um novo post. Mantenha-o aberto como guia enquanto escreve.
Markdown nativo
O Markdown padrão funciona normalmente. Todos os elementos abaixo não precisam de componentes extras.
Tipografia
Negrito, itálico, tachado e código inline funcionam nativamente.
Blockquote para destacar citações ou trechos importantes de outras fontes.
Âncoras em headings
Todos os headings (##, ###, etc.) recebem automaticamente um id gerado a partir do texto (via rehype-slug) e um link âncora # clicável ao passar o mouse (via rehype-autolink-headings).
O id é gerado em kebab-case. Use para linkar diretamente para uma seção:
[Ir para seção "# Âncoras em headings"](#âncoras-em-headings)Ir para seção "# Âncoras em headings"
Links
Links internos e externos são tratados automaticamente pelo componente Link da pasta components/mdx/link.tsx:
[texto](/rota)→ link interno com Next.js, sem reload de página usandoLinkdo Next.js[texto](https://...)ou[texto](http://...)→ abre em nova aba com ícone de link externo e usa 'a' comtarget="_blank"erel="noopener noreferrer"www.exemplo.com→ detectado como externo e prefixado comhttps://contato@exemplo.com→ vira link de e-mail
Exemplos:
Listas
Lista não ordenada:
- Item simples
- Item com
código inline - Item com negrito ou itálico
Lista ordenada:
- Primeiro passo
- Segundo passo
- Terceiro passo
Lista de tarefas (via remark-gfm):
- Tarefa concluída
- Tarefa pendente
Tabelas
Suporte nativo via remark-gfm:
| Prop | Tipo | Padrão | Descrição |
|---|---|---|---|
variant | string | 'info' | Estilo visual do componente |
title | string | — | Título opcional |
className | string | — | Classes CSS adicionais |
children | ReactNode | — | Conteúdo interno |
Código
O rehype-pretty-code processa os blocos de código com syntax highlight automático. O componente CodeBlock adiciona o botão de copiar.
Bloco simples:
function greet(name: string): string {
return `Hello, ${name}!`
}Com title e caption:
function greet(name: string): string {
return `Hello, ${name}!`
}Com showLineNumbers:
interface ButtonProps extends React.ComponentProps<'button'> {
variant?: 'default' | 'outline' | 'ghost'
}
export function Button({ variant = 'default', className, ...props }: ButtonProps) {
return (
<button className={cn(buttonVariants({ variant }), className)} {...props} />
)
}Linguagens suportadas: ts, tsx, js, jsx, bash, css, json, html, md, mdx, sql, yaml e muitas outras.
Callout
Caixas de destaque para informações importantes. Variantes: info (padrão), tip, warning, danger.
Informação
Use para contexto adicional que complementa o conteúdo principal mas não é crítico para o entendimento.
Dica
Use para boas práticas, atalhos ou formas mais eficientes de fazer algo.
Atenção
Use para alertar sobre comportamentos inesperados, deprecações ou armadilhas comuns.
Cuidado
Use para avisos críticos — dados que podem ser perdidos, falhas de segurança ou erros irreversíveis.
Sintaxe:
<Callout variant='tip' title='Título opcional'>
Conteúdo do callout. Suporta `código inline` e **negrito**.
</Callout>| Prop | Valores | Padrão |
|---|---|---|
variant | info tip warning danger | info |
title | string | — |
Badge
Etiquetas inline para categorizar ou destacar termos.
Variantes:
default primary success warning danger infoTamanhos:
sm md lgModificadores:
dashed primary dashed borderless success borderlessSintaxe:
<Badge variant='primary' size='md' dashed>texto</Badge>| Prop | Valores | Padrão |
|---|---|---|
variant | default primary success warning danger info | default |
size | sm md lg | md |
dashed | boolean | false |
borderless | boolean | false |
Kbd
Representa teclas e atalhos de teclado inline no texto.
Salve com ⌘ + S no macOS ou Ctrl + S no Windows. Abra o terminal com ⌘ + J no VS Code.
Sintaxe:
<Kbd>⌘</Kbd> + <Kbd>K</Kbd>Tabs
Conteúdo alternado em abas. Ideal para mostrar o mesmo conteúdo em múltiplas variações — gerenciadores de pacotes, linguagens, frameworks.
A prop tabs define os nomes das abas. Cada Tab recebe um value que deve corresponder a um item do array tabs.
npm install react react-domUse defaultValue para definir qual aba abre por padrão (padrão: primeira aba):
<Tabs tabs={['TypeScript', 'JavaScript']} defaultValue='JavaScript'>
<Tab value='TypeScript'>...</Tab>
<Tab value='JavaScript'>...</Tab>
</Tabs>Sintaxe completa:
<Tabs tabs={['Aba 1', 'Aba 2']}>
<Tab value='Aba 1'>Conteúdo da aba 1</Tab>
<Tab value='Aba 2'>Conteúdo da aba 2</Tab>
</Tabs>Steps
Passo a passo numerado automaticamente. Ideal para tutoriais e guias de instalação.
Instale as dependências
Execute o comando no terminal na raiz do projeto:
bun add react react-domConfigure o arquivo principal
Crie o arquivo de configuração com o conteúdo abaixo:
// app.config.ts
export default {
port: 3_000,
host: 'localhost',
}Inicie o servidor
Com tudo configurado, suba o servidor de desenvolvimento:
bun devSintaxe:
<Steps>
<Step title='Título do passo'>
Descrição e conteúdo do passo. Suporta markdown e blocos de código.
</Step>
</Steps>Accordion
Conteúdo expansível em seções. Útil para FAQs, glossários e informações complementares.
Por padrão (type='single'), apenas um item fica aberto por vez. Com type='multiple', vários itens podem ficar abertos simultaneamente.
Sintaxe:
<Accordion type='single'>
<AccordionItem title='Pergunta ou título'>
Resposta ou conteúdo expandido.
</AccordionItem>
</Accordion>| Prop | Valores | Padrão |
|---|---|---|
type | single multiple | single |
A prop value do AccordionItem é opcional — por padrão usa o próprio title como identificador.
FileTree
Visualização de estrutura de arquivos e pastas. Pastas são clicáveis e expansíveis interativamente.
- layout.tsx
- page.tsx
- next.config.ts
- tailwind.config.ts
- package.json
Sintaxe:
<FileTree>
<FileTreeFolder name='src' defaultOpen>
<FileTreeFolder name='components'>
<FileTreeFile name='button.tsx' />
</FileTreeFolder>
<FileTreeFile name='app.tsx' highlight />
</FileTreeFolder>
<FileTreeFile name='package.json' />
</FileTree>| Prop | Componente | Descrição |
|---|---|---|
name | Folder e File | Nome exibido |
defaultOpen | FileTreeFolder | Pasta aberta por padrão |
highlight | FileTreeFile | Destaca o arquivo — útil para "edite este arquivo" |
Imagens
Imagem de capa
Cada post pode ter uma imagem de capa. Basta nomear o arquivo cover e colocá-lo na mesma pasta do index.mdx — nenhuma configuração adicional é necessária.
content/posts/2026-01-15-meu-post/
├── index.mdx
└── cover.jpg ← detectado automaticamenteA capa aparece no topo do post, logo abaixo do título, e também é usada na OG image gerada para redes sociais. Extensões suportadas: .jpg, .jpeg, .png, .webp, .avif.
Imagem inline (Markdown)
Imagens inseridas diretamente no Markdown. O caminho é relativo ao index.mdx.
Sintaxe:
Exemplo:

Componente Image
Para imagens com legenda ou controle fino de propriedades, use o componente Image.
Sintaxe:
import MinhaImagem from './cover.jpg'
<Image
src={MinhaImagem}
alt='Descrição acessível obrigatória'
title='Título da imagem. Opcional.'
caption='Legenda exibida abaixo da imagem. Opcional.'
/>Exemplo:

RatioImage
Imagem com proporção fixa usando object-cover. Evita layout shift com imagens de tamanhos variados.
Sintaxe:
<RatioImage
src={MinhaImagem}
alt='Descrição acessível'
ratio={16 / 9}
title='Título da imagem. Opcional.'
caption='Legenda opcional.'
/>Exemplo:

Proporções comuns: 16 / 9, 4 / 3, 1 (quadrado), 21 / 9 (ultrawide).
ImageGrid
Grade de imagens com número de colunas configurável. O columns definido no ImageGrid é propagado automaticamente via context para todos os GridImage filhos — não é necessário repetir a prop em cada um.
Sintaxe:
<ImageGrid columns={3}>
<GridImage src={Imagem1} alt='...' caption='Legenda 1' />
<GridImage src={Imagem2} alt='...' caption='Legenda 2' />
<GridImage src={Imagem3} alt='...' caption='Legenda 3' />
</ImageGrid>Para sobrescrever o número de colunas em um item específico, passe columns diretamente no GridImage:
<ImageGrid columns={3}>
<GridImage src={Imagem1} alt='...' />
<GridImage src={Imagem2} alt='...' columns={2} /> {/* sobrescreve o contexto */}
</ImageGrid>Exemplo:



| Prop | Componente | Valores | Padrão |
|---|---|---|---|
columns | ImageGrid | 2 3 4 5 6 | 3 |
columns | GridImage | 2 3 4 5 6 | contexto |
YouTube
O id é o código na URL do YouTube — em youtube.com/watch?v=dQw4w9WgXcQ o id é dQw4w9WgXcQ.
Sintaxe:
<Youtube id='m3DJmtLdqhU' />Exemplo:
Tweet
Embed de um tweet pelo id. O id está na URL: x.com/user/status/123456789 → id é 123456789.
Sintaxe:
<Tweet id='2026946487599697948' />Exemplo:
Emojis
O remark-gemoji converte shortcodes no emoji correspondente.
Sintaxe:
:rocket: → 🚀 | :tada: → 🎉 | :warning: → ⚠️ | :bulb: → 💡 | :white_check_mark: → ✅
Exemplo:
🚀 🎉 ⚠️ 💡 ✅
Referência completa: github.com/ikatyang/emoji-cheat-sheet
Importando assets locais
Imagens e outros arquivos da mesma pasta podem ser importados diretamente no MDX:
import Cover from './cover.jpg'
import Diagrama from './diagrama.png'
<Image src={Cover} alt='Capa do post' />
<Image src={Diagrama} alt='Diagrama de arquitetura' caption='Figura 1.' />O rehype-mdx-import-media permite referenciar imagens diretamente no Markdown sem import explícito — o plugin resolve o caminho automaticamente.
Frontmatter
Todo post precisa de um frontmatter válido no topo do arquivo .mdx:
---
slug: 'meu-post'
title: 'Título do Post'
description: 'Descrição curta exibida no card e no SEO.'
publishedAt: '2026-01-15'
tags: ['tag1', 'tag2']
status: 'published'
authors:
- erivelton
---Campo status
| Valor | Comportamento |
|---|---|
draft | Nunca foi publicado — não aparece na listagem e não tem rota gerada |
published | Visível publicamente |
archived | Removido da listagem, mas a URL continua acessível com um banner de aviso |
Campo updatedAt
Quando informado, substitui o publishedAt em todas as exibições de data e troca o ícone de calendário pelo ícone de "atualizado" nos cards e no cabeçalho do post:
---
publishedAt: '2026-01-15'
updatedAt: '2026-04-03' # opcional — use ao fazer atualizações significativas
---Campos para posts de série
Quando o post faz parte de uma série, informe o slug da série e a posição. Ambos os campos são obrigatórios em conjunto:
---
series: 'slug-da-serie'
order: 1
---O banner da série é exibido automaticamente no rodapé do post com a lista de todos os posts da série e a posição atual destacada.
Séries
Séries são definidas no arquivo content/series/index.json:
[
{
"slug": "minha-serie",
"title": "Minha Série",
"description": "Descrição curta da série.",
"publishedAt": "2026-01-15",
"status": "in-progress"
}
]| Campo | Tipo | Valores | Obrigatório |
|---|---|---|---|
slug | string | kebab-case | ✅ |
title | string | — | ✅ |
description | string | máx. 300 caracteres | ✅ |
publishedAt | string | ISO date (YYYY-MM-DD) | ✅ |
status | string | planned in-progress complete | ✅ |
Após editar o arquivo, regenere o índice:
bun run build:blogSéries com status in-progress aparecem automaticamente na seção Séries em andamento da home.
Projetos
Projetos são gerados automaticamente a partir dos repositórios públicos do GitHub via API (@octokit/rest). O controle de quais repositórios aparecem é feito pelos topics de cada repositório na interface do GitHub.
| Topic | Efeito |
|---|---|
portfolio | Inclui o repositório na listagem de projetos |
featured | Marca como destaque — aparece na seção "Em destaque" |
wip | Define o status como Em progresso |
Repositórios arquivados no GitHub recebem automaticamente o status Arquivado. Os demais aparecem como Ativo.
As tags exibidas no card são os topics do repositório, excluindo os três de controle acima.
Para regenerar a listagem após alterar os topics no GitHub:
bun run build:projectsVariáveis de ambiente
O script de geração de projetos requer um token do GitHub configurado. Certifique-se
de que GITHUB_TOKEN está definido no .env.local antes de rodar o comando.