Rendering new Date() in a Server Component Is a Silent Timezone Bug Waiting to Happen

1 13
calendar_today agoschedule3 min read

If you've ever seen a hydration mismatch warning in the console for a timestamp, and just suppressed it with suppressHydrationWarning because the page still worked, it's worth actually understanding what that warning was telling you, because the underlying issue doesn't go away just because the warning did.

The Setup That Looks Completely Normal

// app/dashboard/page.tsx
export default function DashboardPage() {
  return (
    <div>
      <h1>Dashboard</h1>
      <p>Last updated: {new Date().toLocaleString()}</p>
    </div>
  );
}

This renders a timestamp. It looks correct in the browser. It also has two separate problems stacked on top of each other, one causing a visible React warning, the other causing a genuinely wrong displayed value that never throws any warning at all.

Problem One: The Hydration Mismatch

The server renders this component once, at whatever exact moment the request is processed, producing a specific timestamp string. Then React hydrates that same component on the client, and new Date().toLocaleString() runs again, this time at whatever moment hydration actually happens, milliseconds or sometimes longer after the server render. Two different calls to new Date(), two different actual timestamps, and React's hydration process expects the server-rendered HTML and the client's first render to match exactly. When they don't, React logs a hydration mismatch warning and, depending on the specifics, may discard and re-render the mismatched content client-side.

Problem Two, the One That Doesn't Even Warn You: Timezone

This is the one that actually causes wrong data, not just a console warning. toLocaleString() without an explicit locale and timezone uses the runtime environment's local timezone setting, and the server's runtime environment and the visitor's browser are very often not in the same timezone at all. A server running in UTC, common for most cloud infrastructure, formats "3:00 PM" in UTC. If a visitor's browser is in a different timezone, the hydrated client-side render of the exact same underlying timestamp shows a completely different clock time, "3:00 PM" during server rendering, silently replaced by something like "10:00 AM" once client-side hydration takes over and reformats it in the visitor's actual local timezone.

Neither of these values is actually wrong in isolation, they're both technically correct representations of the same underlying moment. The problem is a visitor briefly seeing one, then having it silently change to a different clock time right in front of them, or worse, seeing whichever version happens to persist without ever realizing the other one was theoretically shown first.

Why Suppressing the Warning Doesn't Fix Anything

// This makes the console warning go away. It does not fix the actual mismatch.
<p suppressHydrationWarning>Last updated: {new Date().toLocaleString()}</p>

suppressHydrationWarning tells React to stop complaining about a mismatch on this specific element, it does nothing to prevent the mismatch from happening, and it does nothing about the underlying timezone confusion. It's treating the symptom, a noisy console warning, while leaving the actual bug, a timestamp that renders differently depending on server versus client timezone, completely intact.

The Actual Fix: Format Explicitly, or Defer to the Client Deliberately

Option one, format with an explicit, fixed timezone, removing ambiguity entirely by never relying on either environment's local timezone setting:

export default function DashboardPage({ lastUpdated }: { lastUpdated: Date }) {
  const formatted = lastUpdated.toLocaleString('en-US', {
    timeZone: 'UTC',
    dateStyle: 'medium',
    timeStyle: 'short',
  });

  return <p>Last updated: {formatted} UTC</p>;
}

This produces the same, deterministic string regardless of where the server runs or where the visitor's browser happens to be, since the timezone is explicitly fixed rather than implicitly inferred from either environment.

Option two, if you genuinely want the visitor's own local time, defer that specific formatting to the client deliberately, rather than accidentally:

'use client';
import { useState, useEffect } from 'react';

export function LocalTimestamp({ date }: { date: string }) {
  const [formatted, setFormatted] = useState<string | null>(null);

  useEffect(() => {
    setFormatted(new Date(date).toLocaleString());
  }, [date]);

  return <span>{formatted ?? 'Loading...'}</span>;
}

Rendering nothing (or a neutral placeholder) on the server, then formatting in the visitor's actual local timezone only after mounting client-side, means there's no mismatch to begin with, since the server never rendered a locale-specific value for the client to potentially disagree with in the first place.

The Rule Worth Remembering

Anything genuinely time-sensitive or locale-sensitive rendered in a Server Component needs an explicit, fixed format, not an implicit one that depends on which environment happens to be doing the formatting. If you actually want the visitor's own local time specifically, that formatting decision belongs on the client, deliberately, not as an accidental side effect of a Server Component's output silently disagreeing with its own hydration.


If you've got a suppressHydrationWarning sitting on a timestamp somewhere in your codebase, worth going back and checking whether it's actually just hiding this exact timezone issue rather than a harmless cosmetic mismatch. Drop what you find in the comments.

Get the templates: https://pixelanas.gumroad.com


Anas, full-stack Next.js developer building SaaS products and premium templates. X: @ASheikh69751

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

TypeScript Complexity Has Finally Reached the Point of Total Absurdity

Karol Modelski - Apr 23

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelski - Mar 19

How I Built a React Portfolio in 7 Days That Landed ₹1.2L in Freelance Work

Dharanidharan - Feb 9

Merancang Backend Bisnis ISP: API Pelanggan, Paket Internet, Invoice, dan Tiket Support

Masbadar - Mar 13

Sovereign Intelligence: The Complete 25,000 Word Blueprint (Download)

Pocket Portfolio - Apr 1
chevron_left
420 Points14 Badges
14Posts
2Comments
1Connections
Full-stack developer building premium Next.js templates & web apps. Specializing in GSAP animations,... Show more

Related Jobs

View all jobs →

Commenters (This Week)

5 comments
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!