Pular para o conteúdo principal

Routing

O template usa React Router v7 com SSR. A definicao de rotas usa a API @react-router/dev/routes.

Route Registry

O Route Registry e o sistema centralizado de rotas configuraveis por brand. Ele separa a definicao de URLs (paths) da arvore de rotas (route tree), permitindo que brands customizem todos os paths de pagina sem tocar em componentes nem na arvore.

Arquivos

ArquivoDescricao
app/types/routes.tsTipos: RoutePathMap, RouteParamsMap, StaticRouteKey, DynamicRouteKey
app/config/routes/paths.tsMapa padrao de paths (overridavel por brand)
app/utils/routes.tsHelpers: routeHref(), routePattern(), gameHref(), sportPath(), isRouteActive(), validateRoutePaths()
app/router/routes.tsArvore de rotas (consome routePattern())
overrides/<brand-key>/app/config/routes/paths.tsOverride de paths por brand

Helpers — ~/utils/routes

FuncaoDescricaoExemplo
routePattern(key)Pattern para route config (sem / inicial)routePattern('casino.play')'games/:provider/:game'
routeHref(key)URL para rota estaticarouteHref('casino')'/games'
routeHref(key, params)URL para rota dinamicarouteHref('casino.play', { provider: 'pgsoft', game: 'tiger' })'/games/pgsoft/tiger'
gameHref(slug)URL de jogo a partir de slug provider/gamegameHref('pgsoft/fortune-tiger')'/games/pgsoft/fortune-tiger'
sportPath(subPath)Deep-link prefixado com a base do sportssportPath('/live')'/sports/live'
isRouteActive(key, pathname)Verifica se o pathname casa a rotaisRouteActive('casino', '/games/category/slots')true
validateRoutePaths(paths)Valida um RoutePathMap (build-time)Usado no startup do server

Como app/router/routes.ts resolve o override

app/router/routes.ts e avaliado por vite-node em config time, antes do sistema de aliases do Vite estar ativo. Por isso ele nao importa ~/config/routes/paths: resolve o arquivo pelo resolver compartilhado (vite-plugins/brand-resolver.mjs — o mesmo usado pelo plugin de overrides e pelo tailwind.config.js) e faz import() dinamico do arquivo certo.

ORIGIN_DOMAIN=state77.com
→ brand-key = state77-com
→ findBrandOverride(cwd, "state77-com", "app/config/routes/paths.ts")
→ overrides/state77-com/app/config/routes/paths.ts
→ routePattern('casino') → 'casino' (sem /)
→ routeHref('casino') → '/casino'

Em runtime, os componentes importam routePaths de ~/config/routes/paths e o brandOverridesPlugin faz a mesma substituicao — o resultado e identico.

Alem de paths.ts, o mesmo mecanismo de config-time e usado para app/config/routes/lp-redirects.ts e app/config/routes/legacy-redirects-cross-brand.ts, porque essas listas geram rotas e precisam existir antes do build da arvore.

Regras

  • Nomes de parametros sao fixos: :slug, :provider, :game, :id, :type, :token, :area, :filename, :cat — componentes usam useParams() com esses nomes exatos
  • Brands so customizam segmentos estaticos do path
  • Rotas de API nao entram no registro — sao endpoints internos do servidor
  • Override deve ser completo — TypeScript da erro se faltar alguma chave

routes.ts (entry point)

O arquivo app/routes.ts na raiz do app/ e o entry point do routing:

// app/routes.ts
import type { RouteConfig } from '@react-router/dev/routes';
import { buildRoutes } from './router/routes';

export default buildRoutes() satisfies RouteConfig;
cuidado

routes.ts usa import relativo ./router/routes — o alias com tilde (~/router/routes) nao funciona aqui por limitacao do React Router.

app/router/routes.ts (arvore de rotas)

Define toda a arvore. Usa routePattern() para obter os paths do Route Registry. Forks editam este arquivo diretamente para adicionar/remover paginas.

informação

Este arquivo foi movido de app/config/routes.config.ts para app/router/routes.ts na refatoracao do sistema de layout. app/config/ hoje contem apenas configuracao brand-editavel — ver Config map.

A arvore esta organizada em:

  1. Redirects legados gerados a partir de config (fora do layout)
  2. Resource routes — manifest, robots, sitemaps (fora do layout, sem DefaultLayout)
  3. API routes — server-only, organizadas em app/routes/api/<dominio>/
  4. Rotas fora do layout — footer embeddable, bounce de redirect, game-error, mkt, clever
  5. Page routes dentro de layout("routes/_layout.tsx")
  6. Guard de restricaolayout("routes/restricted-guard-layout.tsx") envolvendo casino, sports, gamification, search, favoritos, recentes e profiles
  7. User arealayout("routes/user/_layout.tsx"), navegacao em abas
  8. Catch-allroute("*", "routes/$.tsx") dentro do layout

