Claude Code e React Native: guia prático de Expo, nativo e release
Use Claude Code com React Native: Expo vs bare, permissões, Metro, emuladores, acessibilidade e checks de release.
Defina o limite mobile antes de pedir código
Claude Code ajuda em React Native porque faz mais do que gerar uma tela. Ele pode ler o projeto, editar TypeScript, rodar comandos de verificação, explicar se é preciso reconstruir a parte nativa e entregar uma nota final de validação. Isso importa porque mobile cruza mais fronteiras do que um componente web: permissões de iOS, package de Android, resolução do Metro, development builds, emuladores, acessibilidade e checks de release.
O pedido fraco é “crie um app React Native”. O pedido forte diz se o projeto é Expo, se precisa de Expo development build ou se é bare React Native com ios/ e android/ mantidos pelo time. Também diz quais arquivos podem ser alterados, quais comandos exigem confirmação e qual plataforma precisa ser testada.
As referências oficiais que uso são Claude Code overview, Claude Code permissions, Expo documentation, Expo development builds, React Native environment setup, React Native Native Modules, Metro troubleshooting e React Native accessibility. Se o time ainda está começando, leia primeiro o guia inicial de Claude Code e depois o guia de permissões.
Comece com um project map
Um project map é um resumo operacional para o agente. Harness aqui significa o apoio de trabalho do agente: onde Claude Code pode atuar, como o resultado será verificado e quais ações não devem rodar automaticamente. Sem isso, ele pode produzir código que funciona, mas esquecer rebuild nativo, diferença iOS/Android ou restrição de release.
flowchart LR
A["Project map"] --> B["Claude Code task"]
B --> C["JS/TS implementation"]
B --> D["Native config"]
C --> E["Metro and unit tests"]
D --> F["Dev build / emulator"]
E --> G["Accessibility check"]
F --> G
G --> H["Release checklist"]
Antes da implementação, um arquivo curto resolve:
# React Native task map
App type: Expo app using TypeScript and Expo Router.
Native runtime: Expo Go for pure JS changes, development build for native libraries.
Targets: Android emulator first, iOS simulator on macOS before release.
Allowed files: app/, components/, hooks/, app.config.ts, metro.config.js, __tests__/.
Do not change: package manager, app slug, bundle identifiers, signing files, .env files.
Verification: npm run lint, npm test, npx expo-doctor, Android emulator smoke test.
Handoff: list changed files, commands run, platform not tested, and any native rebuild needed.
Templates atuais do Expo podem incluir contexto para agentes de código. Mesmo assim, apps existentes costumam ter README antigo, suposições de SDK velho e configurações nativas pouco documentadas. Faça Claude Code ler o repositório real antes de alterar.
Escolha Expo ou bare React Native com intenção
A documentação do React Native separa o desenvolvimento com framework do caminho em que você configura Android Studio e Xcode diretamente. Na prática, Expo costuma ser o caminho mais rápido para apps novos. Bare React Native continua adequado quando há SDKs nativos existentes, Gradle complexo, Pods customizados ou exigências de fornecedor.
| Decisão | Expo combina melhor | Bare React Native combina melhor |
|---|---|---|
| Velocidade inicial | MVPs, ferramentas internas, aprendizado, funções cobertas pelo Expo SDK | Código iOS/Android existente precisa permanecer |
| Recursos nativos | Camera, SecureStore, notificações e ajustes via config plugins | SDK próprio, terminal de pagamento, Bluetooth específico, build detalhado |
| Escopo do Claude Code | app/, components/, app.config.ts, testes | ios/, android/, Codegen, Pods, Gradle, arquivos gerados |
| Verificação | Expo Go ou development build, npx expo-doctor | npm run android, pod install, Xcode/Android Studio |
Expo Go não é prova de release. A documentação do Expo o descreve como ambiente rápido com conjunto fixo de bibliotecas nativas. Ao adicionar uma biblioteca com código nativo, use development build e peça que Claude Code informe se um novo binário é necessário.
Para criar um projeto Expo limpo:
npx create-expo-app@latest rn-claude-lab
cd rn-claude-lab
npx expo install expo-camera expo-secure-store @react-native-community/netinfo
npm install --save-dev @testing-library/react-native jest-expo @types/jest
npx expo start
Use a para Android Emulator ou i para iOS Simulator no macOS. Se a rede local bloquear o dispositivo, npx expo start --tunnel ajuda, mas será mais lento.
Ajuste permissões do Claude Code para mobile
Em React Native, um comando pode gerar um diff grande. expo prebuild, pod install, gradlew clean e eas build são válidos na tarefa certa, mas não devem rodar silenciosamente durante uma pequena mudança de UI.
Claude Code permissions separam allow, ask e deny. Um .claude/settings.json compartilhado pode liberar checks seguros, pedir confirmação antes de regenerar nativo ou fazer release build, e bloquear secrets e publicação.
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(npm test)",
"Bash(npm run lint)",
"Bash(npx expo-doctor)",
"Bash(adb devices)"
],
"ask": [
"Edit",
"Bash(npx expo prebuild*)",
"Bash(eas build*)",
"Bash(cd ios && bundle exec pod install)"
],
"deny": [
"Read(.env)",
"Read(.env.local)",
"Bash(git push*)",
"Bash(rm -rf*)"
]
}
}
Isso não é falta de confiança. É manter efeitos colaterais mobile dentro de uma revisão clara. Se a tarefa precisar de rebuild nativo, Claude Code deve explicar antes.
Implemente uma tela com permissão de câmera
O primeiro caso prático é um leitor QR para check-in, inventário ou ferramentas internas. Expo Camera oferece CameraView e useCameraPermissions. Peça para Claude Code modelar três estados: permissão carregando, permissão negada/não concedida e câmera pronta. Inclua labels acessíveis e proteção contra leitura duplicada.
// components/CameraQrCheck.tsx
import { CameraView, useCameraPermissions } from 'expo-camera';
import { useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
type ScanResult = {
data: string;
type: string;
} | null;
export function CameraQrCheck() {
const [permission, requestPermission] = useCameraPermissions();
const [scan, setScan] = useState<ScanResult>(null);
if (!permission) {
return (
<View style={styles.center}>
<Text>Checking camera permission...</Text>
</View>
);
}
if (!permission.granted) {
return (
<View style={styles.center}>
<Text style={styles.title}>Camera access is required</Text>
<Text style={styles.body}>
Allow camera access to scan QR codes on this device.
</Text>
<Pressable
accessibilityRole="button"
accessibilityLabel="Allow camera access"
disabled={!permission.canAskAgain}
onPress={requestPermission}
style={({ pressed }) => [
styles.button,
pressed && styles.buttonPressed,
!permission.canAskAgain && styles.buttonDisabled,
]}
>
<Text style={styles.buttonText}>Allow camera</Text>
</Pressable>
</View>
);
}
return (
<View style={styles.container}>
<CameraView
style={styles.camera}
barcodeScannerSettings={{ barcodeTypes: ['qr'] }}
onBarcodeScanned={
scan
? undefined
: ({ data, type }) => {
setScan({ data, type });
}
}
/>
<View style={styles.result}>
<Text accessibilityLiveRegion="polite" style={styles.title}>
{scan ? `Scanned ${scan.type}` : 'Point the camera at a QR code'}
</Text>
{scan ? <Text selectable>{scan.data}</Text> : null}
{scan ? (
<Pressable
accessibilityRole="button"
accessibilityLabel="Scan another QR code"
onPress={() => setScan(null)}
style={styles.button}
>
<Text style={styles.buttonText}>Scan again</Text>
</Pressable>
) : null}
</View>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#111827' },
center: { flex: 1, justifyContent: 'center', gap: 12, padding: 24 },
camera: { flex: 1 },
result: { gap: 12, padding: 16, backgroundColor: '#f9fafb' },
title: { fontSize: 18, fontWeight: '700', color: '#111827' },
body: { fontSize: 15, lineHeight: 22, color: '#374151' },
button: {
alignItems: 'center',
borderRadius: 8,
backgroundColor: '#2563eb',
paddingHorizontal: 16,
paddingVertical: 12,
},
buttonPressed: { opacity: 0.75 },
buttonDisabled: { backgroundColor: '#9ca3af' },
buttonText: { color: '#ffffff', fontWeight: '700' },
});
O componente pode ser copiado para um projeto Expo após instalar expo-camera. Texto de permissão e configuração nativa devem ficar no app config, porque algumas mudanças exigem reconstruir o app nativo.
// app.config.ts
import type { ConfigContext, ExpoConfig } from 'expo/config';
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: config.name ?? 'rn-claude-lab',
slug: config.slug ?? 'rn-claude-lab',
ios: {
...config.ios,
bundleIdentifier: 'com.example.rnclaudelab',
infoPlist: {
...config.ios?.infoPlist,
NSCameraUsageDescription: 'Scan QR codes for check-in.',
},
},
android: {
...config.android,
package: 'com.example.rnclaudelab',
permissions: ['CAMERA'],
},
plugins: ['expo-camera'],
});
Expo config plugins aplicam configurações nativas durante prebuild e builds nativos. A instrução prática para Claude Code é: “se editar app.config.ts, informe se precisa de development build”.
Trate erros do Metro como evidência
O segundo caso é Unable to resolve module, especialmente em monorepos. O troubleshooting oficial do Metro inclui reset de cache, mas isso não substitui diagnóstico.
Passe para Claude Code o erro completo, comando, package manager, layout do workspace e último arquivo movido. Em uma app Expo dentro de workspace, esta configuração pode ser necessária:
// metro.config.js
const path = require('path');
const { getDefaultConfig } = require('expo/metro-config');
const projectRoot = __dirname;
const workspaceRoot = path.resolve(projectRoot, '..');
const config = getDefaultConfig(projectRoot);
config.watchFolders = [workspaceRoot];
config.resolver.nodeModulesPaths = [
path.resolve(projectRoot, 'node_modules'),
path.resolve(workspaceRoot, 'node_modules'),
];
module.exports = config;
Não adicione isso às cegas. Uma app Expo comum de pacote único normalmente não precisa. Peça que Claude Code explique por que watchFolders é necessário e evite a mudança se a causa for caixa de letras, dependência ausente, Babel antigo ou import errado.
Planeje módulos nativos antes de editar nativo
O terceiro caso é conectar um SDK de fornecedor ou API de sistema. A documentação atual de Native Modules foca Turbo Native Modules e Codegen. Então uma boa tarefa começa pela interface TypeScript e só depois vai para Android e iOS.
Se Camera, SecureStore ou NetInfo do Expo resolvem, não escreva um módulo nativo próprio. Para terminal de pagamento, Bluetooth específico ou SDK interno de autenticação, peça um plano primeiro.
Implement a native bridge plan, not the full code yet.
Goal: expose a device serial reader to TypeScript.
First output:
1. TypeScript interface and error model.
2. Android/iOS files that would need edits.
3. Build commands for each platform.
4. Risks: permissions, threading, simulator limitations, release signing.
Do not edit ios/ or android/ until the plan is reviewed.
Isso parece mais lento no começo, mas acelera a revisão. Kotlin, Swift, Pods e Gradle precisam de fronteira clara.
Coloque testes e acessibilidade na mesma tarefa
As APIs de acessibilidade do React Native alimentam VoiceOver e TalkBack. Se uma View deve ser acessível, label e role fazem parte da implementação.
Comece com um teste para o estado sem permissão:
// __tests__/CameraQrCheck.test.tsx
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react-native';
import { CameraQrCheck } from '../components/CameraQrCheck';
const mockRequestPermission = jest.fn();
jest.mock('expo-camera', () => ({
CameraView: 'CameraView',
useCameraPermissions: () => [
{ granted: false, canAskAgain: true },
mockRequestPermission,
],
}));
describe('CameraQrCheck', () => {
beforeEach(() => {
mockRequestPermission.mockClear();
});
it('requests camera permission from the empty state', () => {
render(<CameraQrCheck />);
fireEvent.press(screen.getByText('Allow camera'));
expect(mockRequestPermission).toHaveBeenCalledTimes(1);
});
});
Depois valide em dispositivo ou emulador: diálogo de permissão, caminho de recusa, rotação, leitura com pouca luz e labels de leitor de tela. O handoff deve dizer qual plataforma foi testada. “Android emulator passed, iOS not tested” ajuda; “looks good” não.
Transforme release check em comandos
Dev Menu e LogBox são ferramentas de desenvolvimento; release builds se comportam de outro jeito. Claude Code deve devolver evidência de comandos.
npm run lint
npm test -- --runInBand
npx expo-doctor
npx expo start -c
adb devices
adb shell input keyevent 82
npx expo run:android
# macOS only:
npx expo run:ios
Com EAS Build, mantenha development, preview e production separados. Claude Code não deve mudar bundle identifier, Android package, signing files ou production profile sem pedido explícito.
Casos práticos e falhas comuns
O primeiro caso é um MVP novo em Expo. Login, QR scan, armazenamento seguro, API simples e fluxo interno podem ser separados em screens, hooks, tests e config. Se o app tem receita, analytics e CTA entram no critério de pronto.
O segundo é corrigir uma app bare existente. Erros de Metro, crash só no Android, texto de permissão no iOS e upgrade de SDK nativo funcionam bem com Claude Code se você exigir classificação do erro, diff mínimo e nota de rebuild nativo.
O terceiro é pesquisa antes de integrar SDK nativo. Pagamentos, saúde, Bluetooth e autenticação corporativa merecem uma tabela com OS suportados, permissões, risco de store review, limites de simulador e dispositivos de teste. Combine com o review workflow checklist e o guia TDD.
As falhas se repetem: Expo Go não prova release, reset de cache não é causa raiz, testes iOS/Android não podem ficar para o fim, acessibilidade não é remendo posterior, e comandos nativos precisam de motivo.
CTA: transforme o fluxo em template
Em React Native, o maior ganho vem de reutilizar project map, permission file, lista de comandos e checklist de review. Quem trabalha sozinho pode começar pela cheatsheet gratuita. Para prompts e materiais de setup, veja ClaudeCodeLab products. Times que precisam aplicar regras Expo/bare React Native, CI, release gates e treinamento em um repositório real devem usar Claude Code training and consultation.
Compare também com o guia React com Claude Code e o guia Flutter/Dart.
Depois de testar esse fluxo, Masa viu três hábitos reduzirem mais retrabalho: decidir Expo vs bare antes de implementar, pedir nota de development build quando app.config.ts muda e validar permissões e Metro primeiro no Android Emulator. Em recursos nativos como Camera e SecureStore, o limite de verificação importa mais que a velocidade de geração.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Workflow Obsidian para CLAUDE.md com Claude Code
Transforme notas de trabalho do Obsidian em notas operacionais CLAUDE.md para preservar contexto.
Claude Code Revenue CTA Routing: artigos para PDF, Gumroad e consultoria
Um fluxo com Claude Code para levar leitores ao PDF grátis, Gumroad ou consultoria conforme intenção.
Regras de handoff para equipes com Claude Code: evidências, permissões, rollback e receita
Formato prático para entregar trabalho do Claude Code com prova, permissões, rollback, PDF grátis, Gumroad e consultoria.