How to Make Stopwatch in React

In this article, we are going to make a stopwatch in react. Like a basic stopwatch we will have a display to show time, then we will have basic buttons for start, stop, resume and reset. As soon we will hit the start button, timer will be started and two buttons will appear stop and reset. By clicking stop, the timer will be stopped, and a resume button will appear. Lastly, the reset button will reset the timer back to 0 again.

So basically this is going to be a beginner-friendly project, so let’s make this project step-by-step.

Pre-requisites to Make Stopwatch in React

  • Basic knowledge of ReactJS.
  • Basic knowledge of React hooks.
  • Basic knowledge of React props.
  • Good knowledge of React Components.

Creating App Component

Let us start with App.js component, we have imported DisplayComponent.js and BtnComponent.js components, in which we will add structure for button and display. Then we also imported useState hook, so we can make use of it to get variable time. Now we have created state for time with initial 0 values for ms,s,m,h, which is actually for millisecond, second,minute and hour.

Then in return statement, we have called these two components in a div. And we have passed time as a prop for <DisplayComponent/>. Okay, so this is not for all for App.js we will do some changes as we progress in the Application.

import React, {useState} from 'react';
import DisplayComponent from './Components/DisplayComponent';
import BtnComponent from './Components/BtnComponent';
import './App.css';

function App() {
  const [time, setTime] = useState({ms:0, s:0, m:0, h:0});

return (
     <div className="clock-holder">
    <div className="main-section">
          <div className="stopwatch">
               <DisplayComponent time={time}/>
               <BtnComponent/>
          </div>
     </div>
    </div>
  );
}

export default App;

Setting up Display Component

In this component, we have to set up our display for timer. Here in return(), we have added a function call h(), in this function we have just check where hour (here ‘h’) is 0 or not, if yes then we will not show hour in our timer. If h is greater than 0 then we will show hour and return as span.

Then we have added 3 span tags for minute, millisecond and for second. in all these span, we have added functionality to add a 0 before the actual timer if any of these time is below 10. Here we have used props to get the values from App.js component, and the changes made in App component directly reflect on here.

import React from 'react';

function DisplayComponent(props) {
  const h = () => {
     if(props.time.h === 0){
       return '';
     }else {
       return <span>{(props.time.h >= 10)? props.time.h : "0"+ props.time.h}</span>;
     }
  }
  return (
    <div>
       {h()}&nbsp;&nbsp;
       <span>{(props.time.m >= 10)? props.time.m : "0"+ props.time.m}</span>&nbsp;:&nbsp;
       <span>{(props.time.s >= 10)? props.time.s : "0"+ props.time.s}</span>&nbsp;:&nbsp;
       <span>{(props.time.ms >= 10)? props.time.ms : "0"+ props.time.ms}</span>
    </div>
  );
}

export default DisplayComponent;
Stopwatch in React

Adding Functionalities of Stopwatch

Now let’s customize App component a little bit, here we have added some props in <BtnComponent/> like start, stop, resume, reset and status with all perspective function calls. We will define these functions one by one, but before that we have added 2 more states, one for interval and other for status with initial values.

After that in start function, we have called a run() function, and we have updated status with value 1, also we have updated value of setInterv with run function for 10ms. So each 10ms run, the function will be called. In this run function we have updated the values of hour, minutes and seconds if it hits the certain conditions. For these condition checking, we have added some temporary variable to store the values of parameters of time state

For stop(), we have just updated the status value with 2 and for reset() function we will clear out the timer and set our time status back to 0. Lastly, for resume we just called start() function to run again the timer.

const [time, setTime] = useState({ms:0, s:0, m:0, h:0});
  const [interv, setInterv] = useState();
  const [status, setStatus] = useState(0);
  // Not started = 0
  // started = 1
  // stopped = 2

  const start = () => {
    run();
    setStatus(1);
    setInterv(setInterval(run, 10));
  };

  var updatedMs = time.ms, updatedS = time.s, updatedM = time.m, updatedH = time.h;

  const run = () => {
    if(updatedM === 60){
      updatedH++;
      updatedM = 0;
    }
    if(updatedS === 60){
      updatedM++;
      updatedS = 0;
    }
    if(updatedMs === 100){
      updatedS++;
      updatedMs = 0;
    }
    updatedMs++;
    return setTime({ms:updatedMs, s:updatedS, m:updatedM, h:updatedH});
  };

  const stop = () => {
    clearInterval(interv);
    setStatus(2);
  };

  const reset = () => {
    clearInterval(interv);
    setStatus(0);
    setTime({ms:0, s:0, m:0, h:0})
  };

  const resume = () => start();


  return (
    <div className="main-section">
     <div className="clock-holder">
          <div className="stopwatch">
               <DisplayComponent time={time}/>
               <BtnComponent status={status} resume={resume} reset={reset} stop={stop} start={start}/>
          </div>
     </div>
    </div>
  );
}

