Seja bem-vindo ao nosso primeiro tutorial! Neste artigo, mostrarei como desenvolver um blog pessoal utilizando as tecnologias Next.js e Prismic CMS.
Antes de prosseguirmos, farei uma breve descrição dos conceitos que abordaremos. É importante que você leia com atenção os tópicos teóricos para entender melhor os termos que utilizaremos ao longo do desenvolvimento.
O projeto que desenvolveremos será inicialmente simples, mas o enriqueceremos com funcionalidades em futuras postagens. Para acompanhar a evolução do projeto, você pode seguir o repositório no GitHub. Sempre que uma nova postagem for concluída, criarei uma tag específica para ajudá-lo a não se perder.
Para acessar o repositório com todo o código desenvolvido nesta postagem, clique neste link. Se deseja ver um exemplo de um blog completo, similar ao que construiremos, visite este repositório.
Vamos começar?
O que é um CMS?
No projeto do blog que estamos desenvolvendo, uma tecnologia crucial é o CMS (Sistema de Gerenciamento de Conteúdo). Um CMS facilita a criação, edição e gestão de conteúdo digital em um website, sem exigir conhecimento técnico avançado.
Essencialmente, um CMS fornece uma interface intuitiva onde usuários podem gerenciar posts, páginas e arquivos de mídia, como imagens e vídeos. Essa ferramenta simplifica a publicação de conteúdos e, muitas vezes, suporta colaboração em equipe por meio de diferentes níveis de acesso e permissões. Isso é particularmente útil para projetos com múltiplos colaboradores.
Por exemplo, no blog que estamos criando, utilizo um CMS que me permite escrever e editar artigos diretamente pela plataforma. Com isso, elimino a necessidade de lidar com HTML estático. A estrutura do blog é definida uma única vez, e todas as postagens futuras são automaticamente adaptadas a essa estrutura, economizando tempo e mantendo a consistência visual do conteúdo.
Vantagens e por que usar o Prismic CMS
A seleção de um Sistema de Gerenciamento de Conteúdo (CMS) deve ser baseada na finalidade específica do seu conteúdo. Enquanto muitos CMSs são versáteis e funcionam bem em várias situações, alguns conteúdos se beneficiam mais de sistemas especializados para tipos específicos de conteúdo.
Veja alguns exemplos de CMSs populares e suas especialidades:
- WordPress: Perfeito para blogs e sites variados.
- Joomla: Excelente para sites corporativos e portais.
- Drupal: Ideal para sites complexos e de grande escala.
- Magento: Especializado em e-commerce de grande volume.
Para nosso projeto, optamos pelo Prismic CMS, destacado pela sua eficácia na criação de artigos e seções de páginas web. Escolhi o Prismic por várias razões importantes:
- Interface Amigável e Custos Acessíveis: Prismic tem uma interface intuitiva e uma política de preços atraente, com um plano gratuito robusto para desenvolvedores e sites pessoais.
- Alta Cota de Uso Gratuito: O plano gratuito é suficiente para a maioria dos blogs pessoais de tecnologia, permitindo uma operação com custo quase nulo.
- Excelente Integração com Next.js: A integração facilita tanto a renderização no servidor (SSR) quanto a geração estática de sites (SSG), melhorando o carregamento das páginas e o SEO.
- Documentação Acessível e Suporte Comunitário: A documentação de Prismic é clara e o fórum da comunidade é um excelente recurso para solucionar dúvidas e problemas técnicos.
A decisão de usar o Prismic equilibra facilidade de uso, eficácia e custo, tornando-o ideal para nosso blog focado em tecnologia.
Criando uma conta e repositório básico no Prismic CMS
Agora que você entendeu os conceitos por trás de um CMS, vamos por a mão na massa. A partir de agora, vamos interagir com o Prismic.
Primeiro passo, precisamos criar uma conta e configurar um repositório no Prismic.
Acesse o site do Prismic e crie uma conta utilizando um email e senha, ou sua conta no Github se preferir.
Aqui estou simulando a criação de uma nova conta com um email temporário. Coloque algumas informações básicas sobre você e clique em “Continue”
Na próxima tela, veremos uma dashboard vazia com algumas opções para criação de um repositório. Para prosseguir selecione o framework Next.js.
Como vamos criar algo do zero, recomendo selecionar a criação de um projeto vazio para facilitar nosso exemplo e evitar recursos desnecessário.
Escolha um nome único para o seu repositório. Se desejar, você pode também adicionar um nome de visualização, que facilitará a identificação do seu repositório entre outros que você possa criar no futuro. Certifique-se de selecionar o plano gratuito e, em seguida, finalize criando o seu repositório.
Após isso, você verá uma tela como essa, significando que ocorreu tudo bem.
Criando o projeto Next
Tendo um repositório básico no Prismic, agora precisamos ter nosso projeto Next para continuar. Se você olhar na página que se abriu no Prismic, ele segue um passo a passo para configurarmos um projeto Next e linka-lo com o CMS.
Tendo o Node instalado, rode o seguinte comando em um terminal para criar seu projeto next:
ATENÇÃO: não se esqueça de substituir “NOME_DO_SEU_PROJETO” pelo nome que você preferir, de preferência, utilize o mesmo nome que você deu ao seu repositório e em caso de duvida, copie o comando da própria página do Prismic.
npx create-next-app@latest NOME_DO_SEU_PROJETO
As seguintes informações vão aparecer e recomendo que você selecione as opções que marquei para criar um projeto Next utilizando Typescript com paths habilitado, Eslint, Tailwind e a nova App routes do Next.
Terminando de criar seu projeto, rode os comando abaixo para entrar na pasta criada e iniciar o servidor local do Next.
cd NOME_DO_SEU_PROJETO
npm run dev
Com isso, acesse http://localhost:3000 e se tudo estiver correto, você verá uma página parecida com essa:
Agora, precisamos inicializar as configurações do Prismic no projeto. Rode o seguinte comando para tal:
ATENÇÃO: Troque “NOME_DO_REPOSITÓRIO_CRIADO_NO_PRISMIC” pelo nome dado ao seu repositório no Prismic. Esse campo deve ser igual ao que foi colocado no Prismic.
npx @slicemachine/init@latest --repository NOME_DO_REPOSITÓRIO_CRIADO_NO_PRISMIC
Após a execução dos comando acima, a CLI vai pedir para você se logar com sua conta do Prismic. Siga os passos que indicarem no terminal e aguarde o Prismic instalar e configurar o básico do Slice Machine (não se preocupe, iremos entender melhor o que é esse recurso mais pra frente). Ao final, a CLI vai perguntar se deseja rodar o Slice Machine, diga que sim e uma aplicação começara a rodar em http://localhost:9999.
Ao acessar a URL acima, uma página do Slice Machine será aberta. De inicio, precisamos criar um modelo de Page Type reutilizável dando um nome para ele e ao final, realizamos o envio de nossa criação para a nuvem do Prismic.
Você pode acompanhar essa criação abaixo.
Voltando ao site do Prismic, podemos avançar até o passo 4 na tela e confirmar que criamos um modelo clicando no botão “I’ve pushed my models”
Se a página do Prismic se parecer com a imagem abaixo, nós temos tudo que é necessário para iniciar nosso blog.
O que é o Slice Machine do Prismic?
Antes de avançarmos, vamos explorar um recurso fundamental que usaremos em breve: o Slice Machine. Essa ferramenta do Prismic revoluciona a maneira como desenvolvedores e designers criam e gerenciam componentes de interface para projetos web. Com o Slice Machine, você pode construir e organizar seções de página, conhecidas como "slices", de forma modular e reutilizável.
Utilizando o Slice Machine, definimos a estrutura física de nossas postagens e, como bônus, incorporamos a tipagem estática, um recurso que considero essencial para qualquer projeto.
O processo é simples, mas poderoso, e envolve alguns passos essenciais:
- Criação de Slices: Primeiro, utilizamos o Slice Machine para projetar a estrutura de conteúdo que desejamos para nosso blog — neste caso, a estrutura de uma postagem. Isso é feito através de uma interface gráfica onde você pode arrastar e soltar diferentes tipos de campos e componentes, como textos, imagens e vídeos, configurando-os para atender às necessidades específicas do seu conteúdo.
- Geração de Tipagem Estática: Após definir os componentes de nossa postagem, o próximo passo é gerar a tipagem estática dessa estrutura. Isso é feito automaticamente pelo Slice Machine, que cria definições de tipos Typescript. Essas definições são cruciais, pois integram-se ao Next.js para garantir que o desenvolvimento do seu site seja seguro em termos de tipos, reduzindo a possibilidade de erros e facilitando a manutenção do código.
- Sincronização com a Nuvem do Prismic: Uma vez que a estrutura do conteúdo e a tipagem estão prontas, sincronizamos tudo com a nuvem do Prismic. Este passo envolve enviar a configuração dos slices criados para o seu repositório Prismic, onde você pode começar a criar e gerenciar postagens usando a estrutura previamente definida. Este processo é facilitado pelo Prismic, que hospeda e gerencia os dados, permitindo que você se concentre apenas no conteúdo.
Se isso parece complexo, não se preocupe! Vamos descomplicar esses conceitos com exemplos práticos, mostrando cada passo em detalhes para que você possa seguir facilmente e aplicar no seu projeto.
Criando nosso primeiro modelo
Agora que está mais claro o que é o Slice Machine, vamos criar nossas primeiras estruturas.
Se já não estiver rodando, em um terminal na pasta do app Next, execute o Slice Machine com o seguinte comando:
npm run slicemachine
Assim que o emulador iniciar, acesse http://localhost:9999, navegue pelo menu lateral até a página “Page Types” e depois acesse o modelo que criamos durante a configuração inicial do Prismic.
Acessando esse modelo nós veremos 3 partes importantes que eu destaquei com cores diferentes.
- Em vermelho: São as camadas (layer ou tab) do seu modelo. Com as camadas, podemos segmentar os documentos que iremos criar em grupos de dados mais específicos. Por exemplo, por padrão, temos duas camadas, Main (Onde vamos trabalhar e criar toda a estrutura visual da nossa postagem) e SEO & Metadata (onde vão os dados de SEO para a postagem). Você pode Criar várias camadas dependendo da complexidade do seu modelo e caso queira segmentar em várias camadas de informação. Para o nosso exemplo, só vamos precisar da primeira página.
- Em azul: É aqui que vamos colocar os campos estáticos. Esses campos são fixos e pertencem exclusivamente a esse modelo. Todo documento desde modelo vai precisar ter os campos que registrarmos aqui.
- Em roxo: Aqui vai ser onde colocaremos o que o Prismic chama de “Slice”. Basicamente esse campo serve para compartilhar estruturas dinâmicas entre os modelos. Para o nosso caso, não será necessário criar Slices, já que teremos estruturas bem simples, porém, entenda que esse recurso é muito importante se você quiser criar estruturas parecidas entre os seus modelos. Trazendo para uma analogia do mundo do React, seria o equivalente a criar componentes reutilizáveis.
Vamos focar na área azul. O Prismic gera o primeiro campo para você chamado “UID”, esse campo vai representar um id único para nossa postagem, ele não pode ser excluído, porém pode ser renomeado se você desejar.
Iremos criar uma estrutura de artigo com campos simples. Nossa estrutura vai precisar de:
- Título
- Subtítulo
- Uma imagem central
- Autor
- Conteúdo Título Parágrafos de conteúdo
O Prismic tem muitos tipos para um campo, Para o nosso exemplo iremos utilizar os tipos:
- Rich Text: esse tipo representa um campo de texto versátil no formato de parágrafo. Através dele podemos renderizar algumas tags do HTML como por exemplo <H1-6>, <strong>, <p>, <image> e entre outros. Por ele ser versátil, podemos utilizar todas as tags juntas em um mesmo campo, parecendo como se estivéssemos criando elementos HTML aninhados.
- Image: como o próprio nome diz, renderiza uma Imagem no campo, podendo ser interna ao storage do Prismic ou externa com o link.
- Key Text: esse campo é uma string exclusivamente e serve como um campo único dentro do seu modelo.
- Group: um grupo serve para criamos uma área de conteúdo repetível no modelo. Trazendo para o mundo da programação, podemos entender um grupo como um array de objetos onde cada objeto tem a mesma estrutura interna (tipagem).
Qualquer outro campo não será tratado neste artigo, mas caso tenha curiosidade em entender como os campos do Prismic funcionam, pode acessar a documentação.
Para criar a estrutura acima vamos clicar no botão “Add a new field” para abrir um modal para podermos escolher o tipo do campo. Selecionando o tipo, de um nome legível e clique em “Add”.
O título e subtítulo serão do tipo “Rich Text”, a imagem será do tipo “Image” e o Autor será “Key Text”.
Agora que temos os campos, vamos personaliza-los. Título e subtítulo vão precisar respeitar uma estrutura semântica igual no HTML, então queremos definir que tenhamos um H1 e um H2 respectivamente. Para isso, clicamos no lápis para editar e modificamos o campo “Accept” para permitir somente as tags desejadas.
Por último, iremos criar o conteúdo. O conteúdo será do tipo “Group”, esse grupo vai conter um título do de tipo um "Rich Text" que permite somente a tag H3 e os parágrafos também serão "Rich Text", porém vão permitir mais tipos de tag HTML, pois cada parágrafo vai poder ter uma estrutura mais flexível dependendo do que quisermos escrever na postagem. Por exemplo, dentro do conteúdo de nossos artigos, teremos listas, imagens, texto, código, links e etc.
Ao final, vamos excluir a camada “SEO & Metadata” já que ela não será útil para nosso exemplo.
Agora que temos uma estrutura, precisamos enviar nossas alterações para o a nuvem do Prismic.
Acesse no menu lateral esquerdo o botão "Review changes”. Se for necessário realizar login, realize clicando no botão que surgir na tela. Após isso, a tela exibira um resumo do que foi atualizado, basta então clicar no botão “Push” e pronto, seus modelos estão sincronizados.
Sempre que você fizer alteração nos modelos, é necessário realizar esse procedimento de envio para a nuvem.
Uma informação importante é que se você olhar a pasta do seu projeto, vai notar que dois arquivos foram gerados pelo Prismic.
Não aconselho que você edite esses arquivos por conta própria, eles servem basicamente para adicionar tipagem estática nos recursos da SDK do Prismic quando formos utiliza-la no Next, então, por enquanto, pode simplesmente envia-los em seu repositório.
Criando nossa primeira postagem
Agora que temos nosso modelo, vamos criar nossa primeira postagem. Primeiro, acessamos nosso repositório nod painel do Prismic e vamos clicar no botão central para iniciar uma postagem.
Ao Iniciar uma postagem vamos ser apresentados ao editor do Prismic, nele, toda a estrutura que criamos no modelo está disponível de forma de um formulário onde podemos simplesmente preencher. Nesta hora, podemos entender melhor como funciona os campos que construímos.
Irei preencher rapidamente com informações básicas e loren ipson. Os pontos que mais importam nesse momento são:
- O campo UID pode ser preenchido manualmente, porém sempre que você digitar o título, ele vai usar o conteúdo para gerar um “slug”;
- É possível adicionar tags a sua postagem no topo da tela;
- O banner pode usar uma imagem local sua ou pode ser pesquisada no banco de imagens do Prismic para ser preenchido automaticamente.
- O campo de conteúdo é repetível, então podemos adicionar vários tipos de conteúdo nele;
Neste momento recomendo que você teste bem cada campo e veja as várias possibilidades para a criação de sua postagem.
Terminando de criar o conteúdo, basta clicar em “Save” no canto superior direito e então clicar em “Publish”. A partir dai, sua postagem já está no ar e podemos começar a codificar a página no Next.
Criando a página de listagem de postagens
É chegada a hora de codificarmos algumas estruturas básicas de um blog. Devo dizer novamente que o exemplo que iremos fazer não vai ter lá o design mais bonito e funcional. Irei focar em como funciona a SDK do Prismic, busca na api e como ela se integra com a construção dos server componentes e componentes assíncronos do Next.
Para casos mais específicos, design e certos componentes do blog, irei criar postagens focadas neles.
Vamos abrir o projeto no VS Code e apagar alguns detalhes que o Next traz por padrão que não nos servirão. No arquivo src/app/globals.css podemos apagar a maioria das estilizações deixando o arquivo somente com as importações do Tailwind.
/* src/app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
No arquivo src/app/page.tsx, podemos remover todo o código e deixar um componente em branco.
// src/app/page.tsx
export default function Component() {
return <h1>Hello Word</h1>;
}
Com isso, teremos uma aplicação limpa igual a imagem abaixo:
Para facilitar o uso de classes Tailwind, vamos instalar uma biblioteca chamada tailwind-merge que vai nos ajudar a mesclar classes CSS sem a necessidade de ficarmos juntando strings e correndo o risco de ter classes duplicadas ou que se anulem. Para isso, rode o comando abaixo no terminal do seu projeto.
npm install tailwind-merge
Agora vamos criar um layout mínimo para a listagem de posts. No arquivo src/app/layout.tsx
iremos adicionar algumas classes ao body para tornar o conteúdo mais simples e responsivo, além de permitir um tema claro e escuro. Também iremos modificar o arquivo de metadata para mudar o título e descrição da página.
// src/app/layout.tsx
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.scss";
import { twMerge } from "tailwind-merge";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Meu Blog",
description: "Blog onde vou escrever sobre tutoriais e dicas de programação.",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body
className={twMerge(
inter.className,
"max-w-3xl mx-auto lg:max-w-5xl dark:bg-gray-900 min-h-screen sm:p-10 p-5 text-slate-900 dark:text-white"
)}>
{children}
</body>
</html>
);
}
Dentro do arquivo src/app/page.tsx iremos adicionar alguns detalhes no header e com isso já vamos conseguir visualizar um cabeçalho inicial do nosso blog.
// src/app/page.tsx
export default function Component() {
return (
<div className="grid gap-6 lg:gap-8">
<header className="space-y-2">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl text-slate-900 dark:text-white">
Meu Blog
</h2>
<p className="text-gray-500 dark:text-gray-400">
Blog onde vou escrever sobre tutoriais e dicas de programação.
</p>
</header>
</div>
);
}
Ok, agora nós temos um cabeçalho e podemos começar a listar nossas postagens. Para isso, iremos utilizar a SDK do Prismic. No começo da página, podemos importar o createCliente diretamente da pasta src/prismicio.ts. Esse arquivo foi criado pelo próprio Prismic alguns passos atrás quando inicializamos nosso projeto. Nele iremos encontrar dois pontos importantes:
- A constante routes : Iremos deixar ela inaltera por enquanto, mas saiba que esse é o “Route Resolver” do Prismic, uma funcionalidade essencial para mapear as rotas de nossa aplicação com os documentos específicos armazenados no Prismic. Ele facilita a integração entre nossa aplicação Next e o Prismic, permitindo o uso de URLs locais diretamente pelo editor do Prismic. Além disso, contribui significativamente para a otimização do SEO, assegurando que as URLs sejam consistentes e amigáveis aos motores de busca.
- A função createClient: Essa função basicamente cria uma instância do cliente do Prisma e nos retorna para fazermos requisições diretamente para a API. Podemos importar ele em qualquer componente do nosso app, mas vamos focar em importar em em componentes assíncronos. Outro ponto interessante de notar, é que ele possui por padrão uma configuração de cache em produção e uma para desenvolvimento. Qualquer coisa aqui é modificável, porém o padrão que vem já é o suficiente para trazer uma experiência de cache favorável em nossa aplicação, onde em desenvolvimento, teremos uma revalidação do cache a cada 5 minutos e em produção iremos armazenar nossas requisições no cache do Next por tempo indeterminado.
Voltando para a importação, adicione o código abaixo no topo do arquivo src/app/page.tsx:
import { createClient } from "@/prismicio";
Tendo o createClient, iremos fazer nossa primeira busca. Para fazermos uma busca, é necessário transformar a página em um componente assíncrono (uma funcionalidade do Next 14), adicionamos a palavra reservada async a função. Então, dentro da função iremos chamar a função createClient e pegaremos seu retorno.
Com o objeto retornado, podemos utiliza-lo para fazermos requisições ao nosso CMS. O código ficaria assim:
// src/app/page.tsx
import { createClient } from "@/prismicio";
export default async function Component() {
const prismicClient = createClient();
const posts = await prismicClient.getAllByType("blog_post").catch(e => {
console.error(e);
return [];
});
//... resto do componente
}
Se você analisar o código, vai ver que estamos utilizando diretamente do prismicClient a função getAllByType. Essa função serve para pegarmos todos os documentos (no nosso caso chamamos de postagens) do Prismic baseado no tipo que passarmos como parâmetro. Esse nome é o mesmo nome do modelo que criamos no Slice Machine, mas não se preocupe se você não se lembrar, o Prismic já criou a tipagem todos os seus modelos para você.
Após chamarmos a função assíncrona, envolvemos seu retorno com o operador await e guardamos seu resultado na nossa constante posts. Outro ponto interessante para notar é que estamos tratando os erros simplesmente imprimindo o erro no console e retornando um array vazio como fallback.
Podemos agora pegar o resultado e renderizar em tela para vermos o que estamos recebendo. para isso, vamos abrir uma tag <main> e simplesmente envolver o array em um JSON.stringfy() :
// src/app/page.tsx
import { createClient } from "@/prismicio";
export default async function Component() {
const prismicClient = createClient();
const posts = await prismicClient.getAllByType("blog_post").catch(e => {
console.error(e);
return [];
});
return (
<div className="grid gap-6 lg:gap-8">
<header className="space-y-2">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl text-slate-900 dark:text-white">
Meu Blog
</h2>
<p className="text-gray-500 dark:text-gray-400">
Blog onde vou escrever sobre tutoriais e dicas de programação.
</p>
</header>
<main className="flex flex-col divide-y">{JSON.stringify(posts)}</main>
</div>
);
}
Se olharmos nossa página principal no navegador, veremos algo assim:
Óbvio que isso é só um amontoado de dados, mas se estamos vendo isso, significa que tivemos um retorno favorável da API, então nós podemos continuar.
Agora, vamos transformar essa informações em cards simples para termos uma visualização interessante. Crie uma pasta chamada components dentro de src e vamos criar o componente PostItem.tsx .
Esse componente vai receber por propriedades um post e usaremos a informação recebida para exibir alguns dados. O código do componente pode ser visto abaixo:
// src/components/PostItem.tsx
import Link from "next/link";
import { AllDocumentTypes } from "../../prismicio-types";
import { asText } from "@prismicio/client";
import dayjs from "dayjs";
interface PostItemProps {
post: AllDocumentTypes;
}
export function PostItem({ post }: PostItemProps) {
return (
<div className="flex flex-col gap-2 py-4">
<Link className="font-medium transition-opacity hover:opacity-70" href={post.uid}>
<div className="flex flex-col gap-2">
<h3 className="transition-opacity duration-200 dark:text-white text-slate-900 hover:opacity-70">
{asText(post.data.title)}
</h3>
<p className="text-gray-500 font-normal">{asText(post.data.subtitle)}</p>
</div>
</Link>
<p className="dark:text-white text-slate-900 text-sm">
Por{" "}
<Link className="text-gray-900 dark:text-gray-500 hover:underline" href="#">
{post.data.author}
</Link>{" "}
<time dateTime="2023-10-10" className="text-gray-500">
- {dayjs(post.first_publication_date).format("DD/MM/YYYY")}
</time>
</p>
</div>
);
}
Vamos analisa-lo. Primeiro, se você notar, utilizo a biblioteca dayjs para trabalhar com datas no Javascript. Ela é uma biblioteca muito útil, pois facilita operações com datas além de ser extremante leve. Então, vamos instala-la:
npm install dayjs
Outro ponto importante é que eu importei e usei o tipo BlogPostDocument . Se você seguir sua localização, vai ver que ele está no arquivo prismicio-types.d.ts na raiz do projeto. Esse arquivo é gerado pelo Prismic quando enviamos nosso modelo pelo Slice Machine. Nele estarão todas as tipagens de nossos modelos, então podemos importa-las onde precisarmos e teremos toda a tipagem necessária para os nossos documentos.
Também é possível notar o uso de um utilitário do Prismic, o asText. Esse utilitário tem como funcionalidade receber qualquer objeto do tipo RichTextField e traduzir para uma string. Muito util quando queremos simplesmente o valor real do campo sem toda a formatação e tags auto geradas do Prismic. No código, podemos notar que utilizei a função para exibir o título e o subtítulo. Fiz isso para conseguir ter uma maior personalização do campo, já que se eu utiliza-se o dado puro vindo do Prismic, não conseguiria estilizar diretamente a tag ou então teria que utilizar os componentes do Prismic que veremos em momentos posteriores.
Um Link do Next é utilizado no componente também. Nele redirecionamos para a página própria da postagem que criaremos mais pra frente.
De resto, temos só estilização para tornar o componente minimamente apresentável.
Agora, podemos utilizar esse componente na nossa página principal e renderizar nossa listagem de posts. Para isso, iremos remover o JSON.stringfy e colocar no lugar um map que percorre todas as postagens no array e renderiza nosso recém criado PostItem:
// src/app/page.tsx
import { PostItem } from "@/components/PostItem";
import { createClient } from "@/prismicio";
export default async function Component() {
const prismicClient = createClient();
const posts = await prismicClient.getAllByType("blog_post").catch(e => {
console.error(e);
return [];
});
return (
<div className="grid gap-6 lg:gap-8">
<header className="space-y-2">
<h2 className="text-3xl font-bold tracking-tight sm:text-4xl text-slate-900 dark:text-white">
Meu Blog
</h2>
<p className="text-gray-500 dark:text-gray-400">
Blog onde vou escrever sobre tutoriais e dicas de programação.
</p>
</header>
<main className="flex flex-col divide-y">
{posts.map(post => (
<PostItem key={post.id} post={post} />
))}
</main>
</div>
);
}
Com isso, temos uma listagem funcional e básica, onde poderemos ver um resumo de nossas postagens e acessa-las.
Criando a página da postagem
Por último, iremos começar a implementação da página da postagem. Para começar, vamos criar a pasta src/app/[uid] . Essa nomenclatura de pasta é como fazemos para que o Next entenda que teremos uma rota dinâmica, assim, poderíamos acessar http://localhost:3000/testando-uma-rota e o Next nos forneceria uma rota válida e como propriedade do componente desta página uma variável chamada uid com o valor "testando-uma-rota".
Dentro da pasta criada, vamos também criar também um arquivo chamado page.tsx. Ele será parecido com a página principal, porém iremos começar fazendo uma busca direcionada para uma postagem específica ao invés de buscar todas as postagens.
// src/app/[uid]/page.tsx
import { createClient } from "@/prismicio";
import { notFound } from "next/navigation";
interface BlogPostProps {
params: {
uid: string;
};
}
export default async function BlogPost({ params }: BlogPostProps) {
const prismicClient = createClient();
const post = await prismicClient.getByUID("blog_post", params.uid).catch(() => notFound());
return (
<div className="flex flex-col gap-4">
//TODO: Implementar conteúdo
</div>
);
}
De início podemos notar alguns pontos:
- Primeiro, estou declarando minha tipagem dizendo que nossa página vai receber como propriedades um campo chamado params e dentro dele vai haver nosso uid ;
- Segundo, nosso componente deve ser assíncrono para podermos buscar os dados do Prismic;
- Terceiro, estou trazendo o createClient novamente, mas desta vez, estou usando a função getByUID. Essa função, diferente da getAllByType, recebe qual modelo é nosso documento e qual é o UID dele, assim, traremos exatamente a postagem relacionada a essa página.
- Quarto ponto, estamos tratando o erro independente do tipo durante a busca, para redirecionar o usuário á página 404. Isso é útil para garantir que caso o UID passado não exista ou tenha acontecido algum erro durante a busca, o usuário vai ser redirecionado por segurança.
Como na página principal, vamos começar pelo header. No header, vamos renderizar um botão de voltar para o menu anterior, o título, subtítulo, a imagem principal da postagem e dados do autor e data de postagem.
// src/app/[uid]/page.tsx
import { createClient } from "@/prismicio";
import { asText } from "@prismicio/client";
import { PrismicImage } from "@prismicio/react";
import dayjs from "dayjs";
import { CircleArrowLeft } from "lucide-react";
import Link from "next/link";
import { notFound } from "next/navigation";
interface BlogPostProps {
params: {
uid: string;
};
}
export default async function BlogPost({ params }: BlogPostProps) {
const prismicClient = createClient();
const post = await prismicClient.getByUID("blog_post", params.uid).catch(() => notFound());
return (
<div className="flex flex-col gap-4" >
<header className="flex flex-col gap-4">
<Link
href=".."
className="hover:opacity-80 transition-opacity flex gap-2 items-center mb-5">
<CircleArrowLeft /> Voltar
</Link>
<h1 className="text-3xl font-bold">{asText(post.data.title)}</h1>
<h2 className="text-xl text-gray-500">{asText(post.data.subtitle)}</h2>
<PrismicImage field={post.data.banner} />
<section>
{post.data.author} - Criado em{" "}
{dayjs(post.first_publication_date).format("DD/MM/YYYY")}
</section>
</header>
<hr />
</div>
);
}
Uma coisa importante sobre o código acima é que logo no começo do header, nós precisamos utilizar o componente <CircleArrowLeft /> . Esse componente é um ícone da biblioteca Lucid Icons. Ela será nossa biblioteca principal de ícones, já que é bem simplista e se encaixa bem com o Next e seus componentes de servidor. Para instala-la, rode o comando abaixo no terminal:
npm install lucide-react
Novamente, o título e o subtítulo estão usando o utilitário asText que já não é nenhum desconhecido para você, porém, uma novidade aqui, é como exibimos a imagem principal, já que estamos utilizando nosso primeiro componente do Prismic. O Prismic, fornece vários componentes que se integram com os dados gerados pelo próprio, assim, ele vai fornecer um componente para cada tipo de dado que era possível ser utilizado durante a montagem do modelo. O componente da vez é o <PrismicImage /> e sua funcionalidade é exibir imagens (😅). Ele abstrai por detrás dos panos o Image do Next e fornece tudo que é necessário para uma boa renderização de imagem na web.
Após essa codificação, se você acessar a página principal e clicar no nome da sua primeira postagem vai ver uma página parecida com essa:
Porém, essa é só a ponta do iceberg, vamos continuar montando nossa página. Agora podemos adicionar o conteúdo repetível.
Para isso, vamos criar uma tag <main> e percorrer o array de conteúdo do nosso objeto de postagem que está acessível por post.data.content e para cada elemento neste array, iremos renderizar uma <Section /> da nossa postagem.
// src/app/[uid]/page.tsx
import { createClient } from "@/prismicio";
import { asText } from "@prismicio/client";
import { PrismicImage, PrismicRichText } from "@prismicio/react";
import dayjs from "dayjs";
import { CircleArrowLeft } from "lucide-react";
import Link from "next/link";
import { notFound } from "next/navigation";
interface BlogPostProps {
params: {
uid: string;
};
}
export default async function BlogPost({ params }: BlogPostProps) {
const prismicClient = createClient();
const post = await prismicClient.getByUID("blog_post", params.uid).catch(() => notFound());
return (
<div className="flex flex-col gap-4">
<header className="flex flex-col gap-4">
<Link
href=".."
className="hover:opacity-80 transition-opacity flex gap-2 items-center mb-5">
<CircleArrowLeft /> Voltar
</Link>
<h1 className="text-3xl font-bold">{asText(post.data.title)}</h1>
<h2 className="text-xl text-gray-500">{asText(post.data.subtitle)}</h2>
<PrismicImage field={post.data.banner} />
<section>
{post.data.author} - Criado em{" "}
{dayjs(post.first_publication_date).format("DD/MM/YYYY")}
</section>
</header>
<hr />
<main className="flex flex-col gap-4 dark:text-gray-400 text-gray-600 font-light">
{post.data.content.map((item, index) => (
<section key={index}>
<h3 className="text-2xl font-medium text-slate-900 dark:text-white">
{asText(item.title)}
</h3>
<PrismicRichText field={item.body} />
</section>
))}
</main>
</div>
);
}
Em cada seção, renderizei um <h3> contendo o título da seção e novamente veremos mais um componente do Prismic. O <PrismicRichText /> é um elemento que vai renderizar um campo do tipo Rich Text e se até então você não entendeu a utilidade dos componente do Prismic, agora você vai entender. Por ele conseguir renderizar qualquer Rich Text , ele recebe o item.body do nosso conteúdo e vai renderizar todo o HTML necessário, seja ele tags <h1-h6>, <strong>,<i>, <a>, <image>, <pre>, <p> ou seja o quê for que você configurou no seu modelo para renderizar. A ideia aqui, é que nosso código não sabe se você escreveu uma postagem com parágrafos, imagens, links e etc, então, usando esse componente, ele vai conseguir transformar sua escrita em uma tag HTML correspondente para renderização correta.
Com o código acima escrito, nossa página de postagem seria algo parecido com isso:
Estamos quase acabando agora. Para finalizar essa página podemos notar que alguns elementos não estão tão bem estilizados como gostaríamos. O tailwind tem um CSS Reset embutido, então tags como <ol> e <ul> por exemplo, não vão apresentar uma estilização e isso pode ocasionar em um visual estranho. Para resolver isso, temos duas abordagens:
- Utilizar uma propriedade do <PrismicRichText> chamado components que permite que você customize a renderização de elementos específicos do tipo Rich Text. Ao definir um objeto com pares de chave (tipo de elemento) e valor (componente React), você pode determinar como cada tipo de conteúdo (como títulos, parágrafos e imagens) será exibido dentro de um componente <PrismicRichText>. Porém, ao fazer isso, perdemos um pouco da mágica que o <PrismicRichText> nos dá, fazendo com que tenhamos uma possibilidade de estilização e personalização extrema, porém perdemos a renderização de tags automáticas. Há casos em que isso vale muito a pena e eu até utilizo no meu Blog original, porém acredito que para a situação atual, devemos partir por outro caminho. Se você quiser saber como utilizar melhor esse parâmetro, não se preocupe, o próprio código do blog que você está vendo tem exemplos para isso e garanto que nas próximas postagens irei tratar do uso deste campo. Por enquanto, se quiser um detalhe mais técnico, veja a documentação do Prismic.
- Estilização do componente utilizando CSS/SASS: Essa abordagem basicamente remete em utilizarmos um arquivo .css ou .scss para estilizarmos somente dentro da nossa página de postagem e garantir que os elementos da nossa postagem tenham a estilização que desejamos e todo o resto da nossa aplicação continue intacto. Por ser uma abordagem simples e escalável, vamos seguir por ela.
Para iniciar, gosto de utilizar SASS no lugar do CSS por conta de sua versatilidade e por ter uma boa integração com o Tailwind. Para instalar o SASS, rode o seguinte comando no seu terminal:
npm install sass
Após isso, vamos até o arquivo src/app/globals.css e vamos trocar seu nome para globals.scss e colocar como conteúdo o texto abaixo:
/* src/app/globals.css */
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
Também não se esqueça de mudar o nome do arquivo na importação dentro de src/app/layout.tsx .
Agora, vamos até a pasta src/app/[uid] e vamos criar o arquivo styles.scss. Nele, adicionarei algumas estilizações referentes a algumas tags "resetadas" pelo Talwind. Um ponto interessante, é que você vai notar o uso da palavra reservada @apply. Com ela conseguimos estilizar as tags utilizando as próprias classes do Talwind.
/* src/app/[uid]/styles.scss */
#blogPost {
ul,
ol {
@apply mx-8 my-2;
}
ul {
@apply list-disc;
}
ol {
@apply list-decimal;
@apply mx-10;
}
a {
@apply underline;
@apply font-medium;
}
em,
strong {
@apply font-medium;
}
}
Agora dentro de src/app/[uid]/page.tsx, vamos fazer duas coisas:
- Primeiro, adicionar a importação import "./styles.scss"; no topo do arquivo;
- Segundo, na primeira <div> da página vamos adicionar uma propriedade id com o valor: “blogPost”. Fazemos isso para a estilização ser aplicada somente nos filhos deste componente.
Com isso, teremos o suficiente para estilizar várias tags. Sinta-se livre para testar várias estilizações dependendo das suas postagens.
Configurando as Rotas no Prismic com o Route Resolver
Agora que todas as nossas páginas estão prontas, é crucial configurar corretamente as rotas no Prismic. Isso permite que links internos sejam adicionados às postagens diretamente pelo editor do Prismic, garantindo que esses links sejam funcionais e façam os redirecionamentos corretos em nossa aplicação.
Para configurar isso, precisamos editar o arquivo src/prismicio.ts. Nele, encontramos a constante routes, que inicialmente deixamos vazia. Esta constante é um array de objetos que define como as URLs dos documentos são resolvidas dentro do nosso aplicativo.
Aqui está como definimos as rotas para nossa listagem principal e para as rotas dinâmicas das postagens:
// src/prismicio.ts
/**
* Uma lista de objetos Route Resolver que definem como o campo `url` de um documento é resolvido.
*
* Saiba mais em: {@link <https://prismic.io/docs/route-resolver#route-resolver>}
*/
const routes: prismic.ClientConfig["routes"] = [
{
type: "blog_post", // Tipo do documento no Prismic
uid: "homepage", // Identificador único para a rota da homepage
path: "/", // Caminho da URL para a homepage
},
{
type: "blog_post", // Tipo do documento para postagens do blog
path: "/:uid", // Caminho dinâmico da URL para cada postagem, baseado em seu UID
},
];
Com essas configurações, o Prismic saberá exatamente como mapear as URLs para a estrutura de pastas do nosso aplicativo App.
Adicionando segurança nas chamadas com o Prismic
Até então, estamos fazendo chamadas diretamente para a API do Prismic utilizando o SDK, porém, tudo de maneira pública, ou seja, qualquer pessoa que souber o nome do nosso repositório, pode fazer requisições de leitura para nossa API por fora do nosso site e isso pode ocasionar problemas de segurança e cobranças indevidas pelo Prismic.
Para resolver isso, iremos privar todas as chamadas para a API e só será permitido realizar requisições se enviarmos um token de acesso junto a cada requisição.
Primeiro, vamos até o site do Prismic, entramos na página do nosso repositório e vamos em configuração. A partir de lá, só acessar a aba de API e segurança.
Vamos então até a área Repository security e iremos trocar o acesso de Public Api para Private Api. Após a seleção, só precisamos clicar no botão escrito Change the API visibility.
Ok, agora todas as chamadas estão protegidas e você não vai mais poder requisitar suas postagens como fazíamos antes. Precisamos então, gerar um Access Token.
Para gerar um Access Token vamos na mesma página um pouco abaixo até a aba Generate an Access Token. Nesta área, precisamos preencher somente um campo que é o nome do aplicativo e deixamos a Callback URL vazia, já que não iremos nos preocupar com Oauth. Coloque o nome que você preferir, mas recomendo que seja algo que te lembre a qual aplicativo esse token se refere. Após isso, só clicar no botão Add this application.
Para nosso caso de uso, iremos utilizar o Permanent access tokens que foi gerado, basta copia-lo já que iremos utiliza-lo mais tarde. Esse token da acesso total de leitura as nossas postagens e iremos utilizar ele em nossa aplicação para tornar nossas requisições seguras.
Com o token em mãos, vamos até nosso código. A primeira coisa que devemos fazer e criar um arquivo na raiz do projeto chamada .env.local . Esse arquivo será onde o Next vai buscar nossas variáveis de ambiente e é imprescindível que os dados que colocarmos aqui não vazem, então certifique-se de não enviar esse arquivo para o repositório Git.
- Lembre-se que em caso de vazamento, basta você gerar um novo Access Token e revogar o anterior. Essa dica vale para qualquer aplicação que precise guardar dados seguros.
Dentro deste arquivo vamos colocar o conteúdo abaixo:
PRISMIC_ACCESS_TOKEN=SEU_TOKEN
Se você estava executando a aplicação do Next em algum terminal, recomendo que você feche ela e rode novamente para a aplicação ter acesso a sua nova variável de ambiente (O next faz isso sozinho, mas por garantia, recomendo realizar esse passo). Um ponto interessante disso, é que essa variável do jeito que está escrita só pode ser acessada pelo lado do servidor do Next. Isso é muito importante, pois como estamos guardando um valor sensível e que só é utilizado no servidor, não há riscos dessa variável vazar para o cliente web da nossa aplicação. Caso queira entender melhor como o Next disponibiliza essas variáveis, acesse essa documentação.
Agora que temos nossa variável, podemos utilizar em nossa aplicação. Vamos até o arquivo src/prismicio.ts e na função createClient iremos adicionar uma nova linha:
// src/prismicio.ts
export const createClient = (config: prismicNext.CreateClientConfig = {}) => {
const client = prismic.createClient(repositoryName, {
routes,
fetchOptions:
process.env.NODE_ENV === "production"
? { next: { tags: ["prismic"] }, cache: "force-cache" }
: { next: { revalidate: 5 } },
accessToken: process.env.PRISMIC_ACCESS_TOKEN,//NOTE: Adicione essa linha
...config,
});
prismicNext.enableAutoPreviews({
client,
previewData: config.previewData,
req: config.req,
});
return client;
};
A linha adicionada vai informar ao SDK do Prismic que devemos utilizar esse Access Token em cada requisição e se tudo estiver correto, agora sua aplicação voltou a funcionar e seu repositório Prismic está mais seguro.
O fim?
Com isso, encerro esse artigo e espero que você tenha conseguido absorver o máximo de conhecimento possível. Como disse anteriormente, foquei nos detalhes técnicos de nossa aplicação e como fazer uma integração fácil e rápida para a criação de um blog pessoal. Fique a vontade para melhorar e personalizar seu blog do jeito que quiser e fique ligado nas atualizações por aqui. Sempre que possível, irei criar novas postagens que vão melhorar esse código que criamos e adicionarei vários outros elementos que um blog deve ter.
Para acessar o repositório git desse projeto, você pode clicar nesse link. Neste repositório irei separar em tags os marcos da evolução do nosso blog. Caso queira ver um projeto de blog completo como o quê você está, Acesse esse repositório.
Agradeço por ter chegado até aqui e qualquer feedback e sugestão são bem vindos para mantermos uma boa comunidade de desenvolvedores.
Vejo você na próxima postagem!