Configurar um blog com MDX pode parecer complicado no início, mas com as ferramentas certas, o processo se torna bem mais simples. Este guia vai direto ao ponto, mostrando como criar um blog funcional usando Next.js e MDX, aproveitando ao máximo o server-side rendering (SSR). O objetivo é apresentar um método eficiente para quem deseja começar a blogar sem complicações, desde a instalação até a publicação do conteúdo.
Instalação e Configuração Inicial
Vamos começar com a instalação básica do Next.js, seguindo as instruções fornecidas pela própria documentação. Em seguida, faremos alguns ajustes para otimizar o processo. Para criar um novo projeto Next.js, abra o terminal e navegue até a pasta onde você deseja criar o projeto. Execute o seguinte comando:
npx create-next-app@latest
Durante a configuração, você pode escolher as opções padrão para facilitar o acompanhamento deste guia. Certifique-se de selecionar Typescript, Tailwind CSS e o App Router do Next.js. O App Router é a API mais recente e será fundamental para utilizarmos o server-side rendering de forma eficaz. Após a criação do projeto, inicie o servidor de desenvolvimento e acesse http://localhost:3000 para verificar se tudo está funcionando corretamente.
npm run dev
Se preferir, você pode usar o pnpm em vez do npm, adicionando --use-pnpm
ao final do comando. Embora o pnpm seja uma ótima opção, manteremos o npm para simplificar as coisas neste guia.
Configurando o Prettier (Opcional)
A configuração do Prettier é opcional, mas altamente recomendada para manter a consistência do código. O Prettier formata o código automaticamente, e o plugin do Tailwind CSS organiza as classes HTML de forma consistente. Para adicionar o Prettier, instale as dependências de desenvolvimento necessárias:
npm install --save-dev prettier prettier-plugin-tailwindcss eslint-plugin-prettier
Crie um arquivo chamado .prettierrc.json
na raiz do projeto e adicione sua configuração preferida. Aqui está um exemplo de configuração:
{
"plugins": ["prettier-plugin-tailwindcss"],
"singleQuote": true,
"jsxSingleQuote": true,
"semi": false
}
Adicione um script ao seu package.json
para formatar todo o projeto de uma vez:
{
"scripts": {
"format": "prettier --write ."
}
}
Execute o script para formatar o projeto:
npm run format
Para configurar o ESLint para trabalhar com o Prettier, atualize o arquivo eslint.config.mjs
para incluir as regras do Prettier:
const eslintConfig = [
...compat.extends(
'next/core-web-vitals',
'next/typescript',
'plugin:prettier/recommended', // <---
),
]
Execute o ESLint para garantir que tudo está funcionando:
npm run lint
Integrando o Blog com MDX
Agora que o projeto está configurado, vamos instalar o MDX. A documentação do Next.js oferece excelentes detalhes para começar. Seguiremos essas instruções, importando os arquivos .mdx
diretamente para os componentes.
Se você estiver carregando conteúdo markdown de uma fonte externa, considere usar o next-mdx-remote-client
. Nesse caso, não será necessário configurar o Next.js para importar arquivos .mdx
diretamente.
Instalação das Dependências do MDX
npm install @next/mdx @mdx-js/loader @mdx-js/react @types/mdx
Configuração do Next.json para MDX
Atualize o arquivo next.config.ts
para permitir a importação de arquivos markdown nos seus componentes JSX:
import { NextConfig } from 'next'
import createMDX from '@next/mdx'
const nextConfig: NextConfig = {
// Configure `pageExtensions` to include markdown and MDX files
pageExtensions: ['js', 'jsx', 'mdx', 'ts', 'tsx'],
}
const withMDX = createMDX({
extension: /\.mdx?$/,
})
// Merge MDX config with Next.js config
export default withMDX(nextConfig)
Configuração dos Componentes Globais do MDX
Crie um arquivo chamado mdx-components.tsx
na raiz do projeto. Este arquivo permite injetar componentes globais que podem ser usados em qualquer arquivo MDX:
import type { MDXComponents } from 'mdx/types'
export function useMDXComponents(components: MDXComponents): MDXComponents {
return { ...components }
}
Adicionando Conteúdo em Markdown
Estamos quase lá. Agora, vamos usar dynamic imports para obter o markdown do sistema de arquivos, de forma similar à documentação do Next.js. Primeiro, adicione alguns arquivos markdown ao projeto.
Criando Arquivos MDX para as Publicações
Crie uma pasta chamada blogs
dentro de src
e adicione arquivos MDX com a extensão .mdx
. Nomeie os arquivos como preferir, mas lembre-se que o nome do arquivo será usado como o slug da URL. Recomenda-se usar a convenção lower-kabob-case.
Aqui estão dois exemplos: um para ter uma ideia rápida de como a página será estilizada com vários elementos markdown, e outro simples para demonstrar várias páginas.
// src/blogs/markdown-test.mdx
export const metadata = {
title: 'A test post',
description: 'A post to test various markdown formatting',
date: '2025-03-17T12:00:00Z',
tags: ['Next.js', 'MDX', 'Typescript'],
}
## Heading 2
### Heading 3
#### Heading 4
A paragraph with some _emphasized_, **bolded**, and <s>strikethrough</s> text.
Unordered lists
- first
- second
- third
Ordered lists
1. first
1. second
2. third
> A quote!
[link to google](https://google.com)
// src/blogs/another-post.mdx
export const metadata = {
title: 'Another test post',
description: 'Demonstrating multiple posts',
date: '2025-03-18T12:00:00Z',
tags: ['writing'],
}
## Introduction
Hello!
Como você pode ver, também exportamos um objeto chamado metadata
do arquivo. Poderemos usar isso, ou qualquer dado exportado dos arquivos MDX, no nosso código React.
Roteamento para as Páginas do seu Blog com MDX
Agora, vamos criar uma página no App Router para visualizar o conteúdo MDX. Dentro da pasta src/app
, crie uma nova pasta chamada blogs
e, dentro dela, uma pasta chamada [slug]
. Lá, criaremos o arquivo page.tsx
.
Aqui está um componente de página básico. Incluí uma seção de título que usa os metadados exportados e o próprio conteúdo markdown.
// src/app/blogs/[slug]/page.tsx
type BlogPageProps = {
params: Promise<{ slug: string }>
}
type BlogPostMetadata = {
title: string
description: string
date: string
tags: string[]
}
export default async function BlogPage({ params }: BlogPageProps) {
const { slug } = await params
const post = await import(`@/blogs/${slug}.mdx`)
// Get the react component from processing the MDX file
const MDXContent = post.default
// Process exported metadata to construct the title area of our blog post
const metadata: BlogPostMetadata = post.metadata
const title = metadata.title
const date = new Date(metadata.date)
const tags = metadata.tags
const formattedDate = new Intl.DateTimeFormat('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
}).format(date)
return (
<div className='flex flex-col items-center gap-6 py-6'>
{/* some wrappers for styling and additional content*/}
<div className='mx-auto w-full max-w-[768px]'>
<article className='w-full p-6'>
{/* A title section using the markdown metadata */}
<div className='mt-6 mb-8'>
<h1 className='mb-2 text-4xl font-bold'>{title}</h1>
<div className='flex items-center gap-2 py-2'>
<span className='text-sm'>{formattedDate}</span>|
<div className='flex gap-1'>
{tags.map((tag) => (
<span
key={tag}
className='border-foreground rounded-full border px-2 py-1 text-xs'
>
{tag}
</span>
))}
</div>
</div>
</div>
{/* The markdown content */}
<MDXContent />
</article>
</div>
</div>
)
}
export function generateStaticParams() {
// A list of params, which we will update shortly to use the file system.
return [{ slug: 'markdown-test' }, { slug: 'another-post' }]
}
// By marking as false, accessing a route not defined in generateStaticParams will 404.
export const dynamicParams = false
A estrutura do seu projeto deve ser semelhante a esta:
project-root/
├── src/
│ ├── app/
│ │ ├── blogs/
│ │ │ └── [slug]/
│ │ │ └── page.tsx
│ │ ├── globals.css
│ │ ├── layout.tsx
│ │ └── page.tsx
│ └── blogs/
│ ├── markdown-test.mdx
│ └── another-post.mdx
├── mdx-components.tsx
├── ...
Testando a Configuração do Blog com MDX
Acesse http://localhost:3000/blogs/markdown-test no seu navegador para ver o resultado. Você deverá ver algo parecido com a imagem fornecida.
Estilizando o Conteúdo com Tailwind CSS
O CSS base do Tailwind não formata os elementos gerados pelo markdown. Para resolver isso, usaremos o plugin @tailwindcss/typography
.
Instalando o Plugin Tailwind Typography
Adicione a dependência de desenvolvimento necessária:
npm install --save-dev @tailwindcss/typography
Importe os novos estilos no topo do arquivo globals.css
:
@import 'tailwindcss';
@plugin "@tailwindcss/typography";
Aplicando a Formatação ao Conteúdo do Artigo
O plugin Typography fornece estilos para tudo dentro de uma tag com a classe prose
. Adicione essa classe ao seu <article>
:
<article className='prose w-full p-6'>
Ajustando o Tema do Typography
O plugin oferece várias variáveis para personalizar o tema. Para este guia, adicionaremos algumas substituições simples. Sinta-se à vontade para ajustar esses valores e incorporá-los ao seu tema!
:root {
/* ...
* add an accent color for light-mode */
--primary: var(--color-blue-800);
}
@theme inline {
/* ...
* register additional colors to use with Tailwind classes, e.g. `bg-primary` */
--color-primary: var(--primary);
}
@media (prefers-color-scheme: dark) {
:root {
/* ...
* add an accent color for dark-mode */
--primary: var(--color-blue-300);
}
}
/* Overrides for Tailwind Typography */
article.prose {
--tw-prose-body: var(--foreground);
--tw-prose-headings: var(--primary);
--tw-prose-lead: var(--foreground);
--tw-prose-links: var(--primary);
--tw-prose-bold: var(--foreground);
--tw-prose-counters: var(--primary);
--tw-prose-bullets: var(--primary);
--tw-prose-hr: var(--foreground);
--tw-prose-quotes: var(--foreground);
--tw-prose-quote-borders: var(--foreground);
--tw-prose-captions: var(--foreground);
--tw-prose-kbd: var(--foreground);
--tw-prose-code: var(--foreground);
--tw-prose-pre-code: var(--foreground);
--tw-prose-th-borders: var(--foreground);
--tw-prose-td-borders: var(--foreground);
}
Agora você deverá ver um resultado mais estilizado, conforme a imagem fornecida.
Gerenciando Arquivos e Metadados no seu Blog com MDX
Vamos adicionar algumas funções utilitárias em um módulo dentro de src/lib
. Isso nos ajudará a buscar as publicações do blog e seus metadados.
Obtendo uma Publicação Única com Metadados
Mova o tipo BlogPostMetadata
para o novo arquivo e c