Blog
App enxuta de blog em Nextra/Next.js/TypeScript
ToDo
- Completar a documentação em Tags Page
Links
Setup
- 🪜 Passo 0 – Criar app Next.js para docs
- 🚀 Passo 1 – Instalar o Nextra
- ⚙️ Passo 2 – Add Next.js config
- Passo 3 - Add mdx-components file
- Passo 4 - Setup search
- Passo 5 - Create the root layout
- 🎉 Passo 5.1 - Criar page.md e rodar o projeto!
- Passo 6 - Render MDX files
- Get Posts and Their Tags
- Posts Page
- Tags Page
🪜 Passo 0 – Criar app Next.js para blog
pnpm create next-app apps/blog --typescript --eslint=false
🚀 Passo 1 – Instalar o Nextra
No diretório da app (apps/blog):
pnpm add nextra nextra-theme-blog shiki
⚙️ Passo 2 – Add Next.js config
No arquivo apps/docs/next.config.ts, substitua tudo por:
import nextra from 'nextra'
// Set up Nextra with its configuration
const withNextra = nextra({
// ... Add Nextra-specific options here
})
// Export the final Next.js config with Nextra included
export default withNextra({
// ... Add regular Next.js options here
turbopack: {
resolveAlias: {
// Path to your `mdx-components` file with extension
'next-mdx-import-source-file': './src/mdx-components.ts'
}
}
})
Passo 3 — Add mdx-components file
https://nextra.site/docs/file-conventions/mdx-components-file
The mdx-components file is required, you use it to customize styles via useMDXComponents function.
The mdx-components.js file must export a single function named useMDXComponents.
apps/docs/src/mdx-components.ts
import { useMDXComponents as getThemeComponents } from 'nextra-theme-blog' // nextra-theme-blog or your custom theme
import type { MDXComponents } from 'nextra/mdx-components'
// Get the default MDX components
const themeComponents = getThemeComponents()
// Merge components
export function useMDXComponents(components: MDXComponents) {
return {
...themeComponents,
...components
}
}
Passo 4 - Setup search
To set up search, follow the instructions on the Search Engine page.
Install pagefind as a dev dependency
pnpm add -D pagefind
Add a postbuild script
apps/docs/package.json
// Server builds
"scripts": {
"postbuild": "pagefind --site .next/server/app --output-path public/_pagefind"
}
// OU
// Static exports
"scripts": {
"postbuild": "pagefind --site .next/server/app --output-path out/_pagefind"
}
Ignore generated files
Add _pagefind/ to your .gitignore file to avoid committing generated index files.
Verify indexing output
After building and running the postbuild script, check that a _pagefind/ directory exists in public/ or out/. Start your app and test the search bar to confirm everything is working.
Configuration
Search is enabled by default. You can disable it entirely by setting search: false in your next.config.mjs file:
import nextra from 'nextra'
const withNextra = nextra({
search: false
})
export default withNextra()
To disable code block indexing while keeping search enabled set search: { codeblocks: false }:
import nextra from 'nextra'
const withNextra = nextra({
search: { codeblocks: false }
})
export default withNextra()
Passo 5 - Create the root layout
Next, create the root layout of your application inside the app folder. This layout will be used to configure your Nextra Theme:
apps/docs/src/app/layout.tsx
🎉 Passo 5.1 - Criar page.md e rodar o projeto!
apps/docs/src/app/page.md
# Hello, Nextra!
🎉🎉🎉🎉🎉
- If you can see this, it's working!!!!!
Passo 6 - Render MDX files
There are two ways to render MDX files using file-based routing, add your MDX files via page files or content directory convention.
Get Posts and Their Tags
https://nextra.site/docs/blog-theme/get-posts-and-tags
apps/blog/src/app/posts/get-posts.ts
Posts Page
https://nextra.site/docs/blog-theme/posts
apps/blog/src/app/posts/page.tsx
Tags Page
https://nextra.site/docs/blog-theme/tags
app/tags/[tag]/page.jsx
Generate RSS feed
https://nextra.site/docs/blog-theme/rss
app/rss.xml/route.js
Notas
Estou tentando configurar um blog com Nextra. Está dando runtime error:
Runtime TypeError Server
Cannot read properties of undefined (reading '0') src/app/posts/get-posts.ts (5:41) @ getPosts
3 |
4 | export async function getPosts() {
> 5 | const { directories } = normalizePages({
| ^
6 | list: await getPageMap('/posts'),
7 | route: '/posts'
8 | })
Como disse, não sei como funciona o Nextra configurado como blog. Onde colocar os posts, o workflow, etc.
Não implemente nada agora. Só me explique porque estou tendo esse erro quando abro: http://localhost:3000/posts
Em http://localhost:3000/ Mostra a página apps/blog/src/app/page.mdx
O erro está ocorrendo porque a função getPageMap('/posts') está retornando um valor inválido (provavelmente undefined ou uma lista vazia/incompatível), o que faz com que normalizePages falhe internamente ao tentar acessar propriedades do parâmetro list. Isso acontece porque, no contexto do Nextra com App Router, não há posts reais configurados para serem listados.
Explicação detalhada:
-
Estrutura esperada para um blog com Nextra (tema blog + App Router): Os posts devem ser organizados como subdiretórios em
src/app/posts/, onde cada post é uma pasta contendo um arquivopage.mdx(ou similar) com frontmatter (ex.: título, data, tags). Exemplo:src/app/posts/
├── page.tsx # Página de listagem (já existe)
├── get-posts.ts # Função auxiliar (já existe)
├── meu-primeiro-post/
│ └── page.mdx # Conteúdo do post com frontmatter
└── outro-post/
└── page.mdxSem esses subdiretórios com arquivos MDX válidos, não há "posts" para o
getPageMapdetectar e normalizar. -
O que
getPageMap('/posts')faz: Ele tenta obter o mapa de páginas para a rota/posts. Como não há subpáginas/posts reais (apenas page.tsx e get-posts.ts), o retorno é vazio ouundefined, em vez de uma lista válida de páginas. -
Por que
normalizePagesfalha: A função espera umalistválida (um array de objetos representando páginas). Selistforundefined, o código interno do Nextra tenta acessarlist[0]ou propriedades similares, resultando no erro "Cannot read properties of undefined (reading '0')". Isso ocorre especificamente na linha 5 do get-posts.ts, ondenormalizePagesé chamado. -
Diferença entre
/e/posts:- Em
http://localhost:3000/(página inicial), o Nextra carregapage.mdxdiretamente, que é um arquivo MDX simples e não depende de listagem de posts, por isso funciona. - Em
http://localhost:3000/posts, a página tenta executargetPosts()para renderizar a listagem, mas falha na obtenção/normalização dos dados dos posts inexistentes.
- Em
-
Workflow típico do Nextra blog:
- Criação de posts: Adicione pastas em
src/app/posts/compage.mdxcontendo frontmatter comotitle,date,tags. - Listagem: page.tsx usa
getPosts()para buscar e ordenar os posts por data. - Tags:
getTags()extrai tags dos posts. - Sem posts, o mapa de páginas fica vazio, causando o erro na tentativa de normalização.
- Criação de posts: Adicione pastas em
Resumindo, o erro surge porque o código assume a presença de posts (subdiretórios com MDX), mas a estrutura atual não os tem, levando a um list inválido passado para normalizePages. Para resolver, seria necessário adicionar posts reais conforme a estrutura descrita, mas como solicitado, não estou implementando nada agora. Se precisar de mais detalhes sobre o setup, posso explicar o workflow completo.
Implemente duas pastas com posts de teste para testes. Estou rodando o app em outro monitor e vendo as mudanças. Vamos ver o que vai mudar aqui.
- apps/blog/src/app/posts/teste-post-1/page.mdx
- apps/blog/src/app/posts/teste-post-2/page.mdx
Implementei duas pastas de posts de teste em posts:
teste-post-1/compage.mdx(data: 2023-10-30, tags: ['teste', 'blog'])teste-post-2/compage.mdx(data: 2023-10-31, tags: ['teste', 'nextra'])
Cada post tem frontmatter básico (title, date, tags) e conteúdo simples em MDX. Isso deve resolver o erro anterior, pois agora há posts reais para o getPageMap detectar e normalizePages processar.
No seu navegador (http://localhost:3000/posts), você deve ver os posts listados, ordenados por data decrescente (teste-post-2 primeiro). Os tags também aparecerão na página de listagem. Se houver problemas ou quiser ajustar o conteúdo/datas, me avise!
Publicar na Vercel
Resumo dos Passos para Publicar o Blog (Next.js) na Vercel
Como o projeto é um monorepo gerenciado com pnpm e o blog está em blog, o deploy na Vercel requer configurações específicas para apontar para o subdiretório correto. A Vercel suporta monorepos com pnpm, mas você precisará ajustar as configurações de build. Aqui está um plano passo a passo (sem execução, apenas planejamento):
-
Preparar o Repositório no GitHub:
- Certifique-se de que o repositório (
qb-monorepo) esteja atualizado no GitHub com todas as mudanças do blog. - O blog deve estar funcional localmente (ex.:
pnpm run devno diretório raiz, acessando blog).
- Certifique-se de que o repositório (
-
Criar uma Conta na Vercel e Conectar o Repositório:
- Acesse vercel.com e faça login (ou crie uma conta gratuita).
- Clique em "New Project" e importe o repositório do GitHub (
qb-monorepo). - Autorize o acesso ao repositório se solicitado.
-
Configurar o Projeto na Vercel:
- Root Directory: Defina como blog (para que a Vercel use esse diretório como raiz do projeto, ignorando o monorepo).
- Build Settings:
- Build Command:
pnpm run build(Vercel detectará automaticamente o Next.js, mas confirme que usa pnpm). - Output Directory: Deixe padrão (
.nextserá gerado automaticamente). - Install Command:
pnpm install(para garantir que pnpm seja usado em vez de npm/yarn).
- Build Command:
- Environment Variables: Adicione qualquer variável necessária (ex.: se usar APIs externas, como chaves de busca do Nextra). Por enquanto, provavelmente não há, mas verifique package.json ou
next.config.ts. - Framework Preset: Selecione "Next.js" (Vercel detectará automaticamente).
-
Configurações Específicas para Monorepo e pnpm:
- A Vercel suporta pnpm nativamente em projetos recentes. Se houver problemas, adicione um arquivo
vercel.jsonna raiz do repositório (não em blog) com:Isso força o uso de pnpm e define o diretório raiz.{
"buildCommand": "pnpm run build",
"installCommand": "pnpm install",
"framework": "nextjs",
"rootDirectory": "apps/blog"
}
- A Vercel suporta pnpm nativamente em projetos recentes. Se houver problemas, adicione um arquivo
-
Configurações do Next.js para Produção:
- Verifique next.config.ts: Certifique-se de que está configurado para Nextra (ex.: com
withNextra). - Static Export (opcional): Se quiser exportar estático, adicione
"output": "export"nonext.config.ts, mas para blogs dinâmicos com Nextra, deixe como SSR/ISR padrão. - Domain: Configure um domínio customizado se desejar (ex.: blog.seudominio.com), ou use o domínio gratuito da Vercel (ex.: qb-monorepo.vercel.app).
- Verifique next.config.ts: Certifique-se de que está configurado para Nextra (ex.: com
-
Deploy Inicial e Testes:
- Clique em "Deploy" na Vercel. O build será executado automaticamente.
- Monitore os logs de build para erros (ex.: problemas com pnpm, dependências ou arquivos em blog).
- Após deploy, teste a URL gerada: Acesse
/(deve mostrarpage.mdx),/posts(deve listar os posts de teste), e posts individuais (ex.:/posts/teste-post-1). - Se houver erros (ex.: relacionados a Nextra ou monorepo), ajuste as configurações e re-deploy.
-
Manutenção e Atualizações:
- Deploys futuros serão automáticos em pushes para a branch
master(ou configure branches específicas). - Para adicionar posts: Crie novas pastas em posts com
page.mdx, commit e push – a Vercel re-buildará automaticamente. - Monitoramento: Use o dashboard da Vercel para ver analytics, logs e performance.
- Deploys futuros serão automáticos em pushes para a branch
Notas Importantes:
- Custos: Plano gratuito da Vercel cobre deploys básicos; upgrade se precisar de mais builds/domínios.
- Dependências: Certifique-se de que package.json tem todas as deps (nextra, nextra-theme-blog, etc.) listadas corretamente.
- Problemas Comuns: Se o build falhar devido ao monorepo, o
vercel.jsonna raiz resolve a maioria dos casos. Teste localmente compnpm buildem blog antes de deployar. - Segurança: Não commite secrets; use env vars na Vercel para qualquer chave sensível.
Esse plano cobre o essencial. Se precisar de detalhes em algum passo ou ajustes baseados em erros específicos, posso refinar!