HextaUI

useToday

Today's date that rolls over at midnight and when the tab comes back, without a hydration mismatch.

"use client"

import { Skeleton } from "@/components/ui/skeleton"
import { useToday } from "@/hooks/use-today"

const formatter = new Intl.DateTimeFormat(undefined, { dateStyle: "full" })

function daysUntilNewYear(today: Date) {
  const next = new Date(today.getFullYear() + 1, 0, 1)
  return Math.round((next.getTime() - today.getTime()) / 86_400_000)
}

export function UseTodayDemo() {
  const today = useToday()

  return (
    <div className="flex flex-col items-center gap-1 text-center">
      {today ? (
        <>
          <p className="text-lg font-medium">{formatter.format(today)}</p>
          <p className="text-sm text-muted-foreground">
            {daysUntilNewYear(today)} days until New Year
          </p>
        </>
      ) : (
        <>
          <Skeleton className="h-6 w-56" />
          <Skeleton className="mt-1 h-4 w-36" />
        </>
      )}
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/use-today.json

Adds the hook and anything it depends on to your project.

import { useToday } from "@/hooks/use-today"
const today = useToday()

if (!today) {
  return <Skeleton className="h-5 w-40" />
}

return <p>{format(today, "PPPP")}</p>

Reach for it whenever a component depends on the current date, such as highlighting today, disabling past days or counting down to a deadline.

const today = new Date()

Reading the date while rendering breaks in two ways:

  • Hydration mismatches. The server renders in its time zone, possibly hours earlier. Near midnight, server and browser disagree on the day, and React throws away the server HTML.
  • Stale dates. A tab left open overnight keeps showing yesterday as today until something else re-renders it.

useToday returns undefined on the server and during hydration, then the local date. It also re-renders at midnight and when the tab becomes visible again, because timers in background tabs can be paused and miss midnight.

Date bounds

Disable past days and anything more than 30 days out. Until today is known, nothing is disabled, so the server HTML matches the first client render.

October 2026
"use client"

import { addDays } from "date-fns"

import { Calendar } from "@/components/ui/calendar"
import { useToday } from "@/hooks/use-today"

export function UseTodayBounds() {
  const today = useToday()

  return (
    <Calendar
      mode="single"
      disabled={today ? [{ before: today }, { after: addDays(today, 30) }] : []}
    />
  )
}
import {
  fromDateKey,
  getTodayKey,
  subscribeToday,
  toDateKey,
} from "@/hooks/use-today"

toDateKey(new Date(2026, 9, 5))
fromDateKey("2026-10-05")

const unsubscribe = subscribeToday(() => refreshAgenda())

The file also exports the pieces the hook is built from. Date keys are local YYYY-MM-DD strings. They compare by day, sort correctly and are safe to use as React keys or cache keys. subscribeToday calls you back at midnight and on tab return, outside React.

  • The returned Date is midnight in local time, so comparing it with < or > compares days, not times.
  • The same Date object comes back until the day changes, so it's safe to use in effect dependencies.
  • Plan for the undefined render. A skeleton, or rendering without date-based limits, both work.
ReturnsDescription
Date | undefinedLocal midnight today. undefined on the server and while hydrating.
ExportDescription
toDateKey(date)A Date as a local YYYY-MM-DD string.
fromDateKey(key)A YYYY-MM-DD string as a local-midnight Date.
getTodayKey()Today's date key.
subscribeToday(callback)Calls back at midnight and when the tab becomes visible. Returns an unsubscribe function.

Calendar, and Date picker through it.