| name | n-countdown |
| description | Countdown component for displaying time countdown. Invoke when user needs to implement countdown timers, deadlines, or time-based displays in Naive UI. |
| metadata | {"author":"jiaiyan","version":"1.0.0"} |
Naive UI Countdown Component
Countdown component for displaying time-based countdown with customizable precision and rendering.
When to Use
Use this component when:
- Countdown timers: Display time remaining for events or deadlines
- Promotional campaigns: Show time-limited offers
- Session timeouts: Warn users about session expiration
- Time tracking: Display elapsed or remaining time
When to Invoke
Invoke this skill when:
- User needs to implement a countdown timer
- User wants to customize countdown display
- User needs precise time display (milliseconds)
- User wants to handle countdown completion
- User asks about resetting or controlling countdown state
Features
- Configurable Duration: Set countdown duration in milliseconds
- Precision Control: Display time with 0-3 decimal precision
- Active Control: Start/stop countdown programmatically
- Custom Rendering: Fully customize countdown display
- Reset Support: Reset countdown to initial state
- Completion Callback: Handle countdown finish event
API Reference
Countdown Props
| Name | Type | Default | Description |
|---|
| active | boolean | true | Whether countdown is active. |
| duration | number | 0 | The duration of the countdown (unit is millisecond). Not reactive. |
| precision | 0 | 1 | 2 | 3 | 0 | The precision of the second. |
| render | (props: { hours: number, minutes: number, seconds: number, milliseconds: number }) => VNodeChild | undefined | Time's render function. |
| on-finish | () => void | undefined | The callback on countdown is finished. |
Countdown Methods
| Name | Type | Description |
|---|
| reset | () => void | Reset countdown. |
Basic Usage
Basic Countdown
<template>
<n-space>
<span style="font-variant-numeric: tabular-nums">
<n-countdown :duration="5000" :active="active" />
</span>
<n-switch v-model:value="active" />
</n-space>
</template>
<script setup>
import { ref } from 'vue';
const active = ref(true);
</script>
Precision Control
<template>
<n-space>
<span style="font-variant-numeric: tabular-nums; white-space: nowrap">
<n-countdown :duration="3600 * 1000" :active="active" />
</span>
<span style="font-variant-numeric: tabular-nums; white-space: nowrap">
<n-countdown :duration="3600 * 1000" :active="active" :precision="1" />
</span>
<span style="font-variant-numeric: tabular-nums; white-space: nowrap">
<n-countdown :duration="3600 * 1000" :active="active" :precision="2" />
</span>
<span style="font-variant-numeric: tabular-nums; white-space: nowrap">
<n-countdown :duration="3600 * 1000" :active="active" :precision="3" />
</span>
<n-switch v-model:value="active" />
</n-space>
</template>
<script setup>
import { ref } from 'vue';
const active = ref(true);
</script>
Custom Rendering
<template>
<n-space item-style="display: flex; align-items: center;">
<n-statistic label="Countdown" tabular-nums>
<n-countdown
:render="renderCountdown"
:duration="996 * 1000"
:active="active"
/>
</n-statistic>
<n-switch v-model:value="active" />
</n-space>
</template>
<script setup>
import { ref, h } from 'vue';
const active = ref(true);
const renderCountdown = ({ hours, minutes, seconds, milliseconds }) => {
return h('span', {}, [
h('span', { style: { color: 'red' } }, hours),
'h ',
h('span', { style: { color: 'green' } }, minutes),
'm ',
h('span', { style: { color: 'blue' } }, seconds),
's',
]);
};
</script>
Reset Countdown
<template>
<n-space>
<span style="font-variant-numeric: tabular-nums">
<n-countdown ref="countdown" :duration="86400000" :active="active" />
</span>
<n-button size="tiny" @click="handleReset"> Reset </n-button>
<n-switch v-model:value="active" />
</n-space>
</template>
<script setup>
import { ref } from 'vue';
const active = ref(true);
const countdown = ref(null);
const handleReset = () => {
countdown.value?.reset();
};
</script>
Common Patterns
Countdown with Finish Handler
<template>
<div>
<n-countdown
v-if="!finished"
:duration="10000"
:active="active"
@finish="handleFinish"
/>
<n-alert v-else type="success" title="Time's up!">
The countdown has finished.
</n-alert>
<n-switch v-model:value="active" :disabled="finished" />
</div>
</template>
<script setup>
import { ref } from 'vue';
const active = ref(true);
const finished = ref(false);
const handleFinish = () => {
finished.value = true;
};
</script>
Sale Countdown
<template>
<n-card title="Flash Sale Ends In">
<n-statistic tabular-nums>
<n-countdown
:duration="remainingTime"
:active="saleActive"
@finish="handleSaleEnd"
/>
</n-statistic>
<template #footer>
<n-button type="primary" :disabled="!saleActive"> Shop Now </n-button>
</template>
</n-card>
</template>
<script setup>
import { ref, computed } from 'vue';
const saleEndTime = new Date();
saleEndTime.setHours(saleEndTime.getHours() + 24);
const remainingTime = computed(() => saleEndTime.getTime() - Date.now());
const saleActive = ref(true);
const handleSaleEnd = () => {
saleActive.value = false;
};
</script>
Session Timeout Warning
<template>
<n-alert v-if="showWarning" type="warning" title="Session Expiring">
Your session will expire in:
<n-countdown
:duration="timeoutDuration"
:active="true"
@finish="handleTimeout"
/>
<n-button size="small" @click="extendSession"> Extend Session </n-button>
</n-alert>
</template>
<script setup>
import { ref } from 'vue';
const showWarning = ref(true);
const timeoutDuration = ref(5 * 60 * 1000);
const handleTimeout = () => {
showWarning.value = false;
console.log('Session expired');
};
const extendSession = () => {
timeoutDuration.value = 5 * 60 * 1000;
};
</script>
Custom Styled Countdown
<template>
<div class="countdown-container">
<n-countdown :duration="3600000" :render="renderStyledCountdown" />
</div>
</template>
<script setup>
import { h } from 'vue';
const renderStyledCountdown = ({ hours, minutes, seconds }) => {
const renderUnit = (value, label) => {
return h('div', { class: 'countdown-unit' }, [
h('div', { class: 'countdown-value' }, String(value).padStart(2, '0')),
h('div', { class: 'countdown-label' }, label),
]);
};
return h('div', { class: 'countdown-wrapper' }, [
renderUnit(hours, 'Hours'),
h('span', { class: 'countdown-separator' }, ':'),
renderUnit(minutes, 'Minutes'),
h('span', { class: 'countdown-separator' }, ':'),
renderUnit(seconds, 'Seconds'),
]);
};
</script>
<style scoped>
.countdown-wrapper {
display: flex;
align-items: center;
gap: 8px;
}
.countdown-unit {
text-align: center;
}
.countdown-value {
font-size: 24px;
font-weight: bold;
}
.countdown-label {
font-size: 12px;
color: #999;
}
.countdown-separator {
font-size: 24px;
font-weight: bold;
}
</style>
Best Practices
-
Use tabular-nums for alignment: Apply font-variant-numeric: tabular-nums for consistent digit width
<span style="font-variant-numeric: tabular-nums">
<n-countdown :duration="60000" />
</span>
-
Handle completion: Always handle the finish event for user feedback
<n-countdown :duration="10000" @finish="handleFinish" />
-
Control active state: Use the active prop to pause/resume countdown
<n-countdown :duration="60000" :active="isRunning" />
-
Use ref for reset: Access the reset method via template ref
<n-countdown ref="countdownRef" :duration="60000" />
<n-button @click="countdownRef?.reset()">Reset</n-button>
-
Duration is not reactive: The duration prop is not reactive, use reset to restart
<n-countdown ref="countdown" :duration="fixedDuration" />
-
Custom rendering for complex UI: Use the render prop for custom layouts
<n-countdown :render="customRenderer" :duration="60000" />