Managing The Timer Props

Lastly, we have to make use of these props in <BtnComponent/>, here we are checking the status value from status prop, If it is 0 that means stopwatch will be in the initial state and at that time we will create a button named Start and also add a onClick listener, in which we will call the start function from the App component using props.start.

If the status value is 1 then we will add the Stop and Reset button, and we will call the props.stop and prop.reset functions. Remember that, initially status will be 0, so we will get Start button but as soon status becomes 1 then we will get Stop and Reset button and previous button (Start) won’t be shown up.

Similarly, for props.status == 2, we will add the resume and reset button with props.resume and props.reset function call.

import React from 'react';

function BtnComponent(props) {
  return (
    <div>
      {(props.status === 0)? 
        <button className="stopwatch-btn stopwatch-btn-gre"
        onClick={props.start}>Start</button> : ""
      }

      {(props.status === 1)? 
        <div>
          <button className="stopwatch-btn stopwatch-btn-red"
                  onClick={props.stop}>Stop</button>
          <button className="stopwatch-btn stopwatch-btn-yel"
                  onClick={props.reset}>Reset</button>
        </div> : ""
      }

     {(props.status === 2)? 
        <div>
          <button className="stopwatch-btn stopwatch-btn-gre"
                  onClick={props.resume}>Resume</button>
          <button className="stopwatch-btn stopwatch-btn-yel"
                  onClick={props.reset}>Reset</button>
        </div> : ""
      }
     
    </div>
  );
}

export default BtnComponent;
Stopwatch in React

Customizing The Application

Now our structure is pretty good, and we have added almost everything but before going to next move, let’s add some colors and customize this calculator, so this can looks like calculator atleast. I won’t go deep in CSS because CSS is actually depended on you and this is not that much hard to put color or something. So if you want same CSS then use below CSS.

App.css

body {
  background:#2C3E50;
}

* {
  padding: 0px;
  margin: 0px;
  box-sizing: border-box;
}

.main-section {
  background: transparent;
  max-width: 600px;
  width: 90%;
  height: 500px;
  margin: 30px auto;
}

.clock-holder {
  width: 100%;
  background: #fff;
  margin: 30px auto;
  position: relative;
}


.stopwatch {
  padding: 60px 0px;
  text-align: center;
}
.stopwatch span{
  background:#00ABA9;
  color: #fff;
  display: inline-block;
  font-family: monospace;
  font-size: 22px;
  padding: 20px;
  border-radius: 5px;
  width: 70px;
}

.stopwatch .stopwatch-btn {
  background: #fff;
  margin-top: 30px;
  margin-left: 5px;
  border: none;
  padding: 12px 20px;
  font-size: 16px;
  text-transform: uppercase;
  width: 150px;
  transition: background 1s;
  cursor: pointer;
}
.stopwatch .stopwatch-btn-red{
  border: 2px solid #C85454;
  color: #C85454;
}
.stopwatch .stopwatch-btn-yel{
  border: 2px solid #FFC900;
  color: #FFC900;
}

.stopwatch .stopwatch-btn-gre{
  border: 2px solid #00ABA9;
  color: #00ABA9;
}
.stopwatch .stopwatch-btn-red:hover {
  background: #C85454;
  border: 2px solid #C85454;
  color: #fff;
}
.stopwatch .stopwatch-btn-yel:hover {
  background: #FFC900;
  border: 2px solid #FFC900;
  color: #fff;
}
.stopwatch .stopwatch-btn-gre:hover {
  background: #00ABA9;
  border: 2px solid #00ABA9;
  color: #fff;
}

Full Source Code to Make Stopwatch in React

index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

App.js

import React, {useState} from 'react';
import DisplayComponent from './Components/DisplayComponent';
import BtnComponent from './Components/BtnComponent';
import './App.css';

