Build a JS Counter App Beginner DOM Mini Project

Build a small counter app with plain JavaScript to practice selecting elements, handling clicks, and updating the page with the DOM.

6 min read

A JavaScript counter app is a small page with a number on screen and two buttons that increase or decrease it. It is one of the first real projects beginners build because it uses the exact pattern behind most interactive websites: select an element, listen for a click, then update the page.

You will build a page with a count display, an increment button, and a decrement button. By the end, clicking a button will update the number instantly, without reloading the page.

What You Will Build

The finished app is three pieces of HTML and about ten lines of JavaScript. Here is the behavior you are aiming for:

  • The page shows a number, starting at zero.
  • Clicking the plus button increases the number by one.
  • Clicking the minus button decreases the number by one.
  • The count never goes below zero, no matter how many times you click minus.

This is a small project, but it covers the same three ideas that almost every interactive web page relies on: finding elements on the page, reacting to user input, and keeping the visible page in sync with a value your code is tracking.

Counter app click flow

Every click follows the same four steps: the browser fires an event, your handler function runs, a variable changes, and the page text updates to match. This loop is the core idea behind the whole project, and you will build it piece by piece below.

Step 1: Build the HTML

Start with a small page that has a number display and two buttons.

htmlhtml
<div class="counter">
  <button id="decrement">-</button>
  <span id="count">0</span>
  <button id="increment">+</button>
</div>

Each element carries an id attribute so JavaScript can find it later. The middle element holds the number, and the two buttons on either side will trigger the increase and decrease actions once the script is wired up.

Step 2: Select the Elements

The next step is grabbing each of these three elements from JavaScript and storing a reference to them in a variable.

javascriptjavascript
const countDisplay = document.getElementById("count");
const incrementButton = document.getElementById("increment");
const decrementButton = document.getElementById("decrement");
 
let count = 0;

The first three lines point to the number display and the two buttons. The last line adds a separate variable that holds the real count as a number, kept apart from whatever text happens to be on the page. Storing the value here, rather than reading it back out of the page later, is what keeps the app reliable as it grows. You can see a related selection approach in how to use querySelector and querySelectorAll.

Step 3: Handle the Button Clicks

With the elements selected, attach a click handler to each button that updates the count and reflects it on the page.

javascriptjavascript
incrementButton.addEventListener("click", () => {
  count += 1;
  countDisplay.textContent = count;
});
 
decrementButton.addEventListener("click", () => {
  if (count > 0) {
    count -= 1;
    countDisplay.textContent = count;
  }
});

The first handler adds one to the count every time someone clicks the plus button, then writes the new value onto the page. The second handler does the same in reverse, but only runs the subtraction when the count is still above zero.

That single check is what stops the number from ever going negative. For more detail on wiring up clicks like this, see handling click events in JavaScript.

Why the Count Lives in a Variable, Not Just the Page

A common beginner approach skips the separate variable entirely and reads the number straight out of the page text on every click.

javascriptjavascript
incrementButton.addEventListener("click", () => {
  const current = Number(countDisplay.textContent);
  countDisplay.textContent = current + 1;
});

This version works, but it converts text back into a number on every single click. Page text is always a string, so a missed conversion anywhere in the code causes the app to silently start concatenating text instead of adding numbers.

Keeping the count as a real number in its own variable avoids that entire category of bug. The page is only ever used to display the value, never to store it.

Common Mistakes

MistakeWhy it breaksFix
Skipping the number conversion when reading from the pagePage text is always a string, so adding to it appends characters instead of doing mathKeep the count in its own variable instead of reading it back from the page each time
Selecting elements before the HTML has loadedElement lookups return nothing if the script runs too earlyPlace the script tag at the end of the body, or select elements after the page finishes loading
Overwriting the whole element to change one numberRewriting more markup than necessary is slower and riskier than it needs to beUpdate just the text of the display element on each click

Next Step

You now have the core pattern for interactive JavaScript: select, listen, update. The same structure scales up to bigger projects, like a JavaScript todo list app or a JavaScript calculator, where you track more state and respond to more kinds of input.

Rune AI

Rune AI

Key Insights

  • A counter app selects a display element and two buttons, then updates a count variable on each click.
  • addEventListener connects a button click to a function that changes the page.
  • textContent updates the visible number without re-rendering the whole page.
  • Keeping the count in a variable, not just in the page text, avoids bugs when you need to read the current value.
  • The select, listen, update pattern used here is the foundation for most interactive JavaScript projects.
RunePowered by Rune AI

Frequently Asked Questions

Do I need a framework to build a counter app?

No. A counter app only needs plain JavaScript, HTML, and the DOM API. Frameworks like React add extra tooling that is not necessary for a project this small.

Why does the count reset when I refresh the page?

The count lives in a JavaScript variable, which only exists while the page is open. Refreshing the page reloads the script and resets the variable to its starting value.

How do I stop the counter from going below zero?

Add a check inside the decrement handler that only updates the count when it is greater than zero, then update the text after the check passes.

Conclusion

A counter app is a small but complete example of the pattern behind most interactive JavaScript pages: select an element, listen for an event, update a variable, then reflect that variable back on the page. Once this loop feels natural, you can reuse it for bigger projects like a todo list or a shopping cart.