Here's the error I'm getting:
iOS Bundled 50ms node_modules/expo-router/entry.js (1 module) ERROR Warning: Text strings must be rendered within a <Text> component. 8 | 9 | export default function SignInScreen() { > 10 | const [email, setEmail] = useState(''); | ^ 11 | const [password, setPassword] = useState(''); 12 | const [otp, setOtp] = useState(''); 13 | const [showForgotPassword, setShowForgotPassword] = useState(false); Call Stack SignInScreen (apps/rider_app/app/sign-in.tsx:10:37) NavigationRoot (apps/rider_app/app/_layout.tsx:15:47) KeyboardControllerView (<anonymous>) AuthProvider (apps/rider_app/contexts/AuthContext.tsx:98:40) RootLayout (apps/rider_app/app/_layout.tsx:41:28) RNCSafeAreaProvider (<anonymous>) App (<anonymous>) ErrorOverlay (<anonymous>)
I don't understand why it tells me about string having to be rendered within a Text
component, then it points to a state declaration.
Here's the screen where I'm getting the error:
import React, { useState } from 'react';
import {
SafeAreaView,
Text,
TextInput,
TouchableOpacity,
View
} from 'react-native';
import {
KeyboardAwareScrollView,
KeyboardToolbar
} from 'react-native-keyboard-controller';
import { Colors } from 'myplatform-ui';
import { globalStyles } from 'myplatform-ui';
import { useAuth } from '@/contexts/AuthContext';
export default function SignInScreen() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [otp, setOtp] = useState('');
const [showForgotPassword, setShowForgotPassword] = useState(false);
const [resetEmail, setResetEmail] = useState('');
const {
signIn,
verifyOtp,
sendPasswordResetOTP,
authError,
setAuthError
} = useAuth();
const handleSignIn = async () => {
if (!email) {
setAuthError(new Error('Please enter a valid email'));
return;
}
if (!password) {
setAuthError(new Error('Please enter a valid password'));
return;
}
await signIn(email, password);
};
const handleOtpSignIn = async () => {
if (!email) {
setAuthError(new Error('Please enter a valid email'));
return;
}
if (!otp) {
setAuthError(new Error('Please enter a valid OTP'));
return;
}
await verifyOtp(email, otp);
};
const handleSendResetOTP = async () => {
if (!resetEmail) {
setAuthError(new Error('Please enter a valid email'));
return;
}
const { error } = await sendPasswordResetOTP(resetEmail);
if (error) {
setAuthError(error);
} else {
// Show success message
setShowForgotPassword(false);
setAuthError(
new Error('Password reset OTP sent. Please check your inbox.')
);
}
};
return (
<>
<SafeAreaView style={{ flex: 1 }}>
<KeyboardAwareScrollView
style={{ flex: 1 }}
// properties inherited from ScrollView:
contentContainerStyle={{ flex: 1 }}
>
<View
style={[
globalStyles.mainContent,
globalStyles.centeredContainer,
{ padding: 32 }
]}
>
<Text
style={[
{
fontSize: 24,
fontWeight: 'bold',
marginBottom: 10,
textAlign: 'center'
}
]}
>
MyPlatform Rider App
</Text>
{!showForgotPassword && <>
{/* Email Section */}
<TextInput
style={[
globalStyles.textInput,
globalStyles.formElement,
{ marginBottom: 10 }
]}
placeholder="Email"
placeholderTextColor={Colors.grey300}
value={email}
onChangeText={(text) => {
setEmail(text);
setAuthError(null);
}}
autoCapitalize="none"
keyboardType="email-address"
/>
{/* Password Section */}
<Text
style={[
{
fontSize: 20,
fontWeight: 'bold',
marginBottom: 10,
textAlign: 'center'
}
]}
>
Password Sign In
</Text>
<TextInput
style={[
globalStyles.textInput,
globalStyles.formElement,
{ marginBottom: 10 }
]}
placeholder="Password"
placeholderTextColor={Colors.grey300}
value={password}
onChangeText={(text) => {
setPassword(text);
setAuthError(null);
}}
secureTextEntry
/>
<TouchableOpacity
style={[
globalStyles.button,
globalStyles.formElement,
{ padding: 12 }
]}
onPress={handleSignIn}
>
<Text style={globalStyles.buttonText}>Sign In</Text>
</TouchableOpacity>
</>}
{/* toggle reset password view */}
<TouchableOpacity
style={{ marginTop: 8, alignSelf: 'center' }}
onPress={() => {
setShowForgotPassword(!showForgotPassword);
setAuthError(null);
}}
>
<Text style={{ color: Colors.primary, }}>
{showForgotPassword
? 'Cancel Password Reset'
: 'Forgot password?'
}
</Text>
</TouchableOpacity>
{!showForgotPassword && <>
{/* OTP Section */}
<Text
style={[
{
fontSize: 20,
fontWeight: 'bold',
margin: 10,
textAlign: 'center'
}
]}
>
OTP Sign In
</Text>
<TextInput
style={[
globalStyles.textInput,
globalStyles.formElement,
{ marginBottom: 10 }
]}
placeholder="One Time Password"
placeholderTextColor={Colors.grey300}
value={otp}
onChangeText={(text) => {
setOtp(text);
setAuthError(null);
}}
keyboardType="number-pad"
/>
<TouchableOpacity
style={[
globalStyles.button,
globalStyles.formElement,
{ padding: 12 }
]}
onPress={handleOtpSignIn}
>
<Text style={globalStyles.buttonText}>Sign In via OTP</Text>
</TouchableOpacity>
{authError && (
<Text
style={{
fontSize: 16,
color: 'red',
marginTop: 10,
textAlign: 'center'
}}
>
{authError.message || 'An unknown error occurred'}
</Text>
)}
</>}
{/* Forgot Password Section */}
{showForgotPassword && (
<View
style={[
{
marginTop: 16,
padding: 15,
backgroundColor: Colors.grey100,
borderRadius: 8
},
globalStyles.formElement,
globalStyles.cardShadow
]}
>
<Text
style={{
fontSize: 18,
fontWeight: 'bold',
marginBottom: 10,
textAlign: 'center'
}}
>
Reset Password
</Text>
<TextInput
style={[
globalStyles.textInput,
globalStyles.formElement,
{ marginBottom: 10 }
]}
placeholder="Enter your email"
placeholderTextColor={Colors.grey300}
value={resetEmail}
onChangeText={(text) => {
setResetEmail(text);
setAuthError(null);
}}
autoCapitalize="none"
keyboardType="email-address"
/>
<TouchableOpacity
style={[
globalStyles.button,
globalStyles.formElement,
{ marginTop: 10 }
]}
onPress={handleSendResetOTP}
>
<Text style={globalStyles.buttonText}>Send OTP</Text>
</TouchableOpacity>
</View>
)}
</View>
</KeyboardAwareScrollView>
</SafeAreaView>
<KeyboardToolbar /> {/* had to put outside of SafeAreaView for it to work:
https://github.com/kirillzyusko/react-native-keyboard-controller/issues/415
*/}
</>
);
}
If I comment out everything related to email in the code, I get the same error but this time pointing at the opening parenthesis of the next (password) state.
The issue is not at the line you see. This error is coming from the JSX you are trying to return.
You see line 10 because code must be minified.
There could be many reasons for this error but here most likely it seems to be the comments:
eg: {/* Forgot Password Section */}
Or authError
is sometimes an empty string.
PS: For OP's case it is the comment.