function App() {
  const [time, setTime] = useState({ms:0, s:0, m:0, h:0});
  const [interv, setInterv] = useState();
  const [status, setStatus] = useState(0);
  // Not started = 0
  // started = 1
  // stopped = 2

  const start = () => {
    run();
    setStatus(1);
    setInterv(setInterval(run, 10));
  };

  var updatedMs = time.ms, updatedS = time.s, updatedM = time.m, updatedH = time.h;

  const run = () => {
    if(updatedM === 60){
      updatedH++;
      updatedM = 0;
    }
    if(updatedS === 60){
      updatedM++;
      updatedS = 0;
    }
    if(updatedMs === 100){
      updatedS++;
      updatedMs = 0;
    }
    updatedMs++;
    return setTime({ms:updatedMs, s:updatedS, m:updatedM, h:updatedH});
  };

  const stop = () => {
    clearInterval(interv);
    setStatus(2);
  };

  const reset = () => {
    clearInterval(interv);
    setStatus(0);
    setTime({ms:0, s:0, m:0, h:0})
  };

  const resume = () => start();


  return (
    <div className="main-section">
     <div className="clock-holder">
          <div className="stopwatch">
               <DisplayComponent time={time}/>
               <BtnComponent status={status} resume={resume} reset={reset} stop={stop} start={start}/>
          </div>
     </div>
    </div>
  );
}

export default App;

DisplayComponent.js

import React from 'react';

function DisplayComponent(props) {
  const h = () => {
     if(props.time.h === 0){
       return '';
     }else {
       return <span>{(props.time.h >= 10)? props.time.h : "0"+ props.time.h}</span>;
     }
  }
  return (
    <div>
       {h()}&nbsp;&nbsp;
       <span>{(props.time.m >= 10)? props.time.m : "0"+ props.time.m}</span>&nbsp;:&nbsp;
       <span>{(props.time.s >= 10)? props.time.s : "0"+ props.time.s}</span>&nbsp;:&nbsp;
       <span>{(props.time.ms >= 10)? props.time.ms : "0"+ props.time.ms}</span>
    </div>
  );
}

export default DisplayComponent;

BtnComponent.js

import React from 'react';

function BtnComponent(props) {
  return (
    <div>
      {(props.status === 0)? 
        <button className="stopwatch-btn stopwatch-btn-gre"
        onClick={props.start}>Start</button> : ""
      }

      {(props.status === 1)? 
        <div>
          <button className="stopwatch-btn stopwatch-btn-red"
                  onClick={props.stop}>Stop</button>
          <button className="stopwatch-btn stopwatch-btn-yel"
                  onClick={props.reset}>Reset</button>
        </div> : ""
      }

     {(props.status === 2)? 
        <div>
          <button className="stopwatch-btn stopwatch-btn-gre"
                  onClick={props.resume}>Resume</button>
          <button className="stopwatch-btn stopwatch-btn-yel"
                  onClick={props.reset}>Reset</button>
        </div> : ""
      }
     
    </div>
  );
}

export default BtnComponent;

App.css

body {
  background:#2C3E50;
}

* {
  padding: 0px;
  margin: 0px;
  box-sizing: border-box;
}

.main-section {
  background: transparent;
  max-width: 600px;
  width: 90%;
  height: 500px;
  margin: 30px auto;
}

.clock-holder {
  width: 100%;
  background: #fff;
  margin: 30px auto;
  position: relative;
}


.stopwatch {
  padding: 60px 0px;
  text-align: center;
}
.stopwatch span{
  background:#00ABA9;
  color: #fff;
  display: inline-block;
  font-family: monospace;
  font-size: 22px;
  padding: 20px;
  border-radius: 5px;
  width: 70px;
}

.stopwatch .stopwatch-btn {
  background: #fff;
  margin-top: 30px;
  margin-left: 5px;
  border: none;
  padding: 12px 20px;
  font-size: 16px;
  text-transform: uppercase;
  width: 150px;
  transition: background 1s;
  cursor: pointer;
}
.stopwatch .stopwatch-btn-red{
  border: 2px solid #C85454;
  color: #C85454;
}
.stopwatch .stopwatch-btn-yel{
  border: 2px solid #FFC900;
  color: #FFC900;
}

.stopwatch .stopwatch-btn-gre{
  border: 2px solid #00ABA9;
  color: #00ABA9;
}
.stopwatch .stopwatch-btn-red:hover {
  background: #C85454;
  border: 2px solid #C85454;
  color: #fff;
}
.stopwatch .stopwatch-btn-yel:hover {
  background: #FFC900;
  border: 2px solid #FFC900;
  color: #fff;
}
.stopwatch .stopwatch-btn-gre:hover {
  background: #00ABA9;
  border: 2px solid #00ABA9;
  color: #fff;
}

Output

Stopwatch in React

Check out video reference here:

You may also like:

Reactjs Guru
Reactjs Guru

Welcome to React Guru, your ultimate destination for all things React.js! Whether you're a beginner taking your first steps or an experienced developer seeking advanced insights.

React tips & tutorials delivered to your inbox

Don't miss out on the latest insights, tutorials, and updates from the world of ReactJs! Our newsletter delivers valuable content directly to your inbox, keeping you informed and inspired.

Leave a Reply

Your email address will not be published. Required fields are marked *