Server‑side date serialization vs client‑side timezone conversion in Next.js
0 reputation · 10 Oct 2022, 22:01 UTC
0 reputation · 10 Oct 2022, 22:01 UTC
The goal is to present dates correctly for users whose preferred time zone is only known at runtime (e.g., from a profile or browser API) while keeping the UI consistent between server‑rendered HTML and the subsequent client hydration.
Constraints: getStaticProps runs at build time and cannot adapt to per‑user zones; getServerSideProps executes on the server where the default time zone may differ from the user’s; performing conversion on the client can cause a flash of UTC‑based content before the client applies Intl.DateTimeFormat with the correct zone.
getServerSideProps?34775 reputation · 11 Oct 2022, 07:00 UTC
To maintain consistency and avoid hydration mismatches, you should keep dates as UTC ISO strings in server-side props and perform the timezone conversion exclusively on the client. This ensures the server renders a deterministic value that matches the initial client-side render, preventing the "flash" of incorrect content from triggering a React hydration error.
Hydration errors occur when the HTML generated by the server differs from the first render on the client. If you use toLocaleString() or Intl.DateTimeFormat during the initial render, the server (likely running in UTC) produces one string, while the browser (running in the user's local zone) produces another. React detects this discrepancy and may throw a warning or fail to attach event listeners correctly.
getServerSideProps or getStaticProps, ensure dates are passed as strings: date.toISOString().useEffect hook or a state-managed wrapper to handle the conversion. This ensures the conversion happens after the initial hydration.// Example Client Component
import { useState, useEffect } from 'react';
export default function LocalizedDate({ utcDate }) {
const [formattedDate, setFormattedDate] = useState(null);
useEffect(() => {
// This runs only on the client after hydration
setFormattedDate(new Date(utcDate).toLocaleString());
}, [utcDate]);
// Return null or a skeleton to avoid the UTC flash,
// or return the UTC date as a fallback
return {formattedDate || 'Loading...'};
}
Deriving the timezone from request headers or cookies in getServerSideProps is technically viable but often impractical. While you can use a cookie to store the user's IANA timezone, this requires an initial client-side request to set that cookie. If the cookie is missing, you still fall back to a default, which may cause inconsistencies for first-time visitors.
To verify your implementation, use the following checks:
Intl API is responding to the system clock.Missing Diagnostic: Are you using a specific date library (e.g., date-fns, Day.js) or the native Intl API? Library-specific serialization can sometimes introduce hidden offsets that affect the toISOString() output.
Use comments to ask for clarification. Post a solution as an answer.
34,775 reputation · 11 Oct 2022, 00:54 UTC
To prevent the "flash" of UTC content or hydration errors, it is important to distinguish between serialization and presentation. Since getServerSideProps and getStaticProps serialize data to JSON, passing dates as ISO 8601 strings is the standard approach to ensure data integrity across the network boundary.
To avoid the hydration mismatch where the server renders one timezone and the client another, consider these implementation patterns:
useEffect hook to set a hasMounted state to true. Only render the localized date after the component has mounted on the client.suppressHydrationWarning attribute can prevent React from flagging the difference, though it does not eliminate the visual jump.Verification: Check the browser console for "Hydration failed because the initial UI does not match what was rendered on the server" when using Intl.DateTimeFormat directly in the JSX return statement.