Criando um projeto simples com react router
05 abr. 2025 • 4 minutos de leitura
Olá! Como vai?
Já pensou em criar um projeto com algumas telas e operações no local storage usando o react das massas mas se viu perdido procurando a melhor forma de criar o projeto, instalar as dependências e configurá-las? Sim?
Eu também! São inúmeras opções mesmo com certas tecnologias já definidas… usando Bun ou não? create-vite
? Next.js? create-react-router
? Zustand ou redux? TailwindCSS com postcss ou a própria CLI? Shadcn UI, Radix UI, Daisy UI…? 😵💫
Nesse final de semana estou construindo um site com o treino do Sung Jinwoo de Solo Leveling (adoro o anime inclusive) e estava perdido nessas opções que mencionei para começar e terminar o projeto.
Este guia vai te ajudar quanto configuração das tecnologias que eu escolhi:
- TypeScript e Bun
- React
- React Router Declarativo
- TailwindCSS
- Eslint
Pensando nestes pontos:
- Projeto relativamente pequeno
- Sem necessidade de server-side rendering
- Estilização fácil e, de preferência, já pronta
- Single-page application
- Dados no local storage ou search params
Base do projeto
Começaremos com o projeto React, vamos criá-lo usando Bun e a ferramenta Vite com o seguinte comando:
bun create vite projeto-simples --template react-ts
Adicionaremos o React Router para fazer o roteamento das “páginas” no lado do cliente.
bun add react-router
Para finalizar esta parte precisamos configurar o modo declarativo do React Router com 3 linhas de código, no arquivo src/main.tsx
:
import { BrowserRouter } from 'react-router';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<BrowserRouter>
<App />
</BrowserRouter>
</StrictMode>
);
O diff atualmente fica assim.
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App.tsx';
+ import { BrowserRouter } from 'react-router';
createRoot(document.getElementById('root')!).render(
<StrictMode>
+ <BrowserRouter>
<App />
+ </BrowserRouter>
</StrictMode>
);
Estilos
Agora, seguindo o guia do TailwindCSS, vamos colocá-lo para usarmos as classes utilitárias.
bun add tailwindcss @tailwindcss/vite
No arquivo vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
export default defineConfig({
plugins: [
// ...outros plugins
react(),
tailwindcss(),
],
});
No arquivo src/index.css
@import "tailwindcss";
Em uma postagem futura vou abordar formas de implementar temas, porém existem duas páginas importantes desse tema (ba-dum-tss): dark-mode e themes variables.
Local storage hook
Também vamos adicionar o hook para acessar e manipular o localStorage. No arquivo src/hooks/use-local-storage.ts
:
import { useState } from 'react';
export function useLocalStorage<T extends string | boolean | number | object>(
key: string,
initialValue: T
): [T, (value: T) => void] {
const storedValue = localStorage.getItem(key);
const parsedStoredValue = storedValue ? (JSON.parse(storedValue) as T) : initialValue;
const [value, setInternalValue] = useState(parsedStoredValue);
const setValue = (v: T) => {
localStorage.setItem(key, JSON.stringify(v));
setInternalValue(v);
};
return [value, setValue];
}
Explicando o que este hook faz na assinatura e no bloco:
- Pega um tipo genérico T para melhorar o autocomplete e ter uma API com type-safety (obs.: não precisa ser definido explicitamente porque pode ser inferido pelo parâmetro
initialValue
) - Pega os parâmetros
key
(chave do que queremos buscar no local storage) einitialValue
(valor inicial caso não exista um atrelado à chave) - No bloco da função, pega o valor armazenado no local storage usando o método
getItem(key)
e depois, caso exista um valor (string), faz o parsing dele comJSON.parse
e aceita que ele é do tipo T (as T
), do contrário usa oinitialValue
- Usa o hook useState do React para criar um estado reativo do tipo T a partir do valor guardado
- Cria uma função para atualizar o item do local storage e o estado reativo
- Retorna um array com o estado reativo na primeira posição e a função para altera-lo na segunda
Linting e formatação
Depois de horas lendo sobre ferramentas conhecidas (eslint, prettier, biome) e convenções, decidi usar o eslint como linter e formatter já que ele é capaz e maduro (soluções e integrações com editores).
Para isso precisei instalar o typescript-eslint e o @stylistic/eslint-plugin.
bun add -D typescript-eslint @stylistic/eslint-plugin
E a configuração eslint.config.js
ficou assim:
import stylistic from '@stylistic/eslint-plugin';
import reactHooks from 'eslint-plugin-react-hooks';
import reactRefresh from 'eslint-plugin-react-refresh';
import globals from 'globals';
import tseslint from 'typescript-eslint';
// Cria configuração do eslint
export default tseslint.config(
tseslint.configs.strictTypeChecked, // estende as configurações estritas com checagem de tipos do typescript-eslint
stylistic.configs.recommended, // estende as configurações recomendadas do stylistic
stylistic.configs.customize({ semi: true }), // customiza a configuração `semi` (ponto e vírgula)
{
// configuração que vem do create-vite
ignores: ['dist'],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
projectService: true, // https://typescript-eslint.io/getting-started/typed-linting
tsconfigRootDir: import.meta.dirname,
},
},
plugins: {
'@stylistic': stylistic, // adiciona o plugin stylistic
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
}
);
Conclusão
Resumindo, com esse setup você deve conseguir criar pequenos projetos ou protótipos facilmente implantáveis e que não requerem um back end dedicado com API e banco dados para funcionar 100%.
Escrevi esse artigo justamente porque adoro construir projetos úteis e que não fogem desse escopo, o problema é que sempre que começo um novo projeto preciso pesquisar novamente alguns comandos, dependências e configurações.
Inclusive, aqui estão os sites:
Muito obrigado por ter lido até aqui :green_heart:
Estou disponível para contato no Twitter/X, Bluesky e LinkedIn. Até a próxima!