HextaUI

useToday

日付が変わる時刻とタブに戻ったときに切り替わる今日の日付。ハイドレーションの不一致は起こりません。

"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

フックと、その依存関係をプロジェクトに追加します。

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

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

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

今日のハイライト、過去の日の無効化、締め切りまでのカウントダウンなど、コンポーネントが現在の日付に依存するときはいつでも使います。

const today = new Date()

レンダリング中に日付を読み取ると、2つの点で壊れます。

  • ハイドレーションの不一致。サーバーは自分のタイムゾーンで描画するため、数時間前の可能性があります。深夜0時付近では、サーバーとブラウザーで日付が食い違い、React はサーバーの HTML を破棄します。
  • 古い日付。一晩開いたままのタブは、何かが再レンダリングするまで、昨日を今日として表示し続けます。

useToday は、サーバー上とハイドレーション中は undefined を返し、その後ローカルの日付を返します。バックグラウンドのタブではタイマーが一時停止して午前0時を逃すことがあるため、午前0時とタブが再び表示されたときにも再レンダリングします。

日付の範囲

過去の日と、30日より先のすべてを無効にします。今日が判明するまでは何も無効にならないため、サーバーの HTML は最初のクライアントのレンダリングと一致します。

"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())

このファイルは、フックの構成要素もエクスポートします。日付キーは、ローカルの YYYY-MM-DD 文字列です。日単位で比較でき、正しく並べ替えられ、React の key やキャッシュキーとして安全に使えます。subscribeToday は、React の外で、午前0時とタブに戻ったときにコールバックします。

  • 返される Date はローカル時間の午前0時なので、< や > と比較すると、時刻ではなく日付が比較されます。
  • 日が変わるまで同じ Date オブジェクトが返されるため、effect の依存配列に安全に使えます。
  • undefined の場合のレンダリングも考慮してください。スケルトンを表示するか、日付に基づく制限なしで描画するかのどちらでも構いません。
戻り値説明
Date | undefined今日のローカルの午前0時。サーバー上とハイドレーション中は undefined です。
エクスポート説明
toDateKey(date)Date をローカルの YYYY-MM-DD 文字列にしたもの。
fromDateKey(key)YYYY-MM-DD 文字列をローカルの午前0時の Date にしたもの。
getTodayKey()今日の日付キー。
subscribeToday(callback)午前0時とタブが表示状態に戻ったときにコールバックします。購読解除の関数を返します。

Calendar と、それを通じた Date picker。