Build a Calculator in React: Complete Step-by-Step Guide

Implementation of React Calculator App

Set up the environment

First, open a terminal and create the React project using Create React App:

npx create-react-app calculator

cd calculator

npm start

This sets up a new folder named calculator with all required files. Running npm start launches the development server on http://localhost:3000/. You should see the default React page.

Now that React is running, delete unused boilerplate files. In the src/ folder, keep only these files:

  • index.js (entry point)
  • App.js (root component)
  • App.css (our styles; can rename or reuse)
  • index.css (global styles)

Remove extra files like App.test.js, logo.svg, etc. We’ll build our calculator inside App.js and its child components.

Create Application Structure (Directory Structure)

Let’s organize our project. Inside the created folder:

calculator/                   # Project root

├── node_modules/            # Installed packages

├── public/

│   └── index.html           # HTML template

└── src/

├── components/          # (create this folder)

│   ├── Display.js       # Displays calculator output

│   └── Button.js        # Represents each calculator button

├── App.js               # Main React component

├── index.js             # React DOM render

├── index.css            # Global CSS

└── App.css              # App-specific CSS

In this structure, we added a components/ folder under src to hold child components. Display.js will handle the screen, and Button.js will render a styled button. App.js (in src/) will import these components and manage state. index.js simply renders <App /> into the root HTML.

Steps to Build Calculator App

Now we implement the code file by file. After each code snippet, we provide a brief explanation

1. src/index.js – This is the entry point that renders the React app into the DOM.

// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';   // global styles
import App from './App';

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

Here we import React and ReactDOM, as well as our global stylesheet (index.css). We render the <App /> component into the <div id="root"></div> in public/index.html. This connects React to the HTML page.

2. src/components/Display.js – 

This component shows the calculator screen. Create a folder called “components” and in that create a file called “Display.js” and then add the following:

import React from 'react';
import '../App.css';  // or a Display.css if preferred

export default function Display({ value }) {
  return (
    <div className="display">
      {value}
    </div>
  );
}

Display is a simple functional component that takes a value prop and shows it inside a div. The className "display" is styled via CSS to look like a calculator screen. This component is reusable for showing any text or number. We use {value} (in curly braces) because value is a JavaScript variable (the current display string).

3. src/components/Button.js – This component renders a single calculator button.

import React from 'react';
import '../App.css';  // or Button.css if separate

export default function Button({ value, onClick, className }) {
  return (
    <button
      className={`btn ${className}`}
      onClick={() => onClick(value)}
    >
      {value}
    </button>
  );
}

Button receives props: value (the label, e.g. "7" or "+"), an onClick function, and an optional className for styling (e.g. to color operator buttons). The onClick={() => onClick(value)} means when this button is clicked, it calls the parent handler with this button’s value.

We wrap the value in curly braces because it’s a dynamic prop. All buttons use the CSS class "btn", plus any extra class passed (for styling). The button label is {value}.

4. src/App.js – Main application logic and UI layout.

