SecurePass: React Native Password Generator

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.