Resource Routes (fora do layout)

RotaArquivoDescricao
manifest.jsonroutes/manifest[.]json.tsPWA manifest
robots.txtroutes/robots[.]txt.tsRobots.txt
sitemap.xmlroutes/sitemap[.]xml.tsIndex de sitemaps
sitemap-static.xmlroutes/sitemap-static[.]xml.tsURLs estaticas
sitemap-games/categories.xml · sitemap-games/providers.xml · sitemap-games/:provider.xmlroutes/sitemap-games.*Catalogo de jogos. categories e providers sao slugs reservados — um provider real com esse slug nunca chegaria na rota dinamica
sitemap/:area/:filenameroutes/sitemap-content.$area.$filename.tsSitemap de conteudo (blog, promotions, news, faq, help, responsible-gaming) com sharding por volume
sitemap-faq.xml · sitemap-profiles.xmlrotas dedicadas
sitemap-<group>.xml (varios)routes/sitemap-brand-group[.]xml.tsSitemap-groups definidos pela brand em ~/config/seo/sitemap.ts. Cada path e registrado explicitamente porque RR7 nao aceita :param depois de -; o handler e o mesmo e resolve o group pelo pathname. 404 quando a brand nao declara o group

Sao 36 rotas sitemap* hoje — a maioria delas grupos de brand apontando para o mesmo handler. Para a lista exata, consulte app/router/routes.ts.

Redirects legados

Tres geradores transformam config em rotas de redirect. Nenhum deles precisa de arquivo novo por path.

GeradorConfigHandler
Legacy Vue/Nuxtapp/config/routes/legacy-redirects.ts (base, compartilhada) + legacy-redirects-cross-brand.ts (overridavel — usada por brands que compartilham usuarios, como as quatro "7k")routes/legacy-redirect.ts
Landing pagesapp/config/routes/lp-redirects.tsroutes/lp-legacy-redirect.ts
Conteudo legado (/<area>/:cat/:slug/<area>/:slug)derivado dos paths das areas de conteudoroutes/legacy-content-redirect.ts

Ha ainda redirects pontuais: routes/casino-redirect.ts (/casino → path de cassino da brand, so registrado quando os dois diferem — evita loop) e routes/pending-redirect.ts (/pending → 301 para /redirect). O redirect do hub live nao e uma rota: vive no loader de routes/games/live.tsx, gateado por liveHubRedirectsToCategory (~/config/routes/live-hub-redirect, overridavel) e pelo CASSINO_MODE.

API Routes

