Taking a Step Back | How to Create Email Signup and Login Screens in React Native (Expo), ExpressJS, and Supabase

A How-to Guide on Creating Signup and Login Screens with JavaScript Frameworks and Tools you know

Programming Advice
The Coders’ Cave
5 min readFeb 3, 2024

--

We just created a simplified chat app in my last article, but that was a lot to unpack. Some details could easily get over looked as you try to organize all of the information. Not to mention that it was also a good sixteen minutes. Let’s just say, it’s a little overkill.

Let’s reel it back in and learn how to stack the building blocks, like using Express.js and React Native to create sign up and login screens. Sounds simple enough. Let’s dive in.

Photo by RealToughCandy.com from Pexels
Photo by RealToughCandy.com from Pexels

Getting Started

You will need to have Node.js installed and a Supabase account to follow along with this article.

To get started, let’s set up our backend (Express.js):

  • Initialize a Node.js project by running npm init -y
  • Install Express.js and necessary middleware packages by running npm install express body-parser
  • Install nodemon for convenience by running npm install nodemon — save-dev
  • Install Supabase by running npm install @supabase/supabase-js
  • And if you haven’t yet, install ngrok by running npm install -g ngrok
  • Lastly, create a file named index.js

Those are all the steps you need to follow to get started besides creating your react native app. I’m sure you know how to do that already. If not, just run npx create-expo-app project-name and read my article on stack navigation.

Review | Index.js, but only the Login and Signup Routes

We already did most of the work in my chat app article, but there are some changes we want to make if we want these screens to work with almost every React Native (Expo) app development project.

We could scrap the uuid package. We don’t need to generate a user_id anymore now that we aren’t creating the chat app discussed in that article. So, get rid of the user_id field in your Supabase users table. It should only have the fields id, email, and created_at.

This is the index.js code with only the routes for the login and signup:

const express = require('express');
const bodyParser = require('body-parser');
const { createClient } = require('@supabase/supabase-js')

const app = express();
const PORT = process.env.PORT || 3000;

const supabaseUrl = 'https://Supabase_URL.supabase.co';
const supabaseKey = 'Supabase_API_Key';
const supabase = createClient(supabaseUrl, supabaseKey);

app.use(bodyParser.json());

app.post('/api/signup', async (req, res) => {
const { email, password } = req.body;

try {
const { user, error } = await supabase.auth.signUp({
email,
password,
});

if (error) {
throw error;
}

const { data, error: insertError } = await supabase
.from('users')
.insert([{ email }]);

if (insertError) {
throw insertError;
}

res.json({ message: 'User registered successfully' });
} catch (error) {
console.error('Error registering user:', error);
res.status(400).json({ error: error.message });
}
});

app.post('/api/login', async (req, res) => {
const { email, password } = req.body;

try {
const { user, session, error } = await supabase.auth.signInWithPassword({
email,
password,
});

if (error) {
throw error;
}

const userData = await supabase
.from('users')
.select('*')
.eq('email', email)
.single();

if (!userData) {
throw new Error('User not found');
}

res.json({ user: userData, session });
} catch (error) {
res.status(400).json({ error: error.message });
}
});

app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});

Review | Login and Signup React Native (Expo) Screens

We could basically keep the same code from our login and signup screens from the chat app article.

Keep the same code for the login screen:

import React, { useState } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity } from 'react-native';

const LoginScreen = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

const handleLogin = () => {
fetch('https://ngrok_URL/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
})
.then(response => response.json())
.then(data => {
navigation.navigate('Dashboard', { user: data.user, user_id: data.user.data.user_id });
})
.catch(error => {
console.error('Error:', error);
});
};

return (
<View style={styles.container}>
<Text style={styles.heading}>Login</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={text => setEmail(text)}
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={text => setPassword(text)}
secureTextEntry={true}
/>
<TouchableOpacity style={styles.button} onPress={handleLogin}>
<Text style={styles.buttonText}>Login</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate('Register')}>
<Text style={styles.registerText}>Don't have an account? Register here</Text>
</TouchableOpacity>
</View>
);
};

export default LoginScreen;

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 20,
},
heading: {
fontSize: 24,
marginBottom: 20,
},
input: {
width: '100%',
height: 40,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 5,
marginBottom: 20,
paddingHorizontal: 10,
},
button: {
backgroundColor: 'blue',
padding: 10,
borderRadius: 5,
width: '100%',
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
},
registerText: {
marginTop: 20,
textDecorationLine: 'underline',
},
});

However, for our signup screen, we want to add a way to confirm the password by entering it again. The rest could stay the same:

import React, { useState } from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, Alert } from 'react-native';

const SignupScreen = ({ navigation }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');

const handleSignup = () => {
if (password !== confirmPassword) {
Alert.alert('Passwords do not match');
} else {
fetch('https://ngrok_URL/api/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
})
.then(response => response.json())
.then(data => {
navigation.navigate('Login');
})
.catch(error => {
console.error('Error:', error);
});
}
};

return (
<View style={styles.container}>
<Text style={styles.heading}>Register</Text>
<TextInput
style={styles.input}
placeholder="Email"
value={email}
onChangeText={text => setEmail(text)}
/>
<TextInput
style={styles.input}
placeholder="Password"
value={password}
onChangeText={text => setPassword(text)}
secureTextEntry={true}
/>
<TextInput
style={styles.input}
placeholder="Confirm Password"
value={confirmPassword}
onChangeText={text => setConfirmPassword(text)}
secureTextEntry={true}
/>
<TouchableOpacity style={styles.button} onPress={handleSignup}>
<Text style={styles.buttonText}>Register</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => navigation.navigate('Login')}>
<Text style={styles.loginText}>Already have an account? Login here</Text>
</TouchableOpacity>
</View>
);
};

export default SignupScreen;

const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
paddingHorizontal: 20,
},
heading: {
fontSize: 24,
marginBottom: 20,
},
input: {
width: '100%',
height: 40,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 5,
marginBottom: 20,
paddingHorizontal: 10,
},
button: {
backgroundColor: 'blue',
padding: 10,
borderRadius: 5,
width: '100%',
alignItems: 'center',
},
buttonText: {
color: 'white',
fontSize: 16,
},
loginText: {
marginTop: 20,
textDecorationLine: 'underline',
},
});

That wraps up our review on creating signup and login screens in React Native (Expo), Express.js, and Supabase. Clap if you found this article useful and respond if you have any questions.

Happy Coding!

--

--

Programming Advice
The Coders’ Cave

Who here has looked all over to solve simple programming problems or figure out how to do something in a programming language? Here's some programming advice.