FrontendAtlas
Interview Questions
Home>Vue.js interview questions> Vue coding challenges >Vue Counter (Guarded Decrement)

Vue Counter (Guarded Decrement)

easy
By FrontendAtlas Editorial · Updated Jan 30, 2026
easy level
Vue 3 Composition API (`ref`, `computed`) Basic component structure Click event handlers Guarded state updates
Build a Vue 3 counter component with increment/decrement and a zero floor. Use reactive state and guard the decrement so it never goes negative. Call out disabled states and clean template logic. Concepts: vue, components, reactivity, event handlers, basics. Framework focus: Vue SFC patterns, ref/reactive state, computed/watch, and v-model bindings.
Vue 3 Composition API (ref, computed)Basic component structureClick event handlersGuarded state updates

Requirements

  • Render the counter UI as the main component.
  • Display the current count value.
  • Provide '+' and '−' buttons to increment and decrement.
  • Prevent the count from ever going below 0.
  • Include a 'Reset' button that sets the count back to 0.

Expected behavior

  • Initial count starts at 0.
  • Clicking '+' increases the count by 1.
  • Clicking '−' decreases the count by 1, but never below 0.
  • Clicking 'Reset' sets the count back to 0.
  • The '−' button is disabled when the count is 0.

Implementation notes

  • Use Vue 3's Composition API with ref to store the count.
  • Use a computed property to determine when the count is zero and disable the decrement button.
  • Keep everything in a single <App.vue> component for this exercise.

Solution

Overview

Use Vue 3's Composition API with ref(0) for the count and a computed property that returns true when the count is 0. The '+' button increments the ref, the '−' button decrements but is guarded so the value never becomes negative, and 'Reset' sets it back to 0.

**Key ideas**

  • Store count in a single ref.
  • Derive disabled state from a computed value.
  • Guard the decrement so it never sets a negative value. Performance & pitfalls: consider edge cases (empty state, rapid clicks), trade-offs in state shape, and accessibility of interactive controls.
1

Single component with guarded decrement

Keep all logic inside <script setup> in App.vue. Use const count = ref(0) to create reactive state. Define a computed property isZero that checks count.value === 0 and bind it to :disabled on the decrement button. Implement inc, dec, and reset as simple functions that mutate count.value, with dec checking that the value is greater than 0 before decrementing. Edge cases & testing: cover invalid inputs, ensure predictable reactivity, and keep the UI stable under rapid updates.

Files

{
  "name": "vue-counter",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "vue": "3.x"
  },
  "scripts": {
    "serve": "vue-cli-service serve"
  }
}
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Node",
    "strict": true,
    "allowJs": true
  }
}
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Vue SDK • Counter</title></head><body><div id="app"></div></body></html>
:root {
  color-scheme: dark;
}

html, body {
  margin: 0;
  height: 100%;
  background: #0a0a0a;
  font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;
}

.main {
  min-height: 100vh;
  background: #0a0a0a;
  color: #e5e7eb;
  display: grid;
  place-items: center;
  padding: 24px;
}

.section {
  width: 100%;
  max-width: 420px;
  display: grid;
  gap: 16px;
}

h1 {
  margin: 0;
  font-size: 20px;
  font-weight: 700;
}

.panel {
  background: #0b1020;
  border: 1px solid #1f2937;
  border-radius: 12px;
  padding: 16px;
}

.value {
  font-size: 48px;
  font-weight: 800;
  text-align: center;
  margin-bottom: 12px;
}

.actions {
  display: flex;
  gap: 8px;
  justify-content: center;
}

button {
  background: #a3e635;
  color: #111;
  border: none;
  border-radius: 10px;
  padding: 10px 14px;
  font-weight: 700;
  cursor: pointer;
}

button[disabled] {
  opacity: .5;
  cursor: not-allowed;
}

button.ghost {
  background: transparent;
  color: #e5e7eb;
  border: 1px solid #374151;
}
<template>
  <main class="main">
    <section class="section">
      <h1>Counter</h1>

      <div class="panel">
        <div class="value">{{ count }}</div>

        <div class="actions">
          <button @click="dec" :disabled="isZero">−</button>
          <button @click="inc">+</button>
          <button @click="reset" class="ghost">Reset</button>
        </div>
      </div>
    </section>
  </main>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue';

// Current count value
const count = ref(0);

// Disable decrement when count is zero
const isZero = computed(() => count.value === 0);

const inc = () => {
  count.value += 1;
};

const dec = () => {
  if (count.value > 0) {
    count.value -= 1;
  }
};

const reset = () => {
  count.value = 0;
};
</script>
import 'zone.js';
import { createApp } from 'vue';
import App from './App.vue';
import './App.css';

createApp(App).mount('#app');

Notes & Pitfalls

Pitfalls
  • Updating `count` without guarding against negatives, allowing the value to go below 0.
  • Forgetting to use `.value` when reading or writing refs inside `<script setup>`.
  • Deriving `isZero` manually and forgetting to update it when count changes instead of using `computed`.
Techniques
  • Using `ref` for local reactive state in Vue 3.
  • Using `computed` to derive boolean UI state (disabled button).
  • Keeping state and handlers co-located with the component template.

Similar questions

Vue Like Button (Toggle + Counter)easyVue Star Rating WidgeteasyVue Nested Checkbox Tree (Parent–Child Sync)intermediate

Guides

Frontend interview preparation guideGuideFrontend coding interview questions and prep guideBlueprintJavaScript Problems That Actually Show UpBlueprintBuild Great UI in 60 MinutesBlueprint

Preparing for interviews? Use Frontend Coding Challenges first, then move into a concrete Study Plan before targeted Company Prep.

Open frontend interview questionsBrowse Vue.js interview questionsOpen Essential 60Open Machine Coding HubOpen Frontend Coding ChallengesOpen System DesignOpen Interview Prep GuideOpen System Design BlueprintOpen Framework Prep PathsOpen Study PlansOpen 30-day foundations study planOpen Company PrepOpen Vue Framework Prep Guide
↗Incidents hub
18 / 25