react-countup & react-intersection-observer not animating the count in Production Build
Unanswered
Egyptian Mau posted this in #help-forum
Egyptian MauOP
Hello,
react-countup and react-intersection-observer is working perfectly fine in the dev env npm run dev but I am building the app for the Production Build using npm run build and then startingthe Prod Server using npm run start then unfortunately the App is not running the count animation at all, even using "use client"; and also the import 'client-only', I am sharing the code snippets for now for overview of the issue and if required I can make the repository public from private on GitHub.{
"name": "novareel-studios",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "biome check",
"format": "biome format --write"
},
"dependencies": {
"@base-ui/react": "^1.1.0",
"class-variance-authority": "^0.7.1",
"client-only": "^0.0.1",
"clsx": "^2.1.1",
"formik": "^2.4.9",
"framer-motion": "^12.27.0",
"lenis": "^1.3.17",
"lucide-react": "^0.562.0",
"next": "16.1.3",
"react": "19.2.3",
"react-countup": "^6.5.3",
"react-dom": "19.2.3",
"react-intersection-observer": "^10.0.2",
"react-lite-youtube-embed": "^3.3.3",
"server-only": "^0.0.1",
"shadcn": "^3.7.0",
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0",
"tw-animate-css": "^1.4.0",
"yup": "^1.7.1"
},
"devDependencies": {
"@biomejs/biome": "2.2.0",
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"babel-plugin-react-compiler": "1.0.0",
"tailwindcss": "^4",
"typescript": "^5"
}
}6 Replies
Egyptian MauOP
below is the
/src/apps/layout.tsximport type { Metadata, Viewport } from "next"
import { Nunito_Sans } from "next/font/google"
import { Toaster } from "sonner"
import TopProgressBar from "@/components/top-progress-bar"
import { APP_INFO } from "@/constants"
import ClientLayoutWrapper from "@/custom-layout/client-layout-wrapper"
import "./globals.css"
import type { ReactNode } from "react"
const nunitoSans = Nunito_Sans({ variable: "--font-nunito-sans", display: "swap", preload: true, subsets: ["latin"] })
export const viewport: Viewport = {
themeColor: "#000000",
width: "device-width",
initialScale: 1,
maximumScale: 5,
userScalable: true,
}
export const metadata: Metadata = {
title: `${APP_INFO.APP_NAME} | Cinematic Storytelling & Film Production`,
description: "Generated by create next app",
}
export default function RootLayout({
children,
}: Readonly<{
children: ReactNode
}>) {
return (
<html lang="en" className={nunitoSans.variable}>
<body className="bg-zinc-950 text-zinc-100 selection:bg-amber-300/20 selection:text-amber-200 heritage-motif">
<TopProgressBar />
<ClientLayoutWrapper>
{children}
{/* Toast Notifications */}
<Toaster
position="bottom-right"
expand={false}
richColors
closeButton
duration={3000}
toastOptions={{
style: {
fontFamily: nunitoSans.className,
},
}}
/>
</ClientLayoutWrapper>
</body>
</html>
)
}below is the
/src/apps/page.tsx:import type { Metadata } from "next"
export default function Page() {
return (
<>
<section className="relative overflow-hidden">
<HomeHeroSection />
<PartnerStrip />
</section>
<FeaturedFilmography />
<section className="relative">
<HomeTeamSection />
<HomeNewsroomBlog />
<HomeNewsletter />
</section>
</>
)
}below is the
/@/components/page-components/home-page/hero-section.tsx:"use client"
import "client-only"
import Link from "next/link"
import type { FC } from "react"
import EasedCountUp from "@/components/library/eased-count-up"
import { MAIN_PARENT_LAYOUT } from "@/ui"
import WatchTrialBtnModal from "./watch-trial-btn-modal"
const HomeHeroSection: FC = () => {
return (
<>
<div className={MAIN_PARENT_LAYOUT}>
<div className="grain relative">
<div className="pt-24 pb-14 sm:pt-28 sm:pb-16">
<div className="mt-8 grid gap-10 lg:grid-cols-12 lg:items-end">
<div className="lg:col-span-7">
<div className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/5 px-3 py-1 text-xs text-zinc-200">
<dl className="mt-10 grid max-w-2xl grid-cols-3 gap-4">
<div className="rounded-2xl border border-white/10 bg-white/5 px-4 py-4">
<dt className="text-xs text-zinc-300">Films Produced</dt>
<dd className="mt-1 text-2xl font-extrabold" data-counter="28">
<EasedCountUp end={28} />
</dd>
</div>
<div className="rounded-2xl border border-white/10 bg-white/5 px-4 py-4">
<dt className="text-xs text-zinc-300">Awards</dt>
<dd className="mt-1 text-2xl font-extrabold" data-counter="54">
<EasedCountUp end={54} />
</dd>
</div>
<div className="rounded-2xl border border-white/10 bg-white/5 px-4 py-4">
<dt className="text-xs text-zinc-300">Languages</dt>
<dd className="mt-1 text-2xl font-extrabold" data-counter="6">
<EasedCountUp end={6} />
</dd>
</div>
</dl>
</div>and below is the
eased-count-up.tsx:"use client"
import "client-only"
import { type FC, useEffect, useId, useMemo } from "react"
import { useCountUp } from "react-countup"
import { useInView } from "react-intersection-observer"
import { formatCompact } from "@/utils/format-compact"
export interface EasedCountUpProps {
end: number
/** seconds (react-countup) */
duration?: number
/** IntersectionObserver threshold */
threshold?: number
className?: string
}
const EasedCountUp: FC<EasedCountUpProps> = (props) => {
const { end, className = "", duration = 0.9, threshold = 0.4 } = props
// useId() contains ":" which isn't great for DOM ids -> sanitize
const reactId = useId()
const id = useMemo(() => `countup-${reactId.replace(/:/g, "")}`, [reactId])
const { ref: inViewRef, inView } = useInView({ triggerOnce: true, threshold })
const { start, reset } = useCountUp({
ref: id,
start: 0,
end,
duration: duration,
startOnMount: false,
// CountUp.js signature: easingFn(t, b, c, d)
// t = elapsed ms, b = startVal, c = end-start, d = duration ms
easingFn: (t, b, c, d) => {
const n = d ? Math.min(1, t / d) : 1 // normalize 0..1
return b + c * (0.12 + 0.88 * n) // EXACT same curve
},
formattingFn: formatCompact,
})
useEffect(() => {
if (!inView) return
reset()
start()
}, [inView, reset, start])
return (
<span id={id} ref={inViewRef} className={className}>
0
</span>
)
}
export default EasedCountUpand this is my
next.config.ts:import type { NextConfig } from "next"
const nextConfig: NextConfig = {
/* config options here */
reactCompiler: true,
reactStrictMode: true,
experimental: {
inlineCss: true,
},
}
export default nextConfigbut during the
npm run build and then npm run start at this point only, my this EasedCountUp is not working at all, it always stays 0