A rede social de quem corre — e o projeto do segundo curso de React da Alura.
As pessoas publicam treinos (foto, tempo, distância, calorias, batimentos), curtem, comentam e visitam perfis. Tudo isso vai existir de verdade até o fim do curso. Por enquanto, o que você tem em mãos é o layout completo, estático e sem nenhum comportamento.
Porque você já provou que sabe fazer isso.
No curso anterior você montou o Fokus do zero: componentes, props, hooks, CSS Modules, TypeScript, build e deploy. Repetir esse trabalho aqui não te ensinaria nada novo — só custaria umas boas horas escrevendo CSS.
O foco deste curso é outro: comportamento. Navegação, dados vindos de uma API real, autenticação, upload de imagem, interações sociais e testes. É a camada que transforma uma tela bonita em um aplicativo.
Então a divisão é essa:
| Já está pronto | Você vai construir |
|---|---|
| 6 páginas estáticas | Rotas e navegação (React Router) |
| Biblioteca de componentes | Consumo de API (fetch → axios) |
| CSS Modules e paleta | Cadastro, login e JWT |
| Tipagens de props | Context do usuário logado e rotas protegidas |
| Ícones (lucide-react) | Upload de imagem com multipart/form-data |
| Dados falsos ("mockados") | Curtidas, comentários, abas via URL |
| Testes com Vitest + React Testing Library |
Pré-requisito: Node.js 20 ou superior.
# clone o repositório
git clone https://github.com/viniciosneves/runner-circle.git
cd runner-circle
# instale as dependências
npm install
# suba o servidor de desenvolvimento
npm run devAbra o endereço que o Vite mostrar no terminal (geralmente http://localhost:5173).
| Comando | O que faz |
|---|---|
npm run dev |
Sobe o servidor de desenvolvimento com hot reload |
npm run build |
Checa os tipos (tsc) e gera o build de produção |
npm run preview |
Serve o build de produção localmente |
npm run lint |
Roda o ESLint no projeto |
Um susto proposital: as seis páginas renderizadas uma embaixo da outra, numa rolagem infinita.
Isso não é bug. É o ponto de partida do curso. Dá uma olhada no App.tsx:
function App() {
return <>
<Register />
<Login />
<Feed />
<NewPost />
<Profile />
<EditProfile />
</>
}Sem rota, sem estado, sem dado real. As páginas existem, mas não há nada que decida qual delas mostrar — porque é exatamente isso que você vai ensinar o app a fazer na aula 1.
💡 Dica pra explorar sozinha: comente as outras e deixe só uma página por vez. É a forma mais rápida de ver cada tela isolada antes de começar.
| Página | Arquivo | Rota que ela vai ganhar | O que tem nela |
|---|---|---|---|
| Login | src/pages/Login/ | /login |
Formulário de acesso, banner lateral, link pro cadastro |
| Register | src/pages/Register/ | /cadastro |
Nome, email e senha, link pro login |
| Feed | src/pages/Feed/ | / |
Busca + grid de treinos publicados |
| NewPost | src/pages/NewPost/ | /nova-postagem |
Upload de foto + campos do treino + tipo (caminhada/corrida) |
| Profile | src/pages/Profile/ | /perfil |
Cabeçalho do perfil, abas Posts/Curtidas, grid de posts |
| EditProfile | src/pages/EditProfile/ | /perfil/editar |
Troca de avatar, usuário, nome e bio |
runner-circle/
├── public/
│ ├── favicon.svg
│ └── images/ # logo, banners das telas de auth, padrão de fundo
└── src/
├── components/ # a biblioteca de componentes (um por pasta)
│ ├── Button/
│ │ ├── Button.module.css
│ │ └── index.tsx
│ └── icons/ # ícones em SVG que o lucide-react não tem
├── pages/ # as seis telas
├── App.tsx # hoje: a pilha de páginas. amanhã: o router
├── main.tsx # o ponto de entrada, com o createRoot
└── index.css # reset + variáveis de cor e fonte
A convenção é a mesma do curso anterior: uma pasta por componente, com index.tsx e o .module.css ao lado. O import fica limpinho (import Button from '../Button') e o CSS nunca vaza pra fora do componente.
São mais de 30 componentes prontos. Você não precisa decorar — mas vale saber que eles existem, porque quase sempre a peça que você precisa já está na caixa.
Estrutura e navegação
AppLayout (a área logada, com a sidebar) · AuthLayout (as telas de login/cadastro, com o banner) · Sidebar · NavItem · Logo · Columns · Divider
Formulários
Form · FormField (label + campo, já ligados) · FormActions · Input · Textarea · TimeInput · Checkbox · Radio · RadioGroup · SearchInput · Button · Label
Conteúdo e feed
PostCard · PostsGrid · StatChip (os chips de distância/calorias/batimentos) · Tag · Avatar · ImagePlaceholder · EmptyState · Text · Title
Perfil e interações
ProfileHeader · Tabs + Tab · Modal · CommentsModal · Comment
Upload
ImageUploader · FileItem
Auth social
SocialLogin · AuthLink · icons/GoogleIcon
Todos usam a mesma receita: ComponentProps do elemento HTML + as props específicas, com ...rest no final pra repassar o que sobrou.
type ButtonProps = ComponentProps<'button'> & {
variant?: 'primary' | 'outline'
icon?: ReactNode
}
function Button({ children, icon, variant = 'primary', type = 'button', ...rest }: ButtonProps) {
return (
<button className={`${styles.button} ${styles[variant]}`} type={type} {...rest}>
{children}
{icon}
</button>
)
}Traduzindo: o Button aceita qualquer atributo de um <button> de verdade — onClick, disabled, aria-label — sem precisar declarar um por um. Quando você for plugar comportamento nos componentes, é esse ...rest que vai te salvar.
Alguns componentes existem no projeto mas não são usados por nenhuma página ainda. Não é esquecimento — é encomenda pro futuro:
ModaleCommentsModal— a modal de comentários da aula 5. Repare que oModalé controlado por props (open+onClose) e não guarda estado nenhum por dentro: quem decide se ele abre é quem o usa.EmptyState— a tela de "nada por aqui ainda", que entra quando o feed vier vazio da API (aula 2).
As cores e a fonte vivem como variáveis CSS no index.css. Use as variáveis, não os valores crus.
| Variável | Valor | Onde aparece |
|---|---|---|
--lime |
#C6FF3F |
Botões primários, destaques |
--bg |
#F8FFE0 |
Fundo das páginas |
--dark |
#17181C |
Sidebar, textos fortes |
--blue |
#587FEE |
Links e ações secundárias |
--text |
#1B1B1B |
Texto padrão |
--gray-input |
#E2E2E2 |
Fundo dos campos |
--gray |
#8F8F8F |
Textos de apoio, placeholders |
--font |
Chakra Petch |
Toda a tipografia |
A fonte vem do Google Fonts, carregada no index.html.
| Ferramenta | Papel |
|---|---|
| React 19 | A biblioteca de UI — com React Compiler ligado |
| TypeScript | Tipagem das props e dos dados |
| Vite | Servidor de dev e build |
| CSS Modules | Estilo com escopo por componente |
| lucide-react | Os ícones |
| ESLint | Padronização e regras dos hooks |
Ao longo do curso entram: react-router, axios, Vitest e React Testing Library.
aula 01 páginas empilhadas → SPA com rotas e navegação
aula 02 feed com dados reais da API (fetch → axios)
aula 03 auth completa: cadastro, login, JWT, context, rotas protegidas
aula 04 criar postagem com foto (multipart + preview)
aula 05 perfil com abas na URL, avatar, curtidas e comentários
aula 06 testes com Vitest + RTL protegendo o app inteiro
No fim, o Runner Circle sai daqui como um app completo — e você sai com o be-a-bá de React fechado.
Este é o segundo curso. Ele parte do princípio de que as ferramentas abaixo já estão no seu músculo:
- Componentes e props
useStatee como o React decide re-renderizaruseEffect(e a função de limpeza)useRefuseActionState+FormData- Listas e
key - TypeScript: interfaces, unions, tipos de props
- Build e deploy
Se algum item dessa lista soou nebuloso, volte antes de seguir. Rever o vídeo certo do curso anterior custa minutos; seguir com um buraco na base custa o curso inteiro. Essas ferramentas não vão ser reapresentadas aqui — elas vão ser usadas.
Ainda na aula 1, você vai instalar a extensão React Developer Tools (Chrome · Firefox). É o "inspecionar elemento" do mundo React: em vez de divs, você enxerga a árvore de componentes ao vivo e as props que cada um recebeu.
Com o projeto rodando, abra a aba Components e clique em um card do feed. Você vai ver as props do PostCard preenchidas — e vai entender esse projeto muito mais rápido do que lendo arquivo por arquivo.
Bons treinos. Bora codar. 🏃♀️💨