SecurePass: React Native Password Generator
Building your first react-native password generator app
Today we're gonna build a react-native password generator with the help of Formik and Yup.
Setup an environment
If you are new to mobile development you need to make an environment in order to create your first native app. So there are 2 ways to set up an environment 1st is from the official docs Installation guide which will gonna takes an hour or two so we are gonna take a second approach by following a youtube tutorial on React native Windows installation created by Hitesh Choudhary.
let's get started
I hope you have created your environment now open your IDE (In case you don't know what's an IDE is an IDE is a software application that helps programmers develop software code efficiently ) For this tutorial we're using Visual Studio Code.
open the terminal in VScode and paste this command
npx react-native@latest init PasswordGenerator
This will create your application folder named PasswordGenerator. Now in terminal type
cd PasswordGenerator
you are in the main directory and now search for a file App.tsx
This will be our main file where we'll write all logic. Let's start our app by connecting your phone via cable (In the developer option in the mobile setting make sure Install via USB
,USB debugging
and USB debugging(security settings)
is enable)
run the following command
npx react-native start
Installing Library
In our app, there will be an input field where users will type their required password length so we need some type of validation for example: The minimum password length should be X
on our password length. Here Yup will help us to do that so let's install yup
via npm.
npm i yup
After installing Yup, we have to install another library called Formik. So, when you actually make your form in a regular way by using regular text inputs and all of that. You have to keep a track of lot of these things like when was the form changed, when was the form submitted and all of that and there is no such way of doing such things in react native. So every single event you have to keep track of it. What formic does it actually exposes those methods to you directly, so everything that happens in the form and the submit event handles, It then passes it on to a state given to you by the Formic itself and through that state you manage all these things.
npm i formik
and the last one is Bouncy-CheckBox
with this package we can decide whether to add upperCase, number or symbols characters in our generated password
npm i react-native-bouncy-checkbox
Creating Password Schema using Yup
let's import it first in App.tsx
import * as Yup from 'yup'
creating a password schema using Yup
const PasswordSchema = Yup.object().shape({
passwordLength: Yup.number().min(5,'Minimum length should be 5').max(20,'Maximum length should be 20').required('Length is required')
})
Defining States in React-Native
In React Native, useState
is a built-in hook that allows you to add and manage states within functional components.
State refers to data that can change over time and affects the rendering of your component. By using useState
, you can declare a state variable and update its value, which triggers a re-rendering of your component to reflect the updated state.
The useState
hook returns an array with two elements. The first element is the current value of the state variable, and the second element is a function that allows you to update the state variable. You can name these elements according to your preference.
Here's the syntax for using useState
:
const [stateVariable, setStateVariable] = useState(initialValue);
stateVariable
is the name of the state variable that you want to declare.setStateVariable
is the function that allows you to update the state variable.initialValue
is the initial value of the state variable.
By calling setStateVariable(newValue)
, you update the state variable to newValue
, and React take care of re-rendering the component to reflect the updated state.
Overall, useState
is a powerful tool for managing state in React Native, enabling you to create interactive and dynamic components.
So let's create the required states for our passwordGenerator app, In App.tsx
inside App function we are going to declare our states
import {Text} from 'react-native'
const App = () => {
const [password, setPassword] = useState('')
const [isPassGenerated, setIsPassGenerated] = useState(false)
const [lowerCase, setLowerCase] = useState(true)
const [upperCase, setUpperCase] = useState(false)
const [numbers, setNumbers] = useState(false)
const [symbols, setSymbols] = useState(false)
return(
<Text>Hello World</Text>
)
export default App
Logic to generate password
In App.tsx
, let's write a logic for creating a password
const createPassword = (characters:string, passwordLength:number)=>{
let result = ''
for(let i =0; i<passwordLength; i++){
const characterIndex = Math.round(Math.random() * characters.length)
result += characters[characterIndex]
}
return result
}
herein createPassword
takes 2 argument characters(we'll give character string through another function) and passwordLength
and creates a password
Let's create a string for the character, by default string will going to contain only lowercase characters but the user can decide whether he wants to add upperCase, numbers and symbols characters by simply clicking on the checkbox(we'll see this in a minute). So, let's create a function to generate a string of characters
const generatePasswordString = (passwordLength:number) => {
let characterList = ''
const upperCaseChars ='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
const lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz'
const numbersChars = '0123456789'
const symbolsChars = '!@#$%^&*'
//if user enable these checkboxes the character string will contain those characters
if(lowerCase) characterList += lowerCaseChars
if(upperCase) characterList += upperCaseChars
if(numbers) characterList += numbersChars
if(symbols) characterList += symbolsChars
//here we are giving string of charater to createPassword and storing the password in variable
const passwordResult = createPassword(characterList, passwordLength)
setPassword(passwordResult)
setIsPassGenerated(true)
}
Now let's write a logic for resetting the password
const resetPassword = () => {
setPassword('')
setIsPassGenerated(false)
setLowerCase(true)
setUpperCase(false)
setNumbers(false)
setSymbols(false)
}
Creating UI
By now, your App.tsx
should be looking like this
const App = () => {
const [password, setPassword] = useState('')
const [isPassGenerated, setIsPassGenerated] = useState(false)
const [lowerCase, setLowerCase] = useState(true)
const [upperCase, setUpperCase] = useState(false)
const [numbers, setNumbers] = useState(false)
const [symbols, setSymbols] = useState(false)
const generatePasswordString = (passwordLength:number) => {
let characterList = ''
const upperCaseChars ='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
const lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz'
const numbersChars = '0123456789'
const symbolsChars = '!@#$%^&*'
if(lowerCase) characterList += lowerCaseChars
if(upperCase) characterList += upperCaseChars
if(numbers) characterList += numbersChars
if(symbols) characterList += symbolsChars
const passwordResult = createPassword(characterList, passwordLength)
setPassword(passwordResult)
setIsPassGenerated(true)
}
const createPassword = (characters:string, passwordLength:number)=>{
let result = ''
for(let i =0; i<passwordLength; i++){
const characterIndex = Math.round(Math.random() * characters.length)
result += characters[characterIndex]
}
return result
}
const resetPassword = () => {
setPassword('')
setIsPassGenerated(false)
setLowerCase(true)
setUpperCase(false)
setNumbers(false)
setSymbols(false)
}
return (
<Text>Hello World</Text>
)
}
export default App
I have already created a styleSheet for you guys add this above export default App
const styles = StyleSheet.create({
appContainer: {
flex: 1,
},
formContainer: {
margin: 8,
padding: 8,
},
title: {
fontSize: 32,
fontWeight: '600',
marginBottom: 15,
},
subTitle: {
fontSize: 26,
fontWeight: '600',
marginBottom: 2,
},
description: {
color: '#758283',
marginBottom: 8,
},
heading: {
fontSize: 15,
},
inputWrapper: {
marginBottom: 15,
alignItems: 'center',
justifyContent: 'space-between',
flexDirection: 'row',
},
inputColumn: {
flexDirection: 'column',
},
inputStyle: {
padding: 8,
width: '30%',
borderWidth: 1,
borderRadius: 4,
borderColor: '#16213e',
},
errorText: {
fontSize: 12,
color: '#ff0d10',
},
formActions: {
flexDirection: 'row',
justifyContent: 'center',
},
primaryBtn: {
width: 120,
padding: 10,
borderRadius: 8,
marginHorizontal: 8,
backgroundColor: '#5DA3FA',
},
primaryBtnTxt: {
color: '#fff',
textAlign: 'center',
fontWeight: '700',
},
secondaryBtn: {
width: 120,
padding: 10,
borderRadius: 8,
marginHorizontal: 8,
backgroundColor: '#CAD5E2',
},
secondaryBtnTxt: {
textAlign: 'center',
},
card: {
padding: 12,
borderRadius: 6,
marginHorizontal: 12,
},
cardElevated: {
backgroundColor: '#ffffff',
elevation: 1,
shadowOffset: {
width: 1,
height: 1,
},
shadowColor: '#333',
shadowOpacity: 0.2,
shadowRadius: 2,
},
generatedPassword: {
fontSize: 22,
textAlign: 'center',
marginBottom: 12,
color:'#000'
},
})
okay, it's time to build the form with the help of Formik. We don't need to worry about events such onChange, onSubmit .Formik will take care of that
inside the return statement , paste this code
return (
<ScrollView keyboardShouldPersistTaps='handled'>
<SafeAreaView style={styles.appContainer}></SafeAreaView>
<View style={styles.formContainer}>
</View>
</ScrollView>
)
In React-Native
ScrollView
is a component that provides a scrollable view, allowing you to display content that exceeds the available screen space in a scrollable manner.
SafeAreaView
ensures that your content is displayed within the safe area of the device, preventing any overlap with system UI elements.
View
is a component that provides a container for other components, allowing you to group and structure your UI elements.
Let's build our Formik
form. I have already made some comments in the code for a better understanding
<Formik
//defining initial values , validation schema and on submit event when form get submitted
initialValues={{passwordLength: '' }}
validationSchema = {PasswordSchema}
onSubmit = {values=>{
console.log(values);
generatePasswordString(+values.passwordLength)
}}>
{({values,errors,touched,isValid,handleChange,handleSubmit,handleReset}) => (
<>
<View style={styles.inputWrapper}>
<View style={styles.inputColumn}>
<Text style={styles.heading}>Password Length</Text>
{/* Error message in case if user validation failes such as minimum/maximum length or length is required errors */}
{touched.passwordLength && errors.passwordLength && (
<Text style={styles.errorText}>{errors.passwordLength}</Text>
)}
</View>
{/*In values.passwordLength passwordLength is coming from intialValues*/}
{/* onChangeText use an event hook (handleChange()) given by Formik handleChange take an argument which should be string
*/}
<TextInput style={styles.inputStyle} value={values.passwordLength} onChangeText={handleChange('passwordLength')} placeholder='Ex. 8' keyboardType='numeric'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include LowerCase</Text>
<BouncyCheckbox disableBuiltInState isChecked={lowerCase} onPress={()=>setLowerCase(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include UpperCase</Text>
<BouncyCheckbox disableBuiltInState isChecked={upperCase} onPress={()=>setUpperCase(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include Numbers</Text>
<BouncyCheckbox disableBuiltInState isChecked={numbers} onPress={()=>setNumbers(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include Symbols</Text>
<BouncyCheckbox disableBuiltInState isChecked={symbols} onPress={()=>setSymbols(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.formActions}>
{/*TouchableOpacity is a React Native component that enables touch interactions and provides visual feedback when pressed.*/}
<TouchableOpacity disabled={!isValid} style={styles.primaryBtn} onPress={()=>handleSubmit()}><Text style={styles.primaryBtnTxt}>Generate Password</Text></TouchableOpacity>
<TouchableOpacity style={styles.primaryBtn} onPress={()=>{handleReset(); resetPassword()}}><Text style={styles.primaryBtnTxt}>Reset</Text></TouchableOpacity>
</View>
</>
)}
</Formik>
By now your code should be looking like this
return(
<ScrollView keyboardShouldPersistTaps='handled'>
<SafeAreaView style={styles.appContainer}></SafeAreaView>
<View style={styles.formContainer}>
<Formik>
{/*Formik Code*/}
</Formik>
</View>
</ScrollView>
)
now, let's create a container for viewing generated password, below </View> Add this code
//using Ternary operator--> (condition) ? "if true this section will get rendered" : "if false this section will get rendered"
{isPassGenerated ? (
<View style={[styles.card, styles.cardElevated]}>
<Text style={styles.subTitle}>Result:</Text>
<Text style={styles.description}>Long Press to copy</Text>
<Text selectable style={styles.generatedPassword}>{password}</Text>
</View>
):null}
final code
import { SafeAreaView, ScrollView, StyleSheet, Text, TextInput, TouchableOpacity, View } from 'react-native'
import React, { useState } from 'react'
//Form Validation
import * as Yup from 'yup'
import { Form, Formik } from 'formik'
import BouncyCheckbox from 'react-native-bouncy-checkbox'
const PasswordSchema = Yup.object().shape({
passwordLength: Yup.number().min(5,'Minimum should be 5').max(20,'Maximum should be 5').required('Length is required')
})
const App = () => {
const [password, setPassword] = useState('')
const [isPassGenerated, setIsPassGenerated] = useState(false)
const [lowerCase, setLowerCase] = useState(true)
const [upperCase, setUpperCase] = useState(false)
const [numbers, setNumbers] = useState(false)
const [symbols, setSymbols] = useState(false)
const generatePasswordString = (passwordLength:number) => {
let characterList = ''
const upperCaseChars ='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
const lowerCaseChars = 'abcdefghijklmnopqrstuvwxyz'
const numbersChars = '0123456789'
const symbolsChars = '!@#$%^&*'
if(lowerCase) characterList += lowerCaseChars
if(upperCase) characterList += upperCaseChars
if(numbers) characterList += numbersChars
if(symbols) characterList += symbolsChars
const passwordResult = createPassword(characterList, passwordLength)
setPassword(passwordResult)
setIsPassGenerated(true)
}
const createPassword = (characters:string, passwordLength:number)=>{
let result = ''
for(let i =0; i<passwordLength; i++){
const characterIndex = Math.round(Math.random() * characters.length)
result += characters[characterIndex]
}
return result
}
const resetPassword = () => {
setPassword('')
setIsPassGenerated(false)
setLowerCase(true)
setUpperCase(false)
setNumbers(false)
setSymbols(false)
}
return (
<ScrollView keyboardShouldPersistTaps='handled'>
<SafeAreaView style={styles.appContainer}></SafeAreaView>
<View style={styles.formContainer}>
<Formik
initialValues={{passwordLength: '' }}
validationSchema = {PasswordSchema}
onSubmit = {values=>{
console.log(values);
generatePasswordString(+values.passwordLength) //TODO
}}>
{({values,errors,touched,isValid,handleChange,handleSubmit,handleReset}) => (
<>
{/* */}
<View style={styles.inputWrapper}>
<View style={styles.inputColumn}>
<Text style={styles.heading}>Password Length</Text>
{touched.passwordLength && errors.passwordLength && (
<Text style={styles.errorText}>{errors.passwordLength}</Text>
)}
</View>
<TextInput style={styles.inputStyle} value={values.passwordLength} onChangeText={handleChange('passwordLength')} placeholder='Ex. 8' keyboardType='numeric'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include LowerCase</Text>
<BouncyCheckbox disableBuiltInState isChecked={lowerCase} onPress={()=>setLowerCase(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include UpperCase</Text>
<BouncyCheckbox disableBuiltInState isChecked={upperCase} onPress={()=>setUpperCase(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include Numbers</Text>
<BouncyCheckbox disableBuiltInState isChecked={numbers} onPress={()=>setNumbers(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.heading}>Include Symbols</Text>
<BouncyCheckbox disableBuiltInState isChecked={symbols} onPress={()=>setSymbols(prev=>!prev)} fillColor='#29AB87'/>
</View>
<View style={styles.formActions}>
<TouchableOpacity disabled={!isValid} style={styles.primaryBtn} onPress={()=>handleSubmit()}><Text style={styles.primaryBtnTxt}>Generate Password</Text></TouchableOpacity>
<TouchableOpacity style={styles.primaryBtn} onPress={()=>{handleReset(); resetPassword()}}><Text style={styles.primaryBtnTxt}>Reset</Text></TouchableOpacity>
</View>
</>
)}
</Formik>
</View>
{isPassGenerated ? (
<View style={[styles.card, styles.cardElevated]}>
<Text style={styles.subTitle}>Result:</Text>
<Text style={styles.description}>Long Press to copy</Text>
<Text selectable style={styles.generatedPassword}>{password}</Text>
</View>
):null}
</ScrollView>
)
}
export default App
const styles = StyleSheet.create({
//stylesheets is already given in above no need to make this code more longer
})
App view
congratulation🎉 , you have built your first react-native app. If you get stuck at any moment feel free to comment I will help you.