Zum Inhalt springen
    WingoUI
    KomponentenVorlagenDokuPreiseKI-AgentenBlogChangelog
    1. Startseite
    2. Blog
    3. Komponenten-Guides
    4. React Countdown Timer: Blöcke, Inline, schlafende Tabs
    Read in English
    1. Blog
    2. Komponenten-Guides
    3. React Countdown Timer: Blöcke, Inline, schlafende Tabs
    Komponenten-Guides
    Komponenten-Guides

    React Countdown Timer: Blöcke, Inline, schlafende Tabs

    SR
    Serban Rusu · Gründer von Wingo UI
    19. Okt. 2026 · 9 Min. Lesezeit

    Auf dieser Seite

    0%
    1. Wie sollte ein React Countdown Timer die Restzeit berechnen?
    2. Welcher Countdown-Stil passt: Blöcke, inline oder in Worten?
    3. Warum driftet ein Countdown in einem Hintergrund-Tab?
    4. Wie gehe ich bei einem Launch Countdown mit Zeitzonen um?
    5. Wie rendere ich einen Countdown mit Server-Side Rendering?
    6. Was passiert, wenn der Countdown null erreicht?
    7. Gibt es einen React Countdown Hook für eigene Layouts?
    8. Wie funktioniert der Countdown mit Screenreadern und auf dem Smartphone?
    9. Wie schneiden die React Countdown Libraries im Vergleich ab?
    10. Wo passt der Countdown auf eine Launch-Seite?
    11. Wie baue ich ihn in mein Projekt ein?
    12. Häufige Fragen
      1. Wie baue ich einen Countdown Timer in React?
      2. Warum läuft mein Countdown Timer in einem Hintergrund-Tab langsamer?
      3. Wie zeige ich denselben Countdown in jeder Zeitzone?
      4. Gibt es einen React Countdown Hook?
      5. Ist der Countdown von Wingo UI kostenlos?

    Kurz gesagt

    Ein React Countdown Timer sollte die Restzeit bei jedem Tick aus einem festen Zielzeitpunkt berechnen, statt pro Tick eine Sekunde abzuziehen, denn Browser drosseln Timer in Hintergrund-Tabs. Speicher das Ziel als UTC-ISO-String, render bis zur Hydration einen Platzhalter und lass den Server entscheiden, wann etwas wirklich freigeschaltet wird, nicht den Countdown. Der Countdown von Wingo UI erledigt das und erscheint als Ziffernblöcke, inline im Satz oder in Worten.

    Veröffentlicht am 19. Okt. 2026

    Du brauchst einen React Countdown Timer für einen Launch, einen Sale, der um Mitternacht endet, eine Auktion oder eine Testphase. Die erste Version steht in zehn Minuten: eine Zahl im State, setInterval, jede Sekunde minus eins. Dann geht sie live. Ein Tab, der im Hintergrund lag, zeigt die falsche Zeit, Berlin und New York sehen den Launch zu verschiedenen Zeitpunkten, und die serverseitig gerenderte Seite meldet eine Mismatch-Warnung. Dieser Guide zeigt die drei Formen, in denen ein Countdown meist auftaucht, die Fallstricke hinter diesen Bugs und die Props, auf die es beim Countdown von Wingo UI ankommt.

    Wie sollte ein React Countdown Timer die Restzeit berechnen?

    Immer aus dem Ziel. Halte einen festen Zeitpunkt (to) fest und berechne bei jedem Tick to - Date.now(). Der Tick entscheidet nur, wann neu gezeichnet wird; die Zeit selbst trägt er nie. Die Version, die eine Restzahl speichert und herunterzählt, ist die, die driftet.

    Ein minimaler Hook, der das richtig macht, sieht so aus:

    tsx
    "use client"
    import { useEffect, useState } from "react"
    // ms bis `to`, oder null, solange die Uhr des Browsers unbekannt ist
    export function useTimeLeft(to: number) {
    const [now, setNow] = useState<number | null>(null)
    useEffect(() => {
    const tick = () => setNow(Date.now())
    tick()
    const id = window.setInterval(tick, 1000)
    return () => window.clearInterval(id)
    }, [])
    return now === null ? null : Math.max(0, to - now)
    }

    Zwei Haken hat er noch. Das Intervall läuft in seinem eigenen Sekundenraster, also kann die Anzeige bis zu einer Sekunde vom echten Wert abweichen und die Null zu spät erreichen. Und er startet mit null, also musst du entscheiden, was auf dem Server gerendert wird. Eine vollständige React Countdown Komponente kümmert sich um beides und um die Punkte weiter unten.

    Welcher Countdown-Stil passt: Blöcke, inline oder in Worten?

    Entscheide danach, wo der Countdown steht. Der Countdown hat drei Varianten:

    VarianteRendertGut für
    blocks (Standard)Eine Kachel pro Einheit mit Beschriftung: 2 DAYS 04 HOURS 12 MINUTES 09 SECONDSLaunch-Hero, Sale-Banner, Wartungsseite
    inline2d 04:12:09 im Fluss eines Satzes„Auktion endet in 3:12:45“ auf einer Angebotskarte
    textWorte: „in 2 days and 4 hours“Hinweise zur Testphase, E-Mails, alles, was ruhig wirken soll

    Blöcke rollen jede geänderte Ziffer wie ein Kilometerzähler an ihren Platz und nutzen dafür den kostenlosen Animated Counter (animation="fade" blendet stattdessen über, "none" wechselt einfach den Text). Führende Einheiten, die auf null stehen, fallen mit der Zeit weg: Ein Launch in zwei Tagen zeigt Tage, die letzte Stunde nur noch Minuten und Sekunden. „0 days“ erscheint nie.

    Wähl unter der Karte in 10 s, in 2 days, in 45 days oder past: Der Countdown tickt, endet oder zeigt gleich den fertigen Zustand; im Playground wechselst du Variante und Kachelstil
    $ npx wingo-ui@latest add countdown
    ProDoku: Countdown

    In einem Launch-Hero ist der Countdown selbst ein einziges Element:

    tsx
    import { Countdown } from "@/components/ui/countdown"
    export function LaunchHero() {
    return (
    <section className="flex flex-col items-center gap-6 py-16 text-center">
    <h1 className="text-4xl font-semibold tracking-tight">Acme Mobile kommt bald</h1>
    <Countdown to="2026-11-17T17:00:00Z" size="lg" tone="brand" completedContent="Acme Mobile ist live" />
    </section>
    )
    }

    Die Varianten inline und text rendern ein <span>, also passen sie in einen Absatz. Die Variante blocks rendert ein <div>, und ein <div> in einem <p> ist ungültiges HTML, das Next.js als Hydration-Fehler meldet. Setz die Blöcke deshalb nie in einen Absatz.

    tsx
    <p className="text-sm text-muted-foreground">
    Auktion endet in <Countdown to={auction.endsAt} variant="inline" size="sm" />
    </p>
    <p className="text-sm">
    Deine Testphase endet <Countdown to={trial.endsAt} variant="text" showSeconds={false} />.
    </p>

    precision legt fest, wie viele Einheiten die Variante text nennt (standardmäßig 2, „in 3 days and 4 hours“). showSeconds={false} blendet die Sekunden aus und tickt einmal pro Minute. Das ist ruhiger und schont die Performance in einer langen Kartenliste.

    Die Variante text in einem Satz: Launch starts in 2 days and 4 hours. Wähl in 10 s, damit die Worte zu Time's up wechseln
    $ npx wingo-ui@latest add countdown
    ProDoku: Countdown

    Warum driftet ein Countdown in einem Hintergrund-Tab?

    Weil Browser Timer in Tabs drosseln, die niemand ansieht. Laut Chromes Hinweisen zur Timer-Drosselung (opens in a new tab) werden Timer auf einer verborgenen Seite einmal pro Sekunde geprüft. Ab Chrome 88 nur noch einmal pro Minute, wenn die Seite länger als 5 Minuten verborgen ist, seit 30 Sekunden keinen Ton abspielt, ihre Timer 5 oder mehr Ebenen tief verkettet sind und kein WebRTC läuft. MDN (opens in a new tab) nennt ein Minimum von 1 Sekunde in inaktiven Tabs von Firefox am Desktop und 15 Minuten in Firefox für Android. Ein Laptop im Ruhezustand führt gar keine Timer aus.

    Ein Countdown, der pro Tick eins abzieht, verliert jeden übersprungenen Tick. Einer, der aus dem Ziel neu rechnet, zeichnet nur verspätet neu. Der Countdown geht einen Schritt weiter: Er plant jedes Neuzeichnen genau für den Moment, in dem der Wert eine Einheitengrenze des Ziels überschreitet, damit der letzte Tick genau auf null fällt. Und er rechnet sofort neu, sobald der Tab wieder sichtbar wird, also springt ein Tab, der geschlafen hat, direkt auf den richtigen Wert.

    Für lange Countdowns gibt es eine zweite Timer-Falle. setTimeout speichert seine Verzögerung als vorzeichenbehaftete 32-Bit-Ganzzahl, das Maximum liegt also bei 2.147.483.647 ms, etwa 24,8 Tagen. Laut MDN laufen größere Werte über, und eine übergelaufene Verzögerung kann sofort feuern. Ein setTimeout(openSale, saleAt - Date.now()) für einen Sale in 45 Tagen kann also sofort auslösen. Der Countdown wartet nie länger als einen Tick und begrenzt jede einzelne Wartezeit auf dieses Limit. Deshalb zählt ein Launch in 45 Tagen ganz normal herunter.

    Wie gehe ich bei einem Launch Countdown mit Zeitzonen um?

    Speicher einen absoluten Zeitpunkt und formatier ihn pro Besucher. Ein Launch am 17. November 2026 um 12 Uhr mittags in New York ist 2026-11-17T17:00:00Z (Eastern Time ist dann UTC-5). Jeder Browser zählt auf denselben Moment herunter, also endet der Countdown überall gleichzeitig.

    Zwei Formate solltest du vermeiden (siehe MDNs Seite zu Date.parse (opens in a new tab)):

    • Ein Datum mit Uhrzeit ohne Offset ("2026-11-17T12:00:00") wird auf jedem Gerät als Ortszeit gelesen. In Berlin steht der Countdown sechs Stunden früher auf null als in New York.
    • Ein reines Datum ("2026-11-17") wird als Mitternacht UTC gelesen, und das ist in New York der 16. November um 19 Uhr.

    Wenn du die Launch-Zeit neben dem Countdown ausgibst, formatier sie in der Zeitzone der jeweiligen Besucher und nenn die Zone, damit „Nov 17, 2026, 12:00 PM EST“ für alle dasselbe bedeutet:

    ts
    const launchLabel = new Intl.DateTimeFormat("en-US", {
    month: "short",
    day: "numeric",
    year: "numeric",
    hour: "numeric",
    minute: "2-digit",
    timeZoneName: "short",
    }).format(new Date("2026-11-17T17:00:00Z"))

    Schreib die Felder hier einzeln aus. dateStyle und timeStyle lassen sich nicht mit timeZoneName kombinieren; versuchst du es trotzdem, gibt es einen TypeError.

    Wie rendere ich einen Countdown mit Server-Side Rendering?

    Render auf dem Server einen Platzhalter und die Ziffern erst nach der Hydration. Der Server kennt die Uhr der Besucher nicht, und eine statisch vorgerenderte Seite würde die Zeit einfrieren, die sie beim Build berechnet hat. Die Next.js-Seite zum Hydration-Fehler (opens in a new tab) nennt zeitabhängige APIs wie den Date()-Konstruktor in der Render-Logik als häufige Ursache für Mismatches.

    Der Countdown liest die Uhr über useSyncExternalStore mit einem Server-Snapshot von null. Bis die Uhr bekannt ist, rendert jede Einheit -- über einem unsichtbaren 00 in ihrer endgültigen Breite. So springt nichts, wenn die echten Ziffern kommen, und es wird kein Mismatch gemeldet. Das Attribut data-state am Root-Element steht dann auf idle, während des Zählens auf running und bei null auf done.

    now friert die Uhr für Screenshots und Tests ein; ein so fixierter Countdown tickt nicht.

    Was passiert, wenn der Countdown null erreicht?

    onComplete feuert einmal beim Übergang auf null, pünktlich auch bei ausgeblendeten Sekunden. completedContent ersetzt die Ziffern und federt herein. Liegt to beim Mounten schon in der Vergangenheit, rendert die Komponente den fertigen Zustand, ohne onComplete aufzurufen. Wer die Seite nach dem Ende des Sales öffnet, bekommt also keinen Toast „Sale gerade beendet“.

    tsx
    "use client"
    import { useRouter } from "next/navigation"
    import { Countdown } from "@/components/ui/countdown"
    export function SaleTimer({ endsAt }: { endsAt: string }) {
    const router = useRouter()
    return (
    <Countdown
    to={endsAt}
    size="sm"
    tone="danger"
    completedContent="Das Angebot ist abgelaufen"
    // die Seite nach dem Sale vom Server holen
    onComplete={() => router.refresh()}
    />
    )
    }

    Der Countdown ist eine reine Anzeige. Er vertraut der Uhr des Geräts, und die kann Minuten danebenliegen oder von Hand verstellt sein. Wenn der Moment wichtig ist (ein Preis im Checkout, das Ende einer Auktion, Early Access), prüft der Server die Zeit, wenn die Anfrage ankommt. Der Countdown sagt den Leuten nur, wann sie wiederkommen sollen.

    Gibt es einen React Countdown Hook für eigene Layouts?

    Ja. useCountdown ist die Uhr hinter der Komponente und wird aus derselben Datei exportiert. Er gibt { days, hours, minutes, seconds, totalMs, done } zurück, bei jedem Tick aus der absoluten Zeit neu berechnet, wobei totalMs null ist, bis die Uhr bekannt ist. Seine Optionen sind now, interval (Standard 1000), onComplete und units. Mit units: ["seconds"] tragen die Sekunden den ganzen Rest, und das passt zu einem Timer für das erneute Senden eines Codes:

    tsx
    "use client"
    import { useState } from "react"
    import { Button } from "@/components/ui/button"
    import { useCountdown } from "@/components/ui/countdown"
    export function ResendCode({ onResend }: { onResend: () => void }) {
    const [to, setTo] = useState(() => Date.now() + 60_000)
    const { seconds, done, totalMs } = useCountdown(to, { units: ["seconds"] })
    const left = totalMs === null ? 60 : seconds
    return (
    <Button
    variant="soft"
    disabled={!done}
    onClick={() => {
    onResend()
    setTo(Date.now() + 60_000)
    }}
    >
    {done ? "Code erneut senden" : `Erneut senden in ${left} s`}
    </Button>
    )
    }

    Die Doku des Countdowns kombiniert diesen React Countdown Hook mit dem kostenlosen Progress-Ring für einen 60-Sekunden-Timer, nach dem sich der OTP-Code erneut senden lässt, dasselbe Muster wie im Guide zum React OTP Input.

    Wie funktioniert der Countdown mit Screenreadern und auf dem Smartphone?

    Das Root-Element hat role="timer" mit aria-live="off", denn eine Live-Region, die sich jede Sekunde ändert, würde Screenreader-Nutzer jede Sekunde unterbrechen. Ein versteckter Satz („2 days, 4 hours and 12 minutes left“) aktualisiert sich einmal pro Minute für alle, die dorthin navigieren, und das Ende wird einmal als höfliche Meldung (polite) angesagt. Bei reduzierter Bewegung wechseln die Ziffern einfach, statt zu rollen.

    Auf dem Smartphone verkleinern die lg-Blöcke ihre Ziffern und Kacheln, sobald sie selbst schmaler als 400px sind, damit vier Einheiten weiterhin auf einen Bildschirm mit 360px passen. Das Root-Element trägt in der letzten Minute data-urgent, für einen Farbwechsel kurz vor dem Ende. Jeder Text steckt in labels (days, hours, minutes, seconds, names, short, sentence, done, and, in), mit Pluralfunktionen pro Einheit. Eine Übersetzung ist also ein Prop statt eines Forks.

    Wie schneiden die React Countdown Libraries im Vergleich ab?

    react-countdown ist die Headless-Option, react-countdown-circle-timer zeichnet einen Ring, und der Countdown von Wingo UI gibt dir fertige Layouts als Quellcode. Stand Oktober 2026, laut dem öffentlichen README des jeweiligen Projekts:

    OptionModellZähltHinweise
    react-countdown (opens in a new tab)MIT-Paket, Komponente plus useCountdown-HookBis zu einem dateProp renderer für dein eigenes Markup, intervalDelay, overtime über null hinaus
    react-countdown-circle-timer (opens in a new tab)MIT-Paket, Komponente plus useCountdown-HookEine duration in SekundenEin SVG-Ring mit Farbstufen; der Hook liefert die Werte für den Pfad
    Ein eigener HookDein CodeWas du schreibstReicht für einen einfachen Timer, solange er aus dem Ziel neu rechnet
    Countdown von Wingo UIPro-Komponente als Quellcode in deinem RepoBis toVarianten blocks, inline und text, Platzhalter für SSR, ausgerichtete Ticks, Labels

    Für einen Ring um einen Quiz-Timer mit 30 Sekunden passt react-countdown-circle-timer besser. Für Headless-Logik mit komplett eigenem Markup ist react-countdown eine solide Wahl.

    Wo passt der Countdown auf eine Launch-Seite?

    Ganz oben auf eine Wartelisten-Seite. Die Vorlage Waitlist Page setzt einen Countdown unter die Überschrift, der bis launchAt zählt, mit einem „live“-Hinweis als Inhalt für den fertigen Zustand, und wird zu einer einfachen Coming-soon-Seite, wenn launchAt null ist. Das Tutorial zur Next.js Waitlist Page verbindet sie mit einer echten Anmeldetabelle, der Position auf der Warteliste und Empfehlungslinks. So sammelt die Seite mit dem Launch Countdown, die du vor dem ersten Tag live stellst, Anmeldungen, statt nur zu ticken. Für eine einzelne rollende Zahl statt eines ganzen Timers gibt es den Guide zum animierten Zahlenzähler, und für relative Angaben wie „vor 3 Stunden“ gibt es die Komponente Relative Time.

    Wie baue ich ihn in mein Projekt ein?

    Führ npx wingo-ui@latest add countdown aus. Die CLI kopiert components/ui/countdown.tsx in dein Projekt, dazu den Animated Counter und die Helfer, die er importiert, und installiert motion. Der Countdown und die Waitlist Page gehören zu Wingo UI Pro. Weitere Deep Dives zu einzelnen Komponenten findest du in der Kategorie Komponenten-Guides.

    Komponenten in diesem Artikel

    • Countdown

      A React countdown timer to a launch, sale or deadline, shown as rolling digit blocks, inline in a sentence or in words, with done content.

      Pro
    • Waitlist Page

      React waitlist page template: a launch countdown, an email signup with social proof, your place in line with a referral link and FAQ.

      Pro

    Häufige Fragen

    Wie baue ich einen Countdown Timer in React?

    Halte den Zielzeitpunkt fest (einen Timestamp oder einen ISO-String mit Offset) und berechne in einem useEffect bei jedem Tick Ziel minus Date.now(), dann teil das Ergebnis in Tage, Stunden, Minuten und Sekunden auf. Zähl nie einen Zähler herunter, denn ein gedrosselter oder schlafender Tab überspringt Ticks, und der Zähler hinkt hinterher.

    Warum läuft mein Countdown Timer in einem Hintergrund-Tab langsamer?

    Browser drosseln Timer in verborgenen Tabs: Chrome prüft sie einmal pro Sekunde und nur noch einmal pro Minute, wenn eine Seite länger als 5 Minuten verborgen ist und ihre Timer verkettet sind. Ein Countdown, der pro Tick eins abzieht, verliert die übersprungenen Ticks; einer, der aus dem Ziel neu rechnet, zeigt beim nächsten Tick den richtigen Wert.

    Wie zeige ich denselben Countdown in jeder Zeitzone?

    Speicher den Launch als einen absoluten Zeitpunkt, etwa 2026-11-17T17:00:00Z, und lass jeden Browser darauf herunterzählen. Ein Datum mit Uhrzeit ohne Offset wird als Ortszeit der Besucher gelesen und ein reines Datum als Mitternacht UTC. Mit beiden Formaten endet der Countdown je nach Zeitzone zu einem anderen Zeitpunkt.

    Gibt es einen React Countdown Hook?

    Ja. Das Paket react-countdown exportiert useCountdown, und der Countdown von Wingo UI exportiert einen useCountdown-Hook, der days, hours, minutes, seconds, totalMs und done für dein eigenes Layout zurückgibt. Ein kurzer eigener Hook geht auch, solange er aus dem Ziel neu rechnet.

    Ist der Countdown von Wingo UI kostenlos?

    Nein. Der Countdown und die Vorlage Waitlist Page sind Pro. Der Animated Counter, der seine Ziffern rollt, und der Progress-Ring aus dem Beispiel zum erneuten Senden sind kostenlos.

    • Countdown
    • Timer
    • React
    • Next.js
    • Barrierefreiheit

    Teilen

    SR

    Über den Autor

    Serban Rusu

    Gründer von Wingo UI

    Serban Rusu hat Wingo UI gegründet. Er baut die Komponenten, die CLI und den MCP-Server und schreibt über React-UIs für Smartphones und KI-Agents.

    Mehr von Serban
    Weiterlesen

    Ähnliche Artikel

    Alle Artikel
    Praxis: Screens und Apps

    Nextjs Waitlist Page: Countdown, Empfehlungen, echte Zahlen

    Bau eine Nextjs Waitlist Page mit Countdown, E-Mail-Feld, Platz auf der Liste und Empfehlungslink auf Postgres, dazu eine Anmeldezahl, die nie gefälscht ist.

    SRSerban Rusu·16. Okt. 2026·10 Min. Lesezeit
    Komponenten-Guides

    Embla Carousel React: barrierefreie Slides ohne Layout Shift

    Bau mit Embla Carousel React ein Karussell für Fotos, Inserate, Testimonials und Onboarding, mit Tastatursteuerung, pausierbarem Autoplay und ohne Layout Shift.

    SRSerban Rusu·19. Okt. 2026·9 Min. Lesezeit
    Komponenten-Guides

    React Animated Counter mit rollenden Ziffern

    Bau einen React Animated Counter, der jede Ziffer wie ein Kilometerzähler rollt, Trennzeichen aus Intl holt, seine Breite hält und Reduced Motion beachtet.

    SRSerban Rusu·19. Okt. 2026·9 Min. Lesezeit
    Newsletter

    Neue Artikel per E-Mail

    Neue Guides, Tutorials und Vergleiche aus dem Wingo UI Blog, sobald sie erscheinen. Die meisten Artikel sind auf Englisch.

    Kein Spam. Du kannst dich jederzeit abmelden.

    WingoUI

    Animierte, konfigurierbare, mobile-first React-Komponenten und Blöcke. Kopier den Quellcode, pass ihn an und lass deinen Coding-Agenten damit bauen.

    Produkt

    • Komponenten
    • Vorlagen
    • Preise
    • KI-Agenten
    • Theme-Generator

    Ressourcen

    • Doku
    • Blog
    • Changelog
    • Changelog-RSS
    • llms.txt

    Rechtliches

    • AGB
    • Datenschutz
    • Rückerstattung
    • Lizenz
    • Impressum

    Komponenten-Kategorien

    • Buttons & Actions
    • Inputs
    • Forms
    • Navigation
    • Overlays
    • Feedback
    • Data Display
    • Tables & Lists
    • Charts & Stats
    • Layout
    • Media
    • AI Kit
    • Text & Effects
    • Mobile
    • Commerce
    • Marketing Sections
    • Blocks
    • Hooks & Utilities

    © 2026 Wingo UI

    Wingo UI