I am getting a hydration error in my project, but it is appearing only in the telegram. I tried to open it from Safari, chrome, and Opera in my locale and also in production. There is no error. But when i opened it from the telegram i am getting this hydration error.
I am using "next": "15.1.6"
and "react": "^19.0.0"
,
Since I see -playsinline="" at the bottom of the error, I thought it was because of the hero component that contains video background getting from a CDN.
You can reach out to the suspected code blocks from below;
this is my hero-video-player.tsx
"use client"
const HeroVideoPlayer = ({
onLoad,
onError,
videoFile,
}: {
onLoad: () => void
onError: (error: string) => void
videoFile: string
}) => {
return (
<video
autoPlay={true}
loop={true}
muted={true}
playsInline={true}
preload="metadata" // Changed from auto to metadata for faster initial load
controls={false}
onLoadedData={onLoad}
onError={(e) => {
onError(
e.currentTarget.error?.message || "Video failed to load"
)
}}
className={`absolute top-0 left-0 w-full h-full object-cover transition-opacity duration-500`}
suppressHydrationWarning
disablePictureInPicture // Add for better mobile compatibility
controlsList="nodownload noplaybackrate" // Prevent download and speed controls
>
<source src={videoFile} type="video/mp4" />
<track kind="captions" /> {/* Add empty track to prevent warning */}
</video>
)
}
export default HeroVideoPlayer
this is my hero-countdown.tsx
"use client"
import React, { useState, useEffect } from "react"
import CountdownBorder from "../../public/hero/countdown-border"
import { useTranslations } from "next-intl"
type HeroCountdownProps = {
targetDate: string
}
function HeroCountdown({ targetDate }: HeroCountdownProps) {
const t = useTranslations("hero")
const [mounted, setMounted] = useState(false)
const calculateTimeLeft = () => {
const now = new Date().getTime()
const difference = new Date(targetDate).getTime() - now
if (difference <= 0) {
return { days: 0, hours: 0, minutes: 0 }
}
const days = Math.floor(difference / (1000 * 60 * 60 * 24))
const hours = Math.floor(
(difference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
)
const minutes = Math.floor(
(difference % (1000 * 60 * 60)) / (1000 * 60)
)
return { days, hours, minutes }
}
const [timeLeft, setTimeLeft] = useState({ days: 0, hours: 0, minutes: 0 })
useEffect(() => {
setMounted(true)
setTimeLeft(calculateTimeLeft())
const interval = setInterval(() => {
setTimeLeft(calculateTimeLeft())
}, 1000)
return () => clearInterval(interval)
}, [])
if (!mounted) {
return null // or a loading placeholder
}
return (
<div className="w-full flex flex-row items-center justify-center space-x-2 sm:space-x-4 xl:space-x-12 h-28 xl:h-40 mx-auto mt-8 md:mx-0">
<div className="h-full flex items-center justify-around space-x-4">
<p className="text-white text-3xl lg:text-7xl font-bold">
{timeLeft && timeLeft?.days
? timeLeft?.days?.toString().padStart(2, "0")
: "00"}
</p>
<p className="text-2xl lg:text-5xl font-extralight text-white">
{t("gun")}
</p>
</div>
<CountdownBorder />
<div className="h-full flex items-center justify-around space-x-4">
<p className="text-white text-3xl lg:text-7xl font-bold">
{timeLeft && timeLeft?.hours
? timeLeft?.hours?.toString().padStart(2, "0")
: "00"}
</p>
<p className="text-2xl lg:text-5xl font-extralight text-white">
{t("saat")}
</p>
</div>
<CountdownBorder />
<div className="h-full flex items-center justify-around space-x-4">
<p className="text-white text-3xl lg:text-7xl font-bold">
{timeLeft && timeLeft?.minutes
? timeLeft?.minutes?.toString().padStart(2, "0")
: "00"}
</p>
<p className="text-2xl lg:text-5xl font-extralight text-white">
{t("dakika")}
</p>
</div>
</div>
)
}
export default `HeroCountdown`
this is my hero.tsx
"use client"
import React, { useState, useEffect } from "react"
import { format } from "date-fns"
import HeroCountdown from "./hero-countdown"
import Link from "next/link"
import { useLocale, useTranslations } from "next-intl"
import { enGB, tr } from "date-fns/locale"
import { poppins, raleway } from "@/fonts"
import HeroVideoPlayer from "./hero-video-player"
type HeroProps = {
time: string[]
name: string
abbreviation: string
videoFile: string
}
function Hero({ time, name, abbreviation, videoFile }: HeroProps) {
const t = useTranslations("hero")
const currentLocale = useLocale()
const [mounted, setMounted] = useState(false)
const [formattedDates, setFormattedDates] = useState({
startDate: "",
endDate: "",
month: "",
})
const [isVideoLoaded, setIsVideoLoaded] = useState(false)
const [videoError, setVideoError] = useState<string | null>(null)
useEffect(() => {
setMounted(true)
setFormattedDates({
startDate: time[0] ? format(new Date(time[0]), "dd") : "",
endDate: time[1] ? format(new Date(time[1]), "dd") : "",
month: time[0]
? format(new Date(time[0]), "MMMM", {
locale:
currentLocale.toLowerCase() === "tr" ? tr : enGB,
})
: "",
})
}, [time, currentLocale])
return (
<div className="w-full">
<div className="relative w-full min-h-[calc(100vh-80px)] flex flex-1 justify-center items-end py-10 lg:py-0 lgulu:pb-20 2xl:pb-40 overflow-hidden">
{mounted && (
<HeroVideoPlayer
onLoad={() => setIsVideoLoaded(true)}
onError={(msg) => setVideoError(msg)}
videoFile={videoFile}
/>
)}
{/* Loading overlay */}
<div
className={`absolute top-0 left-0 w-full h-full bg-black transition-opacity duration-500 ${
isVideoLoaded ? "opacity-0" : "opacity-100"
}`}
/>
{/* Error display */}
{videoError && (
<div className="absolute top-4 left-4 bg-red-500 text-white p-2 rounded">
{videoError}
</div>
)}
{/* Gradient overlay */}
<div className="absolute top-0 left-0 w-full h-full bg-hero-gradient" />
{/* Content */}
<div className="relative flex flex-col justify-end items-center space-y-10 z-10">
<div className="flex flex-col">
<h2
className={`${poppins.className} font-semibold text-2xl lg:text-5xl text-[#F0F0F0] mb-4 text-center capitalize`}
>
{formattedDates.startDate} -{" "}
{formattedDates.endDate} {formattedDates.month}
</h2>
<h2
className={`${raleway.className} font-bold text-3xl lg:text-5xl text-[#ADE0F5] text-center uppercase px-6`}
>
{name}
</h2>
<h3
className={`${raleway.className} font-extrabold text-5xl lg:text-7xl text-white text-center uppercase tracking-[1.8rem]`}
>
{abbreviation}
</h3>
</div>
<HeroCountdown targetDate={time[0]} />
<div className="w-full flex flex-row flex-wrap items-center justify-center gap-3 py-5 lg:p-12">
<Link
href="/bildiri-gonderimi"
className="w-full max-w-72 bg-button-light-color text-xl text-text-light-color text-center font-medium p-3 rounded-xl hover:bg-zinc-300 transition-all duration-300"
>
{t("bildiri")}
</Link>
<Link
href="/katilimci"
className="w-full max-w-72 bg-main-red text-xl p-3 rounded-xl text-white font-medium text-center hover:bg-main-red/80 transition-all duration-300"
>
{t("kayit")}
</Link>
<Link
href="/konusmaci"
className="w-full max-w-72 bg-button-light-color text-xl text-text-light-color text-center font-medium p-3 rounded-xl hover:bg-zinc-300 transition-all duration-300"
>
{t("konusmaci")}
</Link>
</div>
</div>
</div>
</div>
)
}
export default Hero
and if you need at the page i am using hero component this is the page.tsx
import { GetCongressInfo } from "@/api/congress/get-congress"
import { GetFaq } from "@/api/faq/get-questions"
import { GetHero } from "@/api/hero/get-hero"
import { GetSocials } from "@/api/socials/get-socials"
import About from "@/components/about"
import EventInfo from "@/components/event-info"
import Faq from "@/components/faq"
import GeneralInfo from "@/components/general-info"
import Guests from "@/components/guests"
import OrganizationContact from "@/components/anization-contact"
import SendEmail from "@/components/send-email"
import Social from "@/components/social"
import { getLocale, getTranslations } from "next-intl/server"
import Hero from "@/components/hero"
export default async function Home() {
const locale = await getLocale()
const congress = await GetCongressInfo(locale.toLowerCase())
const hero = await GetHero()
const questions = await GetFaq(locale.toLowerCase())
const social = await GetSocials()
const t = await getTranslations("home")
if (!congress) {
return (
<div className="flex flex-col min-h-[calc(100vh-80px)] flex-1 justify-center items-center space-y-4">
<h2 className="text-main-red text-6xl font-extrabold text-center">
{t("error")}
</h2>
<h3 className="text-text-color text-3xl font-bold text-center">
{t("error-text")}
</h3>
</div>
)
}
return (
<div className="overflow-x-hidden">
<Hero
time={
congress && congress[0] && congress[0]?.date.length > 0
? congress[0]?.date
: []
}
name={
congress && congress[0]?.name
? (congress[0].name as string)
: ""
}
abbreviation={
congress && congress[0]?.name
? (congress[0]?.abbreviation as string)
: ""
}
videoFile={hero ? (hero[0]?.hero_video_file as string) : ""}
/>
<GeneralInfo address={congress ? congress[0]?.address : ""} />
{/* <Register /> */}
<Guests />
{/* <SessionTable /> */}
<EventInfo congress={congress ? congress[0] : null} />
<About congress={congress ? congress[0] : null} />
{/* <HonorList /> */}
<OrganizationContact />
<Social social={social && social[0] ? social[0] : null} />
<Faq questions={questions ? questions : null} />
<SendEmail email={"[email protected]"} />
</div>
)
}