Criando Skeletons
No exercício anterior, utilizamos fallbacks simples com mensagens de Carregando...
. Agora, vamos melhorar a experiência do usuário implementando skeletons
- representações visuais do conteúdo enquanto ele carrega.
O que são Skeletons?
Skeletons são versões simplificadas da UI que servem como placeholders enquanto o conteúdo real está carregando. Eles ajudam a:
- Reduzir a percepção de tempo de carregamento
- Prevenir layout shifts
- Dar uma ideia do conteúdo que está por vir
- Manter a consistência visual da aplicação
Instalando o react-loading-skeleton
Vamos usar a biblioteca react-loading-skeleton
para criar nossos skeletons. Primeiro, instale-a:
npm install react-loading-skeleton
Adicione os estilos necessários importando-os no seu arquivo root layout:
import 'react-loading-skeleton/dist/skeleton.css';
Criando Skeletons para JobDetails
Primeiro, vamos criar um skeleton para os detalhes da vaga. Crie um novo componente:
import Skeleton from 'react-loading-skeleton';
export default function JobDetailsSkeleton() { return ( <div className="rounded-lg border border-gray-200 bg-white p-6 shadow-md"> <div className="mb-4"> <Skeleton height={32} width={200} /> </div> <div className="mb-6"> <Skeleton height={20} width={150} /> </div> <div className="mb-6"> <Skeleton count={3} /> </div> <div className="flex gap-2"> <Skeleton height={30} width={100} /> <Skeleton height={30} width={100} /> </div> </div> );}
Criando Skeletons para Comments
Agora, vamos criar um skeleton para a seção de comentários:
import Skeleton from 'react-loading-skeleton';
export default function CommentsSkeleton() { return ( <div className="mt-8"> <h2 className="mb-6 text-2xl font-bold">Comentários</h2> <div className="space-y-4"> {Array.from({ length: 3 }).map((_, i) => ( <div key={i} className="max-w-lg"> <Skeleton height={80} className="mb-4" /> </div> ))} </div> </div> );}
Atualizando a página principal
Agora vamos atualizar nossa página principal para usar esses skeletons:
import { ArrowLeft } from 'lucide-react';import Link from 'next/link';import { Suspense } from 'react';import JobDetails from './job-details';import Comments from './comments';import JobDetailsSkeleton from './job-details-skeleton';import CommentsSkeleton from './comments-skeleton';
export default async function JobPosting({ params,}: { params: Promise<{ id: string }>;}) { const jobId = (await params).id;
return ( <div className="container mx-auto max-w-4xl p-4"> <div className="mb-6"> <Link href="/vagas" className="text-muted-foreground hover:text-foreground inline-flex items-center" > <ArrowLeft className="mr-2 h-4 w-4" /> Todas as Vagas </Link> </div> <Suspense fallback={<JobDetailsSkeleton />}> <JobDetails jobId={jobId} /> </Suspense> <Suspense fallback={<CommentsSkeleton />}> <Comments jobId={jobId} /> </Suspense> </div> );}
Criando Skeleton para a lista de vagas
Não podemos esquecer da página principal que lista todas as vagas. Vamos criar um skeleton para ela também:
import Skeleton from 'react-loading-skeleton';
export default function JobsListSkeleton() { return ( <div className="space-y-4"> {Array.from({ length: 5 }).map((_, i) => ( <div key={i} className="rounded-lg border border-gray-200 bg-white p-6 shadow-md" > <div className="mb-2"> <Skeleton height={24} width={200} /> </div> <div className="mb-4"> <Skeleton height={16} width={150} /> </div> <div className="flex gap-2"> <Skeleton height={24} width={80} /> <Skeleton height={24} width={80} /> </div> </div> ))} </div> );}
E atualizar o arquivo loading.tsx da página de vagas:
import JobsListSkeleton from './jobs-list-skeleton';
export default function Loading() { return <JobsListSkeleton />;}
Dicas para criar bons Skeletons
- Deixamos a estrutura relativamente similar: O skeleton deve refletir o layout do conteúdo real para evitar mudanças bruscas quando o conteúdo carregar.
- É mais fácil esperar com animações: A biblioteca
react-loading-skeleton
já inclui animações por padrão.
Resultado final
Com essas implementações, nossa aplicação agora tem:
- Loading states
- Melhor feedback visual durante o carregamento
- Transições mais suaves entre estados de loading e conteúdo
- Uma experiência mais profissional e polida