Voltar ao blog

Como organizei a arquitetura deste portfólio com Next.js

Rotas, componentes, traduções e conteúdo: um percurso pelo código deste portfólio e pela inclusão do blog com MDX.

Guilherme Caramori ·

Como as pastas se dividem

Este portfólio usa Next.js com App Router e TypeScript. Para acompanhar a implementação de uma tela, começo pelo arquivo da rota. Ele aponta para os componentes que montam a interface, e os imports desses componentes levam aos textos e às funções que a página usa.

As URLs e os metadados ficam em app/. A interface fica em components/, enquanto lib/ reúne conteúdo e funções compartilhadas. Imagens, documentos e modelos 3D estão em public/. Para os artigos, há uma pasta própria, content/blog/, com arquivos MDX separados por idioma.

O arquivo de rota

As páginas usam o nome page.tsx. A página sobre mim está em app/about/page.tsx, e a listagem de projetos, em app/projects/page.tsx. Já app/projects/[slug]/page.tsx recebe o identificador do projeto no segmento entre colchetes. Dá para encontrar essas entradas seguindo a própria URL nas pastas.

Na home em português, o arquivo da rota importa Index e define os metadados com getHomeMetadata('pt-BR'). Também renderiza LatestPost, com o idioma passado explicitamente, para exibir o artigo mais recente. A entrada em inglês, app/en/page.tsx, usa os mesmos componentes.

O layout raiz cuida do que essas páginas compartilham. Nele estão a configuração das fontes Manrope e Fraunces, o CSS global, a barra superior e o provedor do contexto de idioma. O conteúdo da rota ocupa um elemento main dentro desse layout. Ao abrir outra página, a navegação e o fundo continuam seguindo essa estrutura.

Onde procurar um componente

Em components/, agrupei os arquivos pelas áreas do site: topbar, index, about, projects, resume e blog. A pasta de projetos tem componentes para os detalhes de cada trabalho, sua apresentação visual e a área de contato.

O botão de copiar e-mail e os recursos de animação de leitura ficam diretamente na pasta de componentes, pois são compartilhados. A divisão varia conforme o uso: algumas seções concentram bastante apresentação, enquanto uma interação reutilizada tem seu próprio arquivo. Quando preciso entender uma tela, sigo os imports a partir da rota até chegar ao trecho responsável por ela.

Os textos das páginas e dos artigos

Os textos das páginas institucionais estão em lib/languages.ts, em um registro tipado por idioma. Ali ficam os rótulos da navegação, o conteúdo da home, a apresentação profissional e as informações dos projetos. lib/projects.ts e lib/contact.ts fornecem outros dados e funções usados na interface.

Na home, IndexContent consulta o contexto de idioma e acessa siteContent[language].home. Com isso, o JSX usa o conjunto de textos correspondente à língua selecionada.

Os artigos têm outro formato. O corpo fica em MDX, e um catálogo separado guarda título, descrição, data e tags. O componente de apresentação usa esses dados para montar o cabeçalho. As duas traduções compartilham esse componente; cada arquivo MDX contém apenas o texto do artigo.

A troca entre português e inglês

As páginas em português usam URLs sem prefixo. As versões em inglês começam com /en. Em lib/locale.ts, as funções identificam e removem o prefixo antes de montar o caminho no idioma escolhido. A construção do caminho em inglês usa este trecho:

if (locale === 'en') {
  return basePath === '/' ? localePrefix : `${localePrefix}${basePath}`
}

O seletor lê a URL com usePathname() e navega com router.push(). As duas versões deste artigo usam o slug arquitetura-do-portfolio. Por isso, trocar o idioma mantém o artigo aberto e altera apenas o prefixo. Ao publicar um texto, é preciso manter o mesmo slug nas duas traduções para preservar esse comportamento.

Há também a escolha do idioma na primeira resposta do servidor. O arquivo proxy.ts coloca o caminho da requisição no cabeçalho x-pathname. O layout lê esse valor com headers() e define o atributo lang do HTML. Esse uso de informação da requisição permanece mesmo com os slugs do blog declarados no build; a árvore inteira não é forçada a ser estática.

Servidor e interações no navegador

O layout e as páginas do blog são Server Components. O catálogo fica no servidor e importa os arquivos MDX explicitamente. O deploy leva os artigos junto com o código, sem consultar um CMS ou percorrer diretórios em produção para encontrar o conteúdo.

A barra superior e o seletor de idioma são componentes de cliente: usam contexto, navegação e interações. A home também tem uma camada de cliente. No blog, o provedor de cliente do layout envolve o conteúdo renderizado no servidor, que continua sendo um Server Component.

A área de leitura tem estilos próprios para títulos, listas, links e imagens. Os blocos de código usam fonte monoespaçada e fundo contrastante, sem realce de sintaxe nesta versão. Eles aceitam foco por teclado e rolagem horizontal para que linhas longas caibam na tela.

Publicar um artigo

A configuração do Next.js usa @next/mdx. O arquivo mdx-components.tsx define as adaptações de renderização compartilhadas, e lib/blog.ts associa cada tradução ao componente compilado do artigo. A listagem filtra os rascunhos e ordena as publicações pela data:

return posts.filter((post) => post.locale === locale && !post.draft)
  .sort((a, b) => b.date.localeCompare(a.date))

As páginas de detalhe usam essa mesma seleção. Um slug inexistente ou em rascunho retorna 404. generateStaticParams declara os identificadores publicados, e a home usa o primeiro artigo da lista como destaque.

Cada artigo tem canonical, alternativas de idioma, autor e metadados Open Graph do tipo artigo. Uma rota gera a imagem de compartilhamento com título e autor. O sitemap inclui as listagens e os textos publicados com suas datas editoriais.

Para publicar, adiciono as duas traduções e suas entradas no catálogo. Depois da revisão, o texto segue com o repositório pelo fluxo de Git e deploy.