{"id":13475,"date":"2025-11-06T10:33:13","date_gmt":"2025-11-06T10:33:13","guid":{"rendered":"https:\/\/www.bacancytechnology.com\/qanda\/?p=13475"},"modified":"2025-11-12T11:30:58","modified_gmt":"2025-11-12T11:30:58","slug":"countdown-timer-in-react-with-hooks","status":"publish","type":"post","link":"https:\/\/www.bacancytechnology.com\/qanda\/react\/countdown-timer-in-react-with-hooks","title":{"rendered":"Build a Countdown Timer in React with Hooks"},"content":{"rendered":"<p>When building a countdown timer in React with Hooks, the most common mistakes are:<\/p>\n<ol>\n<li><strong>Placing<\/strong> setInterval <strong>outside <\/strong>of useEffect (causes multiple intervals\/flickering).<\/li>\n<li><strong>Not clearing intervals properly<\/strong> (memory leaks, multiple timers running).<\/li>\n<li><strong>Calculating time incorrectly<\/strong> (restarting on every render).<\/li>\n<\/ol>\n<p>Here\u2019s a clean, optimized implementation of a countdown timer using useEffect and useState:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"squirrel\">import React, { useEffect, useState } from \"react\";\r\nconst Timer = ({ initialSeconds = 300 }) =&gt; {\r\n const [timeLeft, setTimeLeft] = useState(initialSeconds);\r\n  useEffect(() =&gt; {\r\n    if (timeLeft &lt;= 0) return; \/\/ stop when timer reaches 0\r\n\r\n    const intervalId = setInterval(() =&gt; {\r\n      setTimeLeft(prev =&gt; {\r\n        if (prev &lt;= 1) {\r\n          clearInterval(intervalId); \/\/ cleanup when reaching 0\r\n          return 0;\r\n        }\r\n        return prev - 1;\r\n      });\r\n    }, 1000);\r\n\r\n    \/\/ cleanup interval when component unmounts or updates\r\n    return () =&gt; clearInterval(intervalId);\r\n  }, [timeLeft]);\r\n\r\n  \/\/ format mm:ss\r\n  const formatTime = seconds =&gt; {\r\n    const minutes = String(Math.floor(seconds \/ 60)).padStart(2, \"0\");\r\n    const secs = String(seconds % 60).padStart(2, \"0\");\r\n    return `${minutes}:${secs}`;\r\n  };\r\n\r\n  return (\r\n    &lt;div&gt;\r\n      &lt;h1&gt;{formatTime(timeLeft)}&lt;\/h1&gt;\r\n    &lt;\/div&gt;\r\n  );\r\n};\r\n\r\nexport default Timer;<\/pre>\n<p>&nbsp;<\/p>\n<h2>Code Understanding<\/h2>\n<ol>\n<li>useEffect ensures the interval starts only when needed and cleans up properly.<\/li>\n<li>We <strong>decrement based on previous state<\/strong> (prev &#8211; 1) instead of recalculating from scratch each time.<\/li>\n<li>Timer <strong>stops automatically<\/strong> when reaching 0.<\/li>\n<li>formatTime ensures output is always mm:ss (e.g., 02:05).<\/li>\n<\/ol>\n<div class=\"qanda-read-box\"><div class=\"bg-light read-more-icon\"><img decoding=\"async\" src=\"https:\/\/assets.bacancytechnology.com\/qanda\/wp-content\/uploads\/2025\/04\/24061434\/read-txt.png\" alt=\"Also Read\"><p><\/p><h3>Also Read:<\/h3><a href=\"https:\/\/www.bacancytechnology.com\/blog\/react-security-solutions\" target=\"_blank\">React Security<\/a><\/div><\/div>\n","protected":false},"excerpt":{"rendered":"<p>When building a countdown timer in React with Hooks, the most common mistakes are: Placing setInterval outside of useEffect (causes multiple intervals\/flickering). Not clearing intervals properly (memory leaks, multiple timers running). Calculating time incorrectly (restarting on every render). Here\u2019s a clean, optimized implementation of a countdown timer using useEffect and useState: import React, { useEffect, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":13677,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-13475","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-react"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13475"}],"collection":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/comments?post=13475"}],"version-history":[{"count":3,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13475\/revisions"}],"predecessor-version":[{"id":13480,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/posts\/13475\/revisions\/13480"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media\/13677"}],"wp:attachment":[{"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/media?parent=13475"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/categories?post=13475"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.bacancytechnology.com\/qanda\/wp-json\/wp\/v2\/tags?post=13475"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}