Building a Counter
The "hello world" of interactive apps: a number you change with the keyboard. It shows the core loop of any Vue TermUI app — reactive state, a key handler, and a way to quit.
vue
<script setup lang="ts">
import { Box, Text, onKeyDown, useExit, ref } from 'vue-termui'
const count = ref(0)
const exit = useExit()
onKeyDown((key) => {
if (key.name === 'up' || key.name === '+') count.value++
else if (key.name === 'down' || key.name === '-') count.value--
else if (key.name === 'q') exit()
})
</script>
<template>
<Box border borderStyle="rounded" :padding="1" flexDirection="column" :gap="1">
<Text bold fg="#42b883">Counter</Text>
<Text>
Value:
<Text bold :fg="count < 0 ? 'red' : '#42b883'">{{ count }}</Text>
</Text>
<Text fg="#888888">↑/↓ or +/- to change · q to quit</Text>
</Box>
</template>How it works
ref(0)holds the count. Because it's reactive, the template re-renders whenever it changes — and the renderer patches only the line that changed.onKeyDownregisters a key handler scoped to this component; it's removed automatically on unmount. We branch onkey.name.useExitreturns the app's exit function so q quits cleanly (Ctrl+C always works too).- The nested
<Text>recolors the number based on its sign — templates compose exactly like on the web.
Variations
Drive it from a timer instead of keys:
vue
<script setup lang="ts">
import { Text, ref, useInterval } from 'vue-termui'
const count = ref(0)
useInterval(() => count.value++, 1000)
</script>
<template>
<Text>Elapsed: {{ count }}s</Text>
</template>Next, try wiring the counter into a multi-screen app with Routing, or add a text field with Forms & Inputs.