├── .eslintrc.json
├── src
├── app
│ ├── favicon.ico
│ ├── page.tsx
│ ├── example-2-with-supressed-hydration-warning
│ │ └── page.tsx
│ ├── example-1-without-supressed-hydration-warning
│ │ └── page.tsx
│ ├── example-3-combined
│ │ └── page.tsx
│ ├── layout.tsx
│ └── globals.css
└── components
│ └── LocalizedDateTime.tsx
├── next.config.mjs
├── .gitignore
├── package.json
├── public
├── vercel.svg
└── next.svg
├── tsconfig.json
└── README.md
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "next/core-web-vitals"
3 | }
4 |
--------------------------------------------------------------------------------
/src/app/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/derweili/nextjs-hydration-issue/main/src/app/favicon.ico
--------------------------------------------------------------------------------
/next.config.mjs:
--------------------------------------------------------------------------------
1 | /** @type {import('next').NextConfig} */
2 | const nextConfig = {};
3 |
4 | export default nextConfig;
5 |
--------------------------------------------------------------------------------
/src/app/page.tsx:
--------------------------------------------------------------------------------
1 |
2 | export default function Home() {
3 | return (
4 | <>
5 |
6 | >
7 | );
8 | }
9 |
--------------------------------------------------------------------------------
/src/app/example-2-with-supressed-hydration-warning/page.tsx:
--------------------------------------------------------------------------------
1 | import LocalizedDateTime from '@/components/LocalizedDateTime'
2 |
3 | const PageWithSupressedHydrationWarning = () => {
4 | return (
5 | <>
6 |
Example 2:
7 | Localized date with supressed hydration warning:
8 |
12 | >
13 | )
14 | }
15 |
16 | export default PageWithSupressedHydrationWarning
--------------------------------------------------------------------------------
/src/app/example-1-without-supressed-hydration-warning/page.tsx:
--------------------------------------------------------------------------------
1 | import LocalizedDateTime from '@/components/LocalizedDateTime'
2 |
3 | const PageWithoutSupressedHydrationWarning = () => {
4 | return (
5 | <>
6 | Example 1:
7 | Localized date without supressed hydration warning:
8 |
12 | >
13 | )
14 | }
15 |
16 | export default PageWithoutSupressedHydrationWarning
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 | .yarn/install-state.gz
8 |
9 | # testing
10 | /coverage
11 |
12 | # next.js
13 | /.next/
14 | /out/
15 |
16 | # production
17 | /build
18 |
19 | # misc
20 | .DS_Store
21 | *.pem
22 |
23 | # debug
24 | npm-debug.log*
25 | yarn-debug.log*
26 | yarn-error.log*
27 |
28 | # local env files
29 | .env*.local
30 |
31 | # vercel
32 | .vercel
33 |
34 | # typescript
35 | *.tsbuildinfo
36 | next-env.d.ts
37 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nextjs-hydration-issue",
3 | "version": "0.1.0",
4 | "private": true,
5 | "scripts": {
6 | "dev": "next dev",
7 | "build": "next build",
8 | "start": "next start",
9 | "lint": "next lint"
10 | },
11 | "dependencies": {
12 | "react": "^18",
13 | "react-dom": "^18",
14 | "next": "14.1.0"
15 | },
16 | "devDependencies": {
17 | "typescript": "^5",
18 | "@types/node": "^20",
19 | "@types/react": "^18",
20 | "@types/react-dom": "^18",
21 | "eslint": "^8",
22 | "eslint-config-next": "14.1.0"
23 | }
24 | }
--------------------------------------------------------------------------------
/public/vercel.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "lib": ["dom", "dom.iterable", "esnext"],
4 | "allowJs": true,
5 | "skipLibCheck": true,
6 | "strict": true,
7 | "noEmit": true,
8 | "esModuleInterop": true,
9 | "module": "esnext",
10 | "moduleResolution": "bundler",
11 | "resolveJsonModule": true,
12 | "isolatedModules": true,
13 | "jsx": "preserve",
14 | "incremental": true,
15 | "plugins": [
16 | {
17 | "name": "next"
18 | }
19 | ],
20 | "paths": {
21 | "@/*": ["./src/*"]
22 | }
23 | },
24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
25 | "exclude": ["node_modules"]
26 | }
27 |
--------------------------------------------------------------------------------
/src/app/example-3-combined/page.tsx:
--------------------------------------------------------------------------------
1 | import LocalizedDateTime from '@/components/LocalizedDateTime'
2 |
3 | const PageWithSupressedHydrationWarning = () => {
4 | return (
5 | <>
6 | Example 3 (both combined on one page)
7 | Localized date with supressed hydration warning:
8 |
12 | Localized date without supressed hydration warning:
13 |
17 | >
18 | )
19 | }
20 |
21 | export default PageWithSupressedHydrationWarning
--------------------------------------------------------------------------------
/src/components/LocalizedDateTime.tsx:
--------------------------------------------------------------------------------
1 | 'use client';
2 |
3 | export interface LocalizedDateTimeProps {
4 | eventDate: string;
5 | supressHydrationWarning?: boolean;
6 | }
7 |
8 | const LocalizedDateTime = ({ eventDate, supressHydrationWarning }: LocalizedDateTimeProps) => {
9 | const localizedDate = new Intl.DateTimeFormat('en-GB', {
10 | day: 'numeric',
11 | month: 'short',
12 | hour: 'numeric',
13 | minute: 'numeric',
14 | timeZoneName: 'short'
15 | }).format(new Date(eventDate));
16 |
17 | // This is always logged to the browser console
18 | console.log({localizedDate});
19 |
20 | // When suppressHydrationWarning is used
21 | // The server output will never be overwritten
22 | // by the browser
23 | return (
24 |
25 | {localizedDate}
26 |
27 | );
28 | };
29 |
30 | export default LocalizedDateTime;
31 |
--------------------------------------------------------------------------------
/src/app/layout.tsx:
--------------------------------------------------------------------------------
1 | import type { Metadata } from "next";
2 | import "./globals.css";
3 |
4 | export const metadata: Metadata = {
5 | title: "Create Next App",
6 | description: "Generated by create next app",
7 | };
8 |
9 | export default function RootLayout({
10 | children,
11 | }: Readonly<{
12 | children: React.ReactNode;
13 | }>) {
14 | return (
15 |
16 |
17 |
39 |
40 | {children}
41 |
42 |
43 |
44 | );
45 | }
46 |
--------------------------------------------------------------------------------
/public/next.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).
2 |
3 | ## Getting Started
4 |
5 | First, run the development server:
6 |
7 | ```bash
8 | npm run dev
9 | # or
10 | yarn dev
11 | # or
12 | pnpm dev
13 | # or
14 | bun dev
15 | ```
16 |
17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
18 |
19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
20 |
21 | This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
22 |
23 | ## Learn More
24 |
25 | To learn more about Next.js, take a look at the following resources:
26 |
27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
29 |
30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!
31 |
32 | ## Deploy on Vercel
33 |
34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
35 |
36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.
37 |
--------------------------------------------------------------------------------
/src/app/globals.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --max-width: 1100px;
3 | --border-radius: 12px;
4 | --font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
5 | "Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
6 | "Fira Mono", "Droid Sans Mono", "Courier New", monospace;
7 |
8 | --foreground-rgb: 0, 0, 0;
9 | --background-start-rgb: 214, 219, 220;
10 | --background-end-rgb: 255, 255, 255;
11 | }
12 |
13 | * {
14 | box-sizing: border-box;
15 | padding: 0;
16 | margin: 0;
17 | }
18 |
19 | html,
20 | body {
21 | max-width: 100vw;
22 | overflow-x: hidden;
23 | }
24 |
25 | body {
26 | color: rgb(var(--foreground-rgb));
27 | background-image: linear-gradient(
28 | to bottom,
29 | transparent,
30 | rgb(var(--background-end-rgb))
31 | );
32 | background-color: rgb(var(--background-start-rgb));
33 | background-size: cover;
34 | min-height: 100vh;
35 | font-family: --apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen,
36 | Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
37 | }
38 |
39 | a {
40 | color: inherit;
41 | text-decoration: none;
42 | }
43 |
44 |
45 | nav ul {
46 | display: flex;
47 | justify-content: center;
48 | list-style: none;
49 | margin: 1rem;
50 | gap: 20px;
51 | }
52 |
53 | nav li a {
54 | border: 1px solid #444;
55 | padding: 0.5rem 1rem;
56 | border-radius: 4px;
57 | }
58 |
59 | nav li a:hover {
60 | background-color: #444;
61 | color: white;
62 | }
63 |
64 | main {
65 | max-width: var(--max-width);
66 | margin: 0 auto;
67 | padding: 2rem;
68 | }
--------------------------------------------------------------------------------