When an interviewer asks you to build a calculator, the first thing that must come to your mind is:
👉 This is a DSA + JavaScript round.
They often expect a scalable evaluation logic, clear trade-offs, and good UX. Before you start, ask these clarifying questions — they’ll tell you what the interviewer really wants:
- Does it support brackets
()? (If yes, expect stack-based parsing.) - Are we more focused on CSS / UI or on core functionality?
- Can we use
eval()? (If yes, focus shifts to UX/CSS; if no, they want parsing/DSA.) - What operations should be supported? (
+ - * /only, or%,^,√, etc.?) - Should it handle decimals and negative numbers properly?
- Do you need memory functions, history, or clipboard support?
- Are keyboard shortcuts / full accessibility required?
Asking these gives you a clear signal of expectations. If they say “yes” to eval(), they usually expect a strong UI demonstration. If they say “no”, roll up your sleeves — you’ll likely need to show parsing, operator precedence, and correctness.
Below is what we are building today
Below is a step-by-step, buildable Medium-style post that keeps all your code exactly as you pasted it and explains each part so a reader can implement the component from scratch.
What We're Building
Our end product will be a highly functional Calculator featuring:
- Custom Expression Evaluator for safe mathematical operations without
eval() - Reusable Button Component with flexible styling and consistent behavior
- React Hooks for clean state management and lifecycle handling
- Keyboard Support with focus management for accessibility
- Modern Design with professional styling and smooth animations
- Responsive Design using Tailwind CSS for rapid, utility-first styling
- Error Handling for edge cases and invalid operations
- Modular Architecture for easy testing and maintenance
Quick project setup (prerequisites)
- Create a React project (Vite or CRA). Example with CRA:
npx create-react-app my-calculator
cd my-calculator - Install Tailwind CSS (follow Tailwind docs) or add your preferred styling approach.
- Create
src/components/Button.jsxandsrc/components/Calculator.jsx— we'll place code there.
Step 1 — The Reusable Button Component
Create src/components/Button.jsx and paste the exact code below. This component standardizes button styling and layout so the Calculator UI code stays tidy.
import React from 'react';
const Button = ({
children,
onClick,
className = '',
colSpan = 1,
...props
}) => {
const baseClasses = "px-4 py-3 font-semibold text-white rounded-xl shadow-lg transition-all duration-200 hover:shadow-xl active:scale-95";
const spanClasses = colSpan > 1 ? `col-span-${colSpan}` : '';
return (
<button
className={`${baseClasses} ${spanClasses} ${className}`}
onClick={onClick}
{...props}
>
{children}
</button>
);
};
export default Button;
Why this component first?
- Keeps styling consistent across all buttons.
colSpanhelps with grid layout (makes the0button span two columns).- Improves reusability and reduces repetitive code in the Calculator.
Step 2 — Component Structure (Calculator shell)
Create src/components/Calculator.jsx. Start with the basic shell — state, operators, and a ref for focus.
import React, { useEffect, useRef, useState } from "react";
function Calculator() {
const operators = new Set(["*", "+", "-", "/"]);
const [input, setInput] = useState([]);
const inputRef = useRef();
// Component logic here...
}
Why this structure?
Setgives O(1) operator checks.inputas an array makes token handling straightforward (you append numbers/operators).inputReflets us manage focus for keyboard support and accessibility.
Step 3 — Number Input Handler
Add the number handler to the component so number button clicks append to the expression.
const numberHandler = (num) => {
setInput((prev) => [...prev, num]);
};
Design choices:
- Immutable updates (
[...]) to avoid mutation bugs. - Keep number handling simple — validation and correctness are handled later by the evaluator.
Step 4 — Operator Input Handler
Prevent consecutive operators and make operator input intuitive.
const operatorHandler = (operator) => {
const tempInput = [...input];
if (operators.has(tempInput.at(-1))) {
tempInput.pop();
}
tempInput.push(operator);
setInput(tempInput);
};
Why this matters:
- Replaces the last operator if user types another one, preventing
5++3. - Keeps expression state clean and predictable.
Step 5 — The Custom Expression Evaluator (the heart)
Implement a safe evaluator that respects operator precedence (handles *// before +/-) without using eval().
function evaluateExpression() {
let currentNum = "";
let currentOp = "+";
const operationArray = [];
let index = 0;
const performOperation = (operator, num) => {
if (operator === "+") {
operationArray.push(+num);
} else if (operator === "-") {
operationArray.push(-num);
} else if (operator === "/") {
const first = operationArray.pop();
operationArray.push(first / num);
} else if (operator === "*") {
const first = operationArray.pop();
operationArray.push(first * num);
}
};
while (index < input.length) {
if (operators.has(input[index])) {
performOperation(currentOp, currentNum);
currentNum = "";
currentOp = input[index];
} else if (!isNaN(input[index])) {
currentNum += input[index];
}
index += 1;
}
performOperation(currentOp, currentNum);
return operationArray.reduce((current, num) => current + num, 0);
}
Why this evaluator works:
- Handles multiplication/division before addition/subtraction by pushing intermediate results and using
pop()for higher precedence ops. - No
eval()→ safe and testable. - Extensible: add more operators by extending
performOperation.
Step 6 — Evaluation Handler (connect evaluator to UI)
Hook the evaluator up to UI with a handler that replaces the input with the result.
const evaluateHandler = () => {
const result = evaluateExpression();
setInput([result]);
};
Note: In production, wrap this in try/catch and validate isFinite / isNaN (we’ll show that in Advanced Features).
Step 7 — Focus Management (Accessibility)
Focus the input on mount so keyboard users can start typing immediately.
useEffect(() => {
inputRef.current.focus();
}, []);
Accessibility wins:
- Keyboard-first UX.
- Screen readers will pick up the focus and current display.
- Small detail that makes a big difference.