import React, { useState } from 'react';
import './App.css';
import Display from './components/Display';
import Button from './components/Button';
function App() {
  const [display, setDisplay] = useState("0");
  const [operator, setOperator] = useState(null);
  const [previousValue, setPreviousValue] = useState(null);

  const handleClick = (value) => {
    if (!isNaN(value) || value === '.') {
      // Number or decimal pressed
      setDisplay(prev =>
        prev === "0" && value !== '.' ? value : prev + value
      );
    } else if (value === 'AC') {
      // All Clear
      setDisplay("0");
      setOperator(null);
      setPreviousValue(null);
    } else if (value === 'C') {
      // Backspace (clear last character)
      setDisplay(prev => prev.length > 1 ? prev.slice(0, -1) : "0");
    } else if (value === '%') {
      // Percentage: divide current value by 100
      setDisplay(prev => (parseFloat(prev) / 100).toString());
    } else if (value === '±') {
      // Negate: multiply by -1
      setDisplay(prev => (parseFloat(prev) * -1).toString());
    } else if (['+', '-', '*', '/'].includes(value)) {
      // Operator pressed
      setOperator(value);
      setPreviousValue(display);
      setDisplay("0");
    } else if (value === '=') {
      // Calculate result
      if (operator && previousValue != null) {
        const a = parseFloat(previousValue);
        const b = parseFloat(display);
        let result = 0;
        if (operator === '+') result = a + b;
        if (operator === '-') result = a - b;
        if (operator === '*') result = a * b;
        if (operator === '/') result = a / b;
        setDisplay(result.toString());
        setOperator(null);
        setPreviousValue(null);
      }
    }
  };
  return (
    <div className="calculator">
      <Display value={display} />
      <div className="keypad">
        <Button value="AC" onClick={handleClick} className="clear" />
        <Button value="C" onClick={handleClick} className="clear" />
        <Button value="%" onClick={handleClick} />
        <Button value="/" onClick={handleClick} className="operator" />
        <Button value="7" onClick={handleClick} />
        <Button value="8" onClick={handleClick} />
        <Button value="9" onClick={handleClick} />
        <Button value="*" onClick={handleClick} className="operator" />
        <Button value="4" onClick={handleClick} />
        <Button value="5" onClick={handleClick} />
        <Button value="6" onClick={handleClick} />
        <Button value="-" onClick={handleClick} className="operator" />
        <Button value="1" onClick={handleClick} />
        <Button value="2" onClick={handleClick} />
        <Button value="3" onClick={handleClick} />
        <Button value="+" onClick={handleClick} className="operator" />
        <Button value="0" onClick={handleClick} className="zero" />
        <Button value="." onClick={handleClick} />
        <Button value="±" onClick={handleClick} />
        <Button value="=" onClick={handleClick} className="equals" />
      </div>
    </div>
  );
}
export default App;

Explanation: In App, we use three pieces of state: display (the current screen text), operator (the last operator pressed), and previousValue (the stored number before the operator). The handleClick function takes a button value and updates state accordingly:

  • If value is a digit or '.', we append it to display (or replace "0" if that’s all there is).
  • If AC is clicked, we reset everything to initial.
  • If C is clicked, we remove the last character (or reset to "0" if it was one digit).
  • If % or ± is clicked, we convert the current display accordingly (percent = divide by 100, negate = multiply by -1).
  • If an operator (+ - * /) is clicked, we store the current display in previousValue, remember the operator, and reset display to "0" for the next input.
  • If = is clicked, we perform the calculation between previousValue and display using the stored operator and show the result.

Finally, the JSX lays out the calculator: a <Display> component at top showing display, and a grid of <Button> components for all keys. We assign special CSS classes (e.g. "operator", "clear", "equals") for styling. The number buttons and . use default styling. Clicking any button calls handleClick with its label.

5. CSS Styling – We add styles in App.css (and index.css) for layout and look:

/* index.css: Global styles */
body {
  margin: 20px;
  background-color: #f0f0f0;
  font-family: Arial, sans-serif;
}

/* App.css: Calculator styling */
.calculator {
  width: 320px;
  margin: 0 auto;
  background: #222;
  padding: 20px;
  border-radius: 8px;
  box-shadow: 0 0 10px #0008;
}
.display {
  background: #444;
  color: #fff;
  font-size: 2em;
  text-align: right;
  padding: 10px;
  margin-bottom: 10px;
  border-radius: 4px;
  min-height: 50px;
}
.keypad {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 10px;
}
.btn {
  font-size: 1.2em;
  padding: 15px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  background: #333;
  color: #eee;
  transition: background 0.2s;
}
.btn:hover {
  background: #555;
}
.operator {
  background: #ff9500;
  color: #fff;
}
.equals {
  background: #4caf50;
  color: #fff;
  grid-column: span 2;
}
.clear {
  background: #d32f2f;
  color: #fff;
}
.zero {
  grid-column: span 2;
}
  • We center the calculator and give it a dark background (.calculator).
  • .display has a dark gray background and large white text, aligned to the right.
  • .keypad uses CSS Grid with 4 columns to lay out buttons.
  • .btn styles all buttons with padding, rounded corners, and hover effects.
  • Additional classes color special buttons: orange for operators, green for equals, red for clear.
  • The .zero and .equals classes make those buttons span two columns for a typical calculator look.
  • We used plain CSS (no frameworks) and simple colors to make the app look polished.

With the code and styles in place, running npm start again should show the styled calculator UI on localhost:3000. You can click numbers and operators to use it. The % button now works (dividing by 100), and the ± button toggles the sign, as required.