101 rotas api/* hoje, todas em diretorios aninhados sob app/routes/api/. A fonte de verdade e app/router/routes.ts — a tabela abaixo mapeia dominio → diretorio, nao rota a rota.

:::danger Os paths NAO sao arquivos flat As rotas de API vivem em routes/api/<dominio>/<nome>.ts — por exemplo routes/api/auth/login.ts, routes/api/wallet/refresh.ts, routes/api/kyc/start.ts. Nao existe nenhum arquivo app/routes/api.*.ts. :::

DominioDiretorioRotas hojeNotas
Authroutes/api/auth/12login, register, register.validate-phone, validate-document, documents.validate, documents.confirm-birthdate, social.$provider, logout, profile, recovery, refresh, recheck-spa
Validationroutes/api/validation/11email.{send,verify,change}, sms.{send,verify,change}, docs.submit, address.submit, full-name.submit, terms.accept, link-confirm
Userroutes/api/user/22Perfil, senha, telefone, limites, preferencias, historico de login, contas sociais, timeout/auto-exclusao, referrals, documentos, 2FA, zendesk-token
Walletroutes/api/wallet/4refresh, transactions, action, receipt
Paymentsroutes/api/payments/8providers, deposit, deposit-status, withdraw, bank-list, bank-account, pix-key, coupon
Gamesroutes/api/games/13list, search, start, vote, by-slugs, suggestions, stats-batch, statistics-dl, top-wins, top-games, category/:slug, provider/:slug, rows/:page
Sportsroutes/api/sports/8search, launch, anonymous-launch, betby-jwt, altenar-token, rogue-anonymous, rogue-login, rogue-proxy/*
Sportsbook proxyroutes/api/cactus-sportbook/$.ts1Proxy raw do iframe First (ApiClient.proxyRaw, binding-aware)
KYCroutes/api/kyc/2start, status
Cacheroutes/api/cache/2purge e inspect. A rota antiga api/cache/games/purge nao existe mais
Income reportroutes/api/income-report/3available-years, generate, status/:id
Rewardsroutes/api/rewards/2list, redeem
FTD cashbackroutes/api/ftd-cashback/2verify, send-cashback
Favoritosroutes/api/favorites.ts1GET le, POST/DELETE fazem toggle
Searchroutes/api/search/unified.ts1Busca unificada (games + sports)
Trackingroutes/api/tracking/appsflyer.ts1Proxy AppsFlyer — 503 sem APPSFLYER_API_KEY
Addressroutes/api/address/lookup-by-postal-code.ts1CEP → endereco
Logsroutes/api/logs/auth-logout.ts1Telemetria de logout
Versionroutes/api/version.ts1Payload de build (BUILD_ID)
Clear cacheroutes/api/clear-cache.ts1Version-check + Clear-Site-Data
Devroutes/api/dev/4cache-clear, cache-policy, proxy, visitor-info

:::caution Rotas de dev nao existem em producao devApiRoutes so e registrado em builds de dev. Em producao as rotas nao existem e o Vite tree-shaka os arquivos do bundle do worker inteiro. Essa e a unica camada de defesa — um gate so em runtime seria insuficiente para uma rota que encaminha requests arbitrarios ao BFF com o cf-worker-key. :::

Contrato de erro das rotas de proxy

Toda rota /api/* que proxia o BFF deve usar proxyErrorResponse() e unauthorizedNoToken() de ~/utils/proxy-error.server — nunca um envelope proprio em 401/403. Detalhes e a lista de excecoes em Services.

Rotas fora do layout (paginas)

RotaArquivoDescricao
_/footerroutes/_._footer.tsxFooter embeddable (iframe do First)
redirect · redirect/*routes/redirect.$.tsxBounce full-screen ("Estamos te redirecionando…"), sem header/sidebar/footer
game-errorroutes/game-error.tsxErro de launch de jogo. noindex,nofollow, Disallow no robots e fora de todo sitemap
mkt · mkt/*routes/mkt.tsLanding de marketing (302 + Set-Cookie de tracking). Nunca cacheada
cleverroutes/clever.tsIntegracao dedicada
dev/ftd-cashbackroutes/dev.ftd-cashback.tsxPreview de dev

Page Routes (dentro do layout)

Renderizadas dentro de layout("routes/_layout.tsx"), que monta o DefaultLayout (shell, header, sidebar, footer, providers).

:::info Route Registry Os paths abaixo sao os defaults de app/config/routes/paths.ts (59 chaves hoje). Brands customizam qualquer um deles via override. app/router/routes.ts usa routePattern(key) para resolver o path correto. :::

Gerais

ChavePath padraoArquivo
home/routes/_index.tsx
search/searchroutes/search.tsx
search.casino/search/casinoroutes/search.casino.tsx
search.sports/search/sportsroutes/search.sports.tsx
favorites/favoritesroutes/favorites.tsx
recents/recentsroutes/recents.tsx
promocode/promocoderoutes/promocode.tsx
debug/debugroutes/debug.tsx (so em build de dev)

Games / Casino

ChavePath padraoArquivo
casino/gamesroutes/games/_index.tsx
casino.live/games/liveroutes/games/live.tsx
casino.category/games/category/:slugroutes/games/category.$slug.tsx
casino.providers/games/providersroutes/games/providers._index.tsx
casino.provider/games/providers/:slugroutes/games/providers.$slug.tsx
casino.play/games/:provider/:gameroutes/games/$provider.$game.tsx

Sports

O sportsbook usa nested layout (routes/sports/_layout.tsx) para manter o componente do sportsbook montado entre sub-rotas.

ChavePath padraoArquivo
sports/sportsroutes/sports/_index.tsx
sports.catchAll/sports/*routes/sports/$.tsx
sports.test/sports-testroutes/sports-test.tsx

VIP / Gamificacao

Agrupadas sob layout("routes/vip.tsx").

ChavePath padraoArquivo
gamification/viproutes/vip/_index.tsx
gamification.missions/vip/missionsroutes/vip/missions.tsx
gamification.tournaments/vip/tournamentsroutes/vip/tournaments.tsx
gamification.tournament/vip/tournaments/:idroutes/vip/tournaments.$id.tsx
gamification.store/vip/storeroutes/vip/store.tsx
gamification.miniGames/vip/mini-gamesroutes/vip/mini-games.tsx
gamification.levels/vip/levelsroutes/vip/levels.tsx
gamification.badges/vip/badgesroutes/vip/badges.tsx
gamification.bonuses/vip/bonusesroutes/vip/bonuses.tsx

Conteudo (WordPress)

Seis areas de conteudo com a mesma forma: hub, category-page e post detail. :slug e um smart resolver — o server decide se e categoria ou post.

ChavePath padraoArquivo
promotions / .category / .post/promotions · /promotions/:slugroutes/promotions.tsx · routes/promotions.$slug.tsx
blog / .category / .post/blog · /blog/:slugroutes/blog.tsx · routes/blog.$slug.tsx
news / .category / .post/news · /news/:slugroutes/news._index.tsx · routes/news.$slug.tsx
responsibleGaming / .category / .post/responsible-gaming · /responsible-gaming/:slugroutes/responsible-gaming._index.tsx · routes/responsible-gaming.$slug.tsx
faq / .category / .post/faq · /faq/:slugroutes/faq/_index.tsx · routes/faq/$slug.tsx
faq.game/faq/:provider/:gameroutes/faq/$provider.$game.tsx
helpCenter/helproutes/help-center.tsx
help.categories/help/categoriesroutes/help.categories.tsx
help.category / help.post/help/:slugroutes/help.$slug.tsx
ChavePath padraoArquivo
legal.page/page/:slugroutes/page.$slug.tsx
profiles/perfisroutes/profiles._index.tsx
profiles.detail/perfil/:slugroutes/profiles.$slug.tsx
— (sem chave)/lp/:slugroutes/lp.$slug.tsx — catch-all generico; cada brand define quais slugs existem em ~/config/routes/lps. Slug desconhecido = 404 em runtime

As rotas de profiles sempre existem, mas os loaders se auto-gateiam por featuresConfig.profiles?.enabled + a quantidade de perfis registrados na brand — quando desligado, lancam 404.

User (fora do user layout)

ChavePath padraoArquivo
user.notifications/user/notificationsroutes/user/notifications.tsx
user.validate/user/validate/:type/:tokenroutes/user/validate.$type.$token.tsx

User Area (auth-protected, navegacao em abas)

Renderizadas dentro de layout("routes/user/_layout.tsx").

:::info Modelo de abas As rotas antigas user.account, user.security, user.loginHistory, user.accountProtection e user.irpf nao existemrouteHref("user.account") e erro de typecheck. Foram substituidas pelas abas abaixo. :::

ChavePath padraoArquivo
user.data/user/dataroutes/user/general.tsx
user.wallet/user/walletroutes/user/wallet.tsx
user.history/user/historyroutes/user/history.tsx
user.config/user/configroutes/user/config.tsx
user.protection/user/protectionroutes/user/protection.tsx
user.rewards/user/rewardsroutes/user/rewards.tsx
user.incomeReport/user/income-reportroutes/user/income-report.tsx
user.refers/user/refersroutes/user/refers.tsx

_layout.tsx (loader)

O _layout.tsx e o layout route que carrega a brand, monta o clientEnv e renderiza o DefaultLayout.

:::danger O loader do _layout NAO retorna auth Desde a spec user-data-out-of-ssr (2026-07), nenhum dado do usuario logado atravessa o SSR. O campo auth foi removido do loader por completo, e existe um teste leak-canary (app/routes/__tests__/no-user-data-in-ssr.test.ts) que quebra se ele voltar. Para ler auth na UI, use useAuthGate() — ver State Management. :::

O shape real do retorno:

interface LayoutLoaderData {
brand: BrandConfig | null;
brandError: BrandError | null;
clientEnv: ClientEnv;
serverIsMobile: boolean;
legalTerms: Promise<Pick<LegalTerm, "route" | "title">[]>; // deferred (streaming)
orgCountry: { alpha2: string; name: string } | null;
}

brand e o unico await critico no caminho do TTFB. legalTerms volta como Promise nao aguardada: o RR7 faz streaming via <Await>/<Suspense> no footer, que degrada graciosamente para links config-only com [].

shouldRevalidate

O loader do _layout nao revalida em navegacao SPA entre paginas diferentes (retorna false): brand, legalTerms, clientEnv e serverIsMobile sao estaticos pela sessao inteira. Isso eliminou um round-trip ao BFF na cauda de cada navegacao. Com auth fora do loader, o skip nao tem nenhum efeito sobre reatividade de auth.

Dicas

  • Edite app/router/routes.ts para adicionar/remover paginas — app/routes.ts apenas importa buildRoutes() dali
  • Use routePattern(key) na arvore em vez de strings hardcoded
  • Use routeHref(key) ou gameHref(slug) para links em componentes — nunca paths hardcoded
  • Use layout() para nested layouts e index() para a pagina default de um grupo
  • Rotas cacheaveis no edge sao whitelists de prefixo literal em workers/middleware.ts: ao renomear um path por override, adicione o prefixo novo la tambem — ver Caching
  • API routes sao server-only, nao geram bundle client-side e nao fazem parte do Route Registry