Skip to content

Conversation

@djstrong
Copy link
Collaborator

No description provided.

@changeset-bot
Copy link

changeset-bot bot commented Jan 19, 2026

⚠️ No Changeset found

Latest commit: aaab5d0

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel
Copy link
Contributor

vercel bot commented Jan 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
examples.nameguard.io Ready Ready Preview, Comment Jan 19, 2026 4:56pm
nameai.io Ready Ready Preview, Comment Jan 19, 2026 4:56pm
namegraph.dev Ready Ready Preview, Comment Jan 19, 2026 4:56pm
nameguard.io Ready Ready Preview, Comment Jan 19, 2026 4:56pm
namehashlabs.org Ready Ready Preview, Comment Jan 19, 2026 4:56pm
namekit.io Ready Ready Preview, Comment Jan 19, 2026 4:56pm
storybook.namekit.io Ready Ready Preview, Comment Jan 19, 2026 4:56pm

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request updates Next.js from a canary version (15.1.1-canary.5) to a stable release (15.1.9), along with updating React from 19.0.0 to 19.0.1 and associated dependencies. The changes also include fixes for server-side rendering (SSR) compatibility issues related to browser APIs.

Changes:

  • Updated Next.js, React, and eslint-config-next to newer stable versions
  • Added SSR-safe guards for browser API usage in client components
  • Disabled experimental PPR feature that's only available in canary versions

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pnpm-lock.yaml Updated dependency versions for Next.js (15.1.9), React (19.0.1), eslint-config-next (15.1.9), and related packages
packages/namekit-react/src/components/Tooltip.tsx Added isMounted state to conditionally enable portal rendering after component mount, fixing SSR hydration issues
apps/namekit.io/src/components/pages/namekit.tsx Added window check to prevent SSR errors when accessing document in useEffect
apps/namekit.io/src/app/page.tsx Added dynamic rendering configuration to force server-side rendering at request time
apps/namekit.io/next.config.js Added empty experimental configuration object with comments
apps/nameai.io/package.json Updated Next.js, React, and eslint-config-next versions to match the main lock file
apps/nameai.io/next.config.ts Disabled PPR experimental feature with explanatory comments
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

pnpm-lock.yaml Outdated
Comment on lines 5220 to 5223
[email protected].9:
resolution: {integrity: sha512-OoQpDPV2i3o5Hnn46nz2x6fzdFxFO+JsU4ZES12z65/feMjPHKKHLDVQ2NuEvTaXTRisix/G5+6hyTkwK329kA==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecation notice indicates that Next.js 15.1.9 has a security vulnerability and requires upgrading to a patched version. This is a critical security issue that should be addressed immediately by updating to a newer version of Next.js that doesn't have this vulnerability. Check https://nextjs.org/blog/security-update-2025-12-11 for details on the specific vulnerability and the required patched version.

Suggested change
[email protected]:
resolution: {integrity: sha512-OoQpDPV2i3o5Hnn46nz2x6fzdFxFO+JsU4ZES12z65/feMjPHKKHLDVQ2NuEvTaXTRisix/G5+6hyTkwK329kA==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
deprecated: This version has a security vulnerability. Please upgrade to a patched version. See https://nextjs.org/blog/security-update-2025-12-11 for more details.
[email protected]:
resolution: {integrity: sha512-OoQpDPV2i3o5Hnn46nz2x6fzdFxFO+JsU4ZES12z65/feMjPHKKHLDVQ2NuEvTaXTRisix/G5+6hyTkwK329kA==}
engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}

Copilot uses AI. Check for mistakes.
// Force dynamic rendering to avoid SSR issues with browser APIs
export const dynamic = 'force-dynamic';
// Disable static generation
export const revalidate = 0;
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setting both 'force-dynamic' and revalidate to 0 is redundant. The 'force-dynamic' option already ensures the page is rendered dynamically on every request, making revalidate: 0 unnecessary. Consider removing the revalidate export to simplify the configuration.

Suggested change
export const revalidate = 0;

Copilot uses AI. Check for mistakes.
Comment on lines 6 to 8
experimental: {
// This helps with libraries that use document/window during module initialization
},
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The experimental configuration object is empty and serves no purpose. If the intent was to keep it as a placeholder for future experimental features, it should be removed or have a more explicit comment. Empty configuration objects can be confusing for other developers.

Suggested change
experimental: {
// This helps with libraries that use document/window during module initialization
},

Copilot uses AI. Check for mistakes.
"next": "15.1.1-canary.5",
"react": "19.0.0",
"react-dom": "19.0.0",
"next": "15.1.9",
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The deprecation notice indicates that Next.js 15.1.9 has a security vulnerability and requires upgrading to a patched version. This is a critical security issue that should be addressed immediately by updating to a newer version of Next.js that doesn't have this vulnerability. Check https://nextjs.org/blog/security-update-2025-12-11 for details on the specific vulnerability and the required patched version.

Copilot uses AI. Check for mistakes.
Comment on lines 43 to 47
const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
setIsMounted(true);
}, []);
Copy link
Contributor

@vercel vercel bot Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The portal prop is changed from always being true to isMounted (which starts as false), creating a behavioral mismatch between server rendering and client rendering. This causes the tooltip to render without portal behavior initially, then switch to portal behavior after hydration, potentially causing visual inconsistencies and hydration mismatches.

View Details
📝 Patch Details
diff --git a/packages/namekit-react/src/components/Tooltip.tsx b/packages/namekit-react/src/components/Tooltip.tsx
index a89d5f4..a6c9438 100644
--- a/packages/namekit-react/src/components/Tooltip.tsx
+++ b/packages/namekit-react/src/components/Tooltip.tsx
@@ -1,7 +1,7 @@
 import { Float } from "@headlessui-float/react";
 import { Popover } from "@headlessui/react";
 import cc from "classcat";
-import React, { useState, useEffect } from "react";
+import React, { useState } from "react";
 
 type Props = {
   trigger: React.ReactNode;
@@ -40,11 +40,6 @@ export function Tooltip({
   ...props
 }: Props) {
   const [open, setOpen] = useState(false);
-  const [isMounted, setIsMounted] = useState(false);
-
-  useEffect(() => {
-    setIsMounted(true);
-  }, []);
 
   const handleOpen = () => setOpen(true);
   const handleClose = () => setOpen(false);
@@ -58,7 +53,7 @@ export function Tooltip({
         shift={6}
         flip={10}
         arrow
-        portal={isMounted}
+        portal={typeof window !== "undefined"}
         enter={cc([
           "nk-transition nk-duration-300 nk-ease-out",
           {

Analysis

Portal behavior mismatch in Tooltip component causes SSR hydration issues

What fails: Tooltip component in packages/namekit-react/src/components/Tooltip.tsx uses portal={isMounted} which starts as false and changes to true after mount, causing the tooltip to render in different locations during server-side rendering vs after hydration. This creates DOM structure mismatches and visual inconsistencies.

How to reproduce:

  1. Use the Tooltip component in a Next.js SSR application (like nameguard.io or namegraph.dev)
  2. Render a page with a Tooltip on the server
  3. The tooltip renders inline during server rendering and initial hydration
  4. After hydration completes and the useEffect runs, the tooltip re-renders and moves to a portal at the end of the body
  5. This causes visual flicker and potential hydration warnings

Result:

  • Server render: portal={false} → tooltip renders inline in the normal DOM hierarchy
  • Client hydration (initial): portal={false} → matches server rendering
  • After effect runs: portal={true} → tooltip moves to document body via Portal, causing unnecessary re-render and DOM structure change

Expected behavior: The portal prop should be consistent between server and client rendering to avoid hydration mismatches. Portals render content at the end of <body> which changes the DOM structure, so toggling the portal location after hydration causes the issues documented in React issue #12615.

Root cause: The pattern of using useState(false) with useEffect to set state to true after mount creates a render mismatch when the state value affects DOM structure placement (like with the portal prop).

Reference documentation:

@vercel vercel bot temporarily deployed to Preview – examples.nameguard.io January 19, 2026 16:27 Inactive
@vercel vercel bot temporarily deployed to Preview – storybook.namekit.io January 19, 2026 16:27 Inactive
@vercel vercel bot temporarily deployed to Preview – nameguard.io January 19, 2026 16:27 Inactive
@vercel vercel bot temporarily deployed to Preview – namegraph.dev January 19, 2026 16:27 Inactive
@vercel vercel bot temporarily deployed to Preview – namehashlabs.org January 19, 2026 16:27 Inactive
Copy link
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@djstrong Cool. Looks good to me 👍

@lightwalker-eth lightwalker-eth merged commit a0fdd12 into main Jan 19, 2026
13 checks passed
@lightwalker-eth lightwalker-eth deleted the fix-nextjs-version branch January 19, 2026 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants