Open In App

Create a Typing Speed Monitor App using React-Native

Last Updated : 08 Jun, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

A typing speed monitor app using React-Native is a software application designed to assess and track a user's typing speed and accuracy. These apps are often used for educational purposes, professional development, or personal improvement in typing skills.

Create-a-Typing-Speed-Monitor-App-using-React-Native

Playground

Note: This Section is to interact with the app which you are going to build.


Prerequisites

Step-by-Step Implementation

Step 1: Create a React Native Project

Now, create a project with the following command.

npx create-expo-app app-name --template

Note: Replace the app-name with your app name for example : react-native-demo-app

Next, you might be asked to choose a template. Select one based on your preference as shown in the image below. I am selecting the blank template because it will generate a minimal app that is as clean as an empty canvas in JavaScript.

It completes the project creation and displays a message: "Your Project is ready!" as shown in the image below.

Now go into your project folder, i.e., react-native-demo

cd app-name

Project Structure:

Step 2: Run  Application

Start the server by using the following command.

npx expo start

Then, the application will display a QR code.

  • For the Android users,
    • For the Android Emulator, press " a" as mentioned in the image below.
    • For the Physical Device, download the " Expo Go " app from the Play Store. Open the app, and you will see a button labeled " Scan QR Code. " Click that button and scan the QR code; it will automatically build the Android app on your device.
  • For iOS users, simply scan the QR code using the Camera app.
  • If you're using a web browser, it will provide a local host link that you can use as mentioned in the image below.

Step 3: Start Coding

- Approach to create Typing Speed Monitor App:

  • The typing speed monitor app is designed to monitor and evaluate the user's typing speed.
  • The app has a timer set initially to 60 seconds, which provides a time constraint for typing.
  • Typing speed is calculated based on the number of words typed within the given time.
  • Users can start typing by clicking the "Start Typing" button.
  • The app allows users to input text in a multiline TextInput component.
  • Users can end their typing session with the "End Typing" button, which stops the timer.
  • The app tracks errors made during typing by comparing the entered text with the provided sample.
  • Error count and typing speed are displayed once the typing session ends.
  • Users can reset the app using the "Reset" button.

Let's dive into the code in detail.

Import libraries:

Import required libraries at the top of the file.

JavaScript
// Import required React and React Native components and hooks
import { useState, useEffect } from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  ScrollView,
} from 'react-native';

StyleSheet:

Create a StyleSheet to style components like container, title, timer, etc.

JavaScript
// StyleSheet for UI styling
const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
    padding: 20,
    backgroundColor: '#f0f4f8',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginVertical: 20,
    textAlign: 'center',
    color: '#2c3e50',
  },
  timer: {
    fontSize: 20,
    backgroundColor: '#ffeaa7',
    padding: 8,
    borderRadius: 8,
    alignSelf: 'center',
    marginBottom: 15,
    color: '#2d3436',
    fontWeight: '600',
  },
  sampleBox: {
    backgroundColor: '#dfe6e9',
    borderRadius: 10,
    padding: 15,
    marginBottom: 15,
  },
  sampleText: {
    fontSize: 16,
    lineHeight: 22,
    color: '#34495e',
  },
  input: {
    height: 120,
    borderColor: '#74b9ff',
    borderWidth: 1.5,
    borderRadius: 10,
    backgroundColor: '#fff',
    padding: 12,
    fontSize: 16,
    marginBottom: 16,
  },
  buttonRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  startButton: {
    flex: 1,
    backgroundColor: '#00b894',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
    marginRight: 10,
  },
  endButton: {
    flex: 1,
    backgroundColor: '#d63031',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
    marginRight: 10,
  },
  resetButton: {
    flex: 1,
    backgroundColor: '#0984e3',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontWeight: '700',
    fontSize: 16,
  },
  resultBox: {
    backgroundColor: '#fff',
    borderRadius: 10,
    marginTop: 20,
    padding: 16,
    elevation: 2,
  },
  resultText: {
    fontSize: 18,
    color: '#2c3e50',
    marginBottom: 8,
  },
  bold: {
    fontWeight: 'bold',
    color: '#0984e3',
  },
});

Title Text:

This title explains what the app does. We use the text "🧠 Typing Speed Monitor" to show that the app is to monitor Typing Speed.

JavaScript
{/* Title-Text */}
<Text style={styles.title}>🧠 Typing Speed Monitor</Text>

- timer section:

This section includes,

  • timer: A state variable used to store the time.
  • Text Component: This component is used to display the time.
JavaScript
// Countdown timer (starts at 60 seconds)
const [timer, setTimer] = useState(60);

{/* Timer Display */}
<Text style={styles.timer}>⏱️ Time Left: {timer}s</Text>

- Sample Text Section:

This section includes,

  • sampleText: It is a variable that contains the sample text.
  • Text Component: This component is used to display the sample text.
JavaScript
// Static sample text for typing test
const sampleText = `GeeksforGeeks is a computer science portal that provides a wide variety of resources for programmers and computer science enthusiasts. It was created with the goal of providing high-quality educational content to help individuals learn and improve their programming and computer science skills.`;

{/* Box displaying the sample text */}
<View style={styles.sampleBox}>
    <Text style={styles.sampleText}>{sampleText}</Text>
</View>

- TextInput Section:

This section includes,

  • text & isTyping: These two state variables are used to hold the user-typed text and track if typing is in progress.
  • TextInput Component: This component is used to take input from the user.
JavaScript
// State to hold user-typed text
const [text, setText] = useState('');
// Boolean flag to track if typing is in progress
const [isTyping, setIsTyping] = useState(false);

{/* Text input for user to type in */}
<TextInput
    style={styles.input}
    multiline
    placeholder="Start typing here..."
    value={text}
    onChangeText={setText}
    editable={isTyping} // Only editable during typing
/>

- Buttons Section:

This Section includes,

  • useEffect: It is used to handle a countdown timer during typing.
  • handleStartTyping: This function is used to start a typing session.
  • handleEndTyping: This function is used to manually or auto end a typing session.
  • handleReset: This function is used to reset the entire typing session.
  • UI for Buttons: We have two buttons here,
    • Start/End Typing Button: This button acts as a Start button before the session starts or when the user has not started typing yet, and an End Button after the session starts or when the user starts typing.
    • Reset Button: This button is used to reset the entire typing session.
JavaScript
// useEffect to handle countdown timer during typing
useEffect(() => {
    let interval;
    if (isTyping) {
        interval = setInterval(() => {
        setTimer(prev => {
            if (prev === 1) {
                clearInterval(interval); // Stop the timer
                handleEndTyping(); // Automatically end typing
                return 60; // Reset timer for next session
            }
                return prev - 1; // Decrease timer by 1 each second
            });
        }, 1000); // Runs every second
    }
    // Clear interval when component unmounts or isTyping changes
    return () => clearInterval(interval);
}, [isTyping]);

// Function to start typing session
const handleStartTyping = () => {
    setText(''); // Clear previous text
    setStartTime(new Date().getTime()); // Capture current time as start time
    setIsTyping(true); // Enable typing mode
    setErrors(0); // Reset errors
};

// Function to manually or auto end typing session
const handleEndTyping = () => {
    setEndTime(new Date().getTime()); // Capture end time
    setIsTyping(false); // Disable typing mode
    calculateErrors(); // Check number of errors
};

// Function to reset the entire typing session
const handleReset = () => {
    setText('');
    setStartTime(0);
    setEndTime(0);
    setTypingSpeed(0);
    setIsTyping(false);
    setTimer(60);
    setErrors(0);
};


{/* Row of control buttons */}
<View style={styles.buttonRow}>
    {/* Start or End Typing Button */}
    <TouchableOpacity
        style={isTyping ? styles.endButton : styles.startButton}
        onPress={isTyping ? handleEndTyping : handleStartTyping}
    >
        <Text style={styles.buttonText}>
            {isTyping ? 'End Typing' : 'Start Typing'}
        </Text>
    </TouchableOpacity>
    
    {/* Reset Button */}
    <TouchableOpacity 
        style={styles.resetButton} 
        onPress={handleReset}
    >
        <Text style={styles.buttonText}>Reset</Text>
    </TouchableOpacity>
</View>

- Result Section:

This section includes,

  • useEffect: It is used to calculate WPM (Words Per Minute) once typing ends.
  • typingSpeed & errors: These state variables are used to store calculated typing speed and the Number of errors between typed text and the sample.
  • Text Component: This component is used to display Typing Speed and Error Count.
JavaScript
// useEffect to calculate WPM (Words Per Minute) once typing ends
useEffect(() => {
    if (startTime && endTime && text.trim().length > 0) {
        const seconds = (endTime - startTime) / 1000; // Calculate time in seconds
        const wpm = (text.trim().split(/\s+/).length / seconds) * 60; // WPM formula
        setTypingSpeed(wpm.toFixed(2)); // Save speed rounded to 2 decimal places
    }
}, [endTime]);

// State to store calculated typing speed
const [typingSpeed, setTypingSpeed] = useState(0);
// Number of errors between typed text and sample
const [errors, setErrors] = useState(0);

{/* Display result only if typing has ended */}
{endTime > 0 && (
<View style={styles.resultBox}>
    <Text style={styles.resultText}>
        🚀 Typing Speed: <Text style={styles.bold}>{typingSpeed}</Text> WPM
    </Text>
    <Text style={styles.resultText}>
         Error Count: <Text style={styles.bold}>{errors}</Text>
    </Text>
</View>

Now, wrap all design code with a ScrollView component to enable scrolling functionality, return it from the TypingSpeedMonitorApp component, and place all methods and useStates within the TypingSpeedMonitorApp component. Ensure to export the TypingSpeedMonitorApp.

Complete Source Code

App.js:

App.js
// Import required React and React Native components and hooks
import { useState, useEffect } from 'react';
import {
  View,
  Text,
  TextInput,
  TouchableOpacity,
  StyleSheet,
  ScrollView,
} from 'react-native';

// Main functional component
const TypingSpeedMonitorApp = () => {
  // Static sample text for typing test
  const sampleText = `GeeksforGeeks is a computer science portal that provides a wide variety of resources for programmers and computer science enthusiasts. It was created with the goal of providing high-quality educational content to help individuals learn and improve their programming and computer science skills.`;

  // State to hold user-typed text
  const [text, setText] = useState('');
  // State to store start time of typing
  const [startTime, setStartTime] = useState(0);
  // State to store end time of typing
  const [endTime, setEndTime] = useState(0);
  // State to store calculated typing speed
  const [typingSpeed, setTypingSpeed] = useState(0);
  // Boolean flag to track if typing is in progress
  const [isTyping, setIsTyping] = useState(false);
  // Countdown timer (starts at 60 seconds)
  const [timer, setTimer] = useState(60);
  // Number of errors between typed text and sample
  const [errors, setErrors] = useState(0);

  // useEffect to handle countdown timer during typing
  useEffect(() => {
    let interval;
    if (isTyping) {
      interval = setInterval(() => {
        setTimer(prev => {
          if (prev === 1) {
            clearInterval(interval); // Stop the timer
            handleEndTyping(); // Automatically end typing
            return 60; // Reset timer for next session
          }
          return prev - 1; // Decrease timer by 1 each second
        });
      }, 1000); // Runs every second
    }
    // Clear interval when component unmounts or isTyping changes
    return () => clearInterval(interval);
  }, [isTyping]);

  // useEffect to calculate WPM (Words Per Minute) once typing ends
  useEffect(() => {
    if (startTime && endTime && text.trim().length > 0) {
      const seconds = (endTime - startTime) / 1000; // Calculate time in seconds
      const wpm = (text.trim().split(/\s+/).length / seconds) * 60; // WPM formula
      setTypingSpeed(wpm.toFixed(2)); // Save speed rounded to 2 decimal places
    }
  }, [endTime]);

  // Function to start typing session
  const handleStartTyping = () => {
    setText(''); // Clear previous text
    setStartTime(new Date().getTime()); // Capture current time as start time
    setIsTyping(true); // Enable typing mode
    setErrors(0); // Reset errors
  };

  // Function to manually or auto end typing session
  const handleEndTyping = () => {
    setEndTime(new Date().getTime()); // Capture end time
    setIsTyping(false); // Disable typing mode
    calculateErrors(); // Check number of errors
  };

  // Compare typed words with sample words to count mistakes
  const calculateErrors = () => {
    const sampleWords = sampleText.split(/\s+/); // Break sample into words
    const enteredWords = text.split(/\s+/); // Break user input into words
    let count = 0;
    for (let i = 0; i < sampleWords.length; i++) {
      if (sampleWords[i] !== enteredWords[i]) count++; // Count mismatched words
    }
    setErrors(count); // Set error count
  };

  // Function to reset the entire typing session
  const handleReset = () => {
    setText('');
    setStartTime(0);
    setEndTime(0);
    setTypingSpeed(0);
    setIsTyping(false);
    setTimer(60);
    setErrors(0);
  };

  // JSX layout for the UI
  return (
    <ScrollView contentContainerStyle={styles.container}>
      {/* Title-Text */}
      <Text style={styles.title}>🧠 Typing Speed Monitor</Text>

      {/* Timer Display */}
      <Text style={styles.timer}>⏱️ Time Left: {timer}s</Text>

      {/* Box displaying the sample text */}
      <View style={styles.sampleBox}>
        <Text style={styles.sampleText}>{sampleText}</Text>
      </View>

      {/* Text input for user to type in */}
      <TextInput
        style={styles.input}
        multiline
        placeholder="Start typing here..."
        value={text}
        onChangeText={setText}
        editable={isTyping} // Only editable during typing
      />

      {/* Row of control buttons */}
      <View style={styles.buttonRow}>
        {/* Start or End Typing Button */}
        <TouchableOpacity
          style={isTyping ? styles.endButton : styles.startButton}
          onPress={isTyping ? handleEndTyping : handleStartTyping}
        >
          <Text style={styles.buttonText}>
            {isTyping ? 'End Typing' : 'Start Typing'}
          </Text>
        </TouchableOpacity>

        {/* Reset Button */}
        <TouchableOpacity style={styles.resetButton} onPress={handleReset}>
          <Text style={styles.buttonText}>Reset</Text>
        </TouchableOpacity>
      </View>

      {/* Display result only if typing has ended */}
      {endTime > 0 && (
        <View style={styles.resultBox}>
          <Text style={styles.resultText}>
            🚀 Typing Speed: <Text style={styles.bold}>{typingSpeed}</Text> WPM
          </Text>
          <Text style={styles.resultText}>
             Error Count: <Text style={styles.bold}>{errors}</Text>
          </Text>
        </View>
      )}
    </ScrollView>
  );
};

// StyleSheet for UI styling
const styles = StyleSheet.create({
  container: {
    flexGrow: 1,
    padding: 20,
    backgroundColor: '#f0f4f8',
  },
  title: {
    fontSize: 28,
    fontWeight: 'bold',
    marginVertical: 20,
    textAlign: 'center',
    color: '#2c3e50',
  },
  timer: {
    fontSize: 20,
    backgroundColor: '#ffeaa7',
    padding: 8,
    borderRadius: 8,
    alignSelf: 'center',
    marginBottom: 15,
    color: '#2d3436',
    fontWeight: '600',
  },
  sampleBox: {
    backgroundColor: '#dfe6e9',
    borderRadius: 10,
    padding: 15,
    marginBottom: 15,
  },
  sampleText: {
    fontSize: 16,
    lineHeight: 22,
    color: '#34495e',
  },
  input: {
    height: 120,
    borderColor: '#74b9ff',
    borderWidth: 1.5,
    borderRadius: 10,
    backgroundColor: '#fff',
    padding: 12,
    fontSize: 16,
    marginBottom: 16,
  },
  buttonRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
  },
  startButton: {
    flex: 1,
    backgroundColor: '#00b894',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
    marginRight: 10,
  },
  endButton: {
    flex: 1,
    backgroundColor: '#d63031',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
    marginRight: 10,
  },
  resetButton: {
    flex: 1,
    backgroundColor: '#0984e3',
    padding: 14,
    borderRadius: 10,
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontWeight: '700',
    fontSize: 16,
  },
  resultBox: {
    backgroundColor: '#fff',
    borderRadius: 10,
    marginTop: 20,
    padding: 16,
    elevation: 2,
  },
  resultText: {
    fontSize: 18,
    color: '#2c3e50',
    marginBottom: 8,
  },
  bold: {
    fontWeight: 'bold',
    color: '#0984e3',
  },
});

// Export the component as default
export default TypingSpeedMonitorApp;

Output:


Next Article

Similar Reads