Todo semana, fundadores de primeira viagem do React Native nos perguntam a mesma coisa: qual pilha eu devo usar?
Aqui está nossa resposta copiada e colada, com as provas.
TL;DR
Cliente: Expo SDK 54 (gerenciado) + Expo Router + TypeScript
Estilo: NativeWind v4
Dados: TanStack Query v5 + React Hook Form
Mecânica: Reanimated 4.1
Back-end: Supabase (Postgres + Autenticação + Armazenamento + Funções de borda + RLS)
Pagamentos: Stripe (modo pagamento, uma vez)
Construção: EAS Build + EAS Update
Sem Redux. Sem Firebase. Sem autenticação personalizada. Sem Fastlane. Sem fluxo de trabalho manual.
Por que "entediante"
Você tem zero informações sobre ajuste produto-mercado no dia zero. Cada decisão de pilha que você faz no dia zero é uma aposta sem dados. A aposta correta é aquela que:
- Tem mais respostas no Stack Overflow
- Tem o caminho mais curto do "hello world → primeiro usuário"
- Custa menos para trocar quando você descobre que estava errado
Isso é uma pilha entediante, por definição.
O cliente
Expo SDK 54 (fluxo gerenciado)
npx create-expo-app@latest my-app
cd my-app
npx expo start
Não use expo prebuild. Não ejeque. O fluxo gerenciado dá a você EAS Build (construções iOS em nuvem sem Mac), expo-updates para pushes JS OTA, e módulos nativos pré-envoltos para 90% do que você precisará.
Expo Router
Roteamento baseado em arquivo. Se você conhece o Next.js App Router, você já conhece o Expo Router.
app/
├── (tabs)/
│ ├── _layout.tsx
│ ├── index.tsx
│ └── profile.tsx
├── notes/
│ └── [id].tsx
└── _layout.tsx
Links profundos, links universais e rotas tipadas vêm de graça. Documentação:
NativeWind v4
Tailwind para React Native.
Sua equipe já conhece o Tailwind. Avance.
Data layer
TanStack Query v5 para qualquer coisa que toca a rede
const { data, isPending, error } = useQuery({
queryKey: ['notes', userId],
queryFn: () => supabase.from('notes').select('*').eq('user_id', userId),
})
Invalidação de cache, refetch em segundo plano, atualizações otimistas, persistência offline — tudo incluso. Você não precisa do Redux.
React Hook Form para formulários
const { control, handleSubmit } = useForm<Note>()
<Controller
control={control}
name="title"
render={({ field }) => (
<TextInput onChangeText={field.onChange} value={field.value} />
)}
/>
Simples. Rápido. Boas tipagens TypeScript. Fim.
Movimento: Reanimated 4.1
const offset = useSharedValue(0)
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ translateX: offset.value }],
}))
const gesture = Gesture.Pan().Agora, nenhum bug do cliente, nenhuma JWT vazada, nenhuma rota da API mal configurada pode expor as linhas de usuário A para o usuário B. O banco de dados recusa. Esta é a decisão de segurança mais estratégica que você toma.
Edge Functions para segredos
Toda chave de API de terceiros (OpenAI, Anthropic, Stripe, Deepgram) vive em uma Edge Function. Zero segredos no pacote do cliente:
Deno.serve(async (req) => {
const apiKey = Deno.env.get('OPENAI_API_KEY')!
// ... chamar OpenAI, transmitir resposta de volta via SSE
})
Pagamentos: Stripe (pagamento único, não assinaturas)
Os primeiros fundadores optam por assinaturas porque é o que a Y Combinator diz para querer. Envie um pagamento único primeiro — simplifica o recobramento, acelera o checkout, sem uma taxa de churn para se preocupar na segunda-feira de manhã.
Webhook → tabela product_licenses → conceder acesso desbloqueado. O próprio Applighter embarca nesse padrão exato — cada modelo no catálogo usa esse fluxo de compra única do início ao fim.
Construções: EAS
eas build --plataforma ios --perfil produção
eas submit --plataforma ios --última
eas update --branch produção --mensagem "correção: falha ao iniciar"
Construções iOS em nuvem. Nenhum Mac necessário. Atualizações OTA são enviadas em minutos, não em dias.
Comparação de tabelas
Pedaço
Escolha Boring
Tendência alternativa
Por que v1 pula a tendência
Framework
Expo gerenciado
Bare RN
2 semanas reembrulhando bibliotecas nativas
Estilo
NativeWind
Tamagui
Sua equipe conhece o Tailwind
Estatos
TanStack Query + useState
Redux / Zustand
95% do estado é de servidor
Servidor
Supabase
Firebase
O modelo de consulta do Firestore é uma armadilha
Autorização
Autenticação Supabase
Clerk
Já está embutido no DB
Pagamentos
Stripe uma vez
RevenueCat subs
Subs são v2
Criação de builds
EAS
Fastlane
Nativo na nuvem, nenhum Mac necessário
O que omitimos deliberadamente
-
Redux / Zustand — veja acima
-
GraphQL — Postgres + PostgREST é suficiente
-
Sentry — recomendado, mas não no dia 1
-
Mono-repo — ótimo para 3+ apps, excessivo para um
-
Detox / Maestro — testes unitários primeiro, E2E após 6 meses
A pergunta sobre a pilha de IA
"Mas meu app é um app de IA — a pilha muda?"
Quase não:
+ Supabase Edge Function (contém OPENAI_API_KEY)
+ Tabela Postgres para histórico de chat (RLS ativado)
+ SSE streaming de volta ao cliente (renderizado com Reanimated)
Isso é tudo. Recursos de IA são apenas recursos.
O playbook
-
npx create-expo-app na versão mais recente e estável do SDK
- Adicionar NativeWind + Expo Router + Reanimated + TanStack Query + RHF
- Projeto Supabase → esquema em migrações → RLS antes de qualquer linha
- Toda chave de terceiros em Edge Functions
- Pagamentos Stripe
método pagamento → webhook → tabela_product_licenses
- EAS → TestFlight nas primeiras duas semanas
- Lançar recursos. Reconsiderar a pilha em 18 meses.
Ou: pule etapas 1–5
Nossos modelos são exatamente essa pilha, conectada do início ao fim, com fluxo real Stripe e RLS em todas as tabelas — o catálogo está ligado na seção de pagamentos. Se você preferir começar no primeiro dia com um aplicativo funcional em vez de no trigésimo, é para isso que eles existem.
De qualquer forma — escolha uma pilha chata e lance. Ler mais posts sobre pilhas é o inimigo.
Qual foi a pilha real que você lançou com — e o que cortaria dessa lista? Deixe um comentário.

