Skip to content

Commit 4833145

Browse files
authored
chore(deps): upgrade next.js to 16.2.4 (#4460)
* chore(deps): upgrade next.js to 16.2.4 - Bump next and @next/env to 16.2.4 across root, apps/sim, apps/docs - Replace next-runtime-env's env() helper (calls unstable_noStore(), rejected by Next 16.2 outside request scope) with a direct window.__ENV / process.env getter - Add export const dynamic = 'force-dynamic' on landing /privacy and /terms pages so NEXT_PUBLIC_* runtime env reads aren't baked at build * fix(whitelabel): force dynamic rendering for manifest.ts Without this, NEXT_PUBLIC_BRAND_* values are baked into the manifest at build time. Pairs with the next-runtime-env removal in the prior commit, restoring Docker runtime injection for whitelabel deployments. * fix(oauth): wrap consent page useSearchParams in Suspense Next 16.2's stricter prerender check fails the build when useSearchParams() is used without a Suspense boundary. Splits the client component into an outer wrapper and inner body. * fix(whitelabel): force dynamic rendering for landing segment Client components in (landing) (e.g. Navbar) read NEXT_PUBLIC_BRAND_* via getEnv. Without this, SSR prerender would bake the build-time process.env values into HTML, mismatching window.__ENV after hydration in Docker runtime-env deployments. Cascades to all landing routes via the layout. * revert(whitelabel): drop force-dynamic from landing layout Cascading force-dynamic neutered dynamicParams = false + generateStaticParams on /blog/[slug], /integrations/[slug], /models/[provider], /models/[provider]/[model] — killing static prerender for SEO-critical pages. The hydration concern only materializes for whitelabel Docker deployments where build-time and runtime NEXT_PUBLIC_BRAND_* differ; those deployments can set the vars at build instead. Keeping force-dynamic on /privacy, /terms, and /manifest where it actually matters. * fix(prerender): wrap useSearchParams callsites for Next 16.2 Next 16.2 fails the build when a client component using useSearchParams() is statically prerendered without a Suspense boundary. - Wrap landing Navbar in Suspense (imported by /oauth/consent and other pages) - Add force-dynamic to reset-password, invite/[id], and unsubscribe pages whose client bodies call useSearchParams * fix(navbar): preserve SSR HTML, drop Suspense bailout Reading useSearchParams() forced a Suspense fallback that emitted no navbar HTML during SSR — leaving crawlers and no-JS users without nav. The 'home' query param only affects client-side link targets, so read it from window.location in an effect after hydration. Restores full SSR navbar markup. * chore: trim verbose comments in next.js upgrade The force-dynamic export name is self-documenting; the remaining env.ts comment is tightened to the essential WHY (why we don't use next-runtime-env's helper).
1 parent ad88859 commit 4833145

14 files changed

Lines changed: 58 additions & 35 deletions

File tree

apps/docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"fumadocs-openapi": "10.8.1",
2727
"fumadocs-ui": "16.8.5",
2828
"lucide-react": "^0.511.0",
29-
"next": "16.1.6",
29+
"next": "16.2.4",
3030
"next-themes": "^0.4.6",
3131
"postgres": "^3.4.5",
3232
"react": "19.2.4",

apps/sim/app/(auth)/oauth/consent/page.tsx

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useCallback, useEffect, useState } from 'react'
3+
import { Suspense, useCallback, useEffect, useState } from 'react'
44
import { ArrowLeftRight } from 'lucide-react'
55
import Image from 'next/image'
66
import { useRouter, useSearchParams } from 'next/navigation'
@@ -25,6 +25,14 @@ interface ClientInfo {
2525
}
2626

2727
export default function OAuthConsentPage() {
28+
return (
29+
<Suspense fallback={null}>
30+
<OAuthConsentInner />
31+
</Suspense>
32+
)
33+
}
34+
35+
function OAuthConsentInner() {
2836
const router = useRouter()
2937
const searchParams = useSearchParams()
3038
const { data: session } = useSession()

apps/sim/app/(auth)/reset-password/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ export const metadata: Metadata = {
55
title: 'Reset Password',
66
}
77

8+
export const dynamic = 'force-dynamic'
9+
810
export default ResetPasswordPage

apps/sim/app/(landing)/components/navbar/navbar.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { useCallback, useContext, useEffect, useRef, useState, useSyncExternalSt
44
import dynamic from 'next/dynamic'
55
import Image from 'next/image'
66
import Link from 'next/link'
7-
import { useSearchParams } from 'next/navigation'
87
import { GithubOutlineIcon } from '@/components/icons'
98
import { cn } from '@/lib/core/utils/cn'
109
import { SessionContext } from '@/app/_shell/providers/session-provider'
@@ -52,12 +51,14 @@ interface NavbarProps {
5251

5352
export default function Navbar({ logoOnly = false, blogPosts = [] }: NavbarProps) {
5453
const brand = getBrandConfig()
55-
const searchParams = useSearchParams()
5654
const sessionCtx = useContext(SessionContext)
5755
const session = sessionCtx?.data ?? null
5856
const isSessionPending = sessionCtx?.isPending ?? true
5957
const isAuthenticated = Boolean(session?.user?.id)
60-
const isBrowsingHome = searchParams.has('home')
58+
const [isBrowsingHome, setIsBrowsingHome] = useState(false)
59+
useEffect(() => {
60+
setIsBrowsingHome(new URLSearchParams(window.location.search).has('home'))
61+
}, [])
6162
const useHomeLinks = isAuthenticated || isBrowsingHome
6263
const logoHref = useHomeLinks ? '/?home' : '/'
6364
const mounted = useSyncExternalStore(

apps/sim/app/(landing)/privacy/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import Link from 'next/link'
33
import { getEnv } from '@/lib/core/config/env'
44
import { ExternalRedirect, LegalLayout } from '@/app/(landing)/components'
55

6+
export const dynamic = 'force-dynamic'
7+
68
export const metadata: Metadata = {
79
title: 'Privacy Policy',
810
description:

apps/sim/app/(landing)/terms/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import Link from 'next/link'
33
import { getEnv } from '@/lib/core/config/env'
44
import { ExternalRedirect, LegalLayout } from '@/app/(landing)/components'
55

6+
export const dynamic = 'force-dynamic'
7+
68
export const metadata: Metadata = {
79
title: 'Terms of Service',
810
description:

apps/sim/app/invite/[id]/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ export const metadata: Metadata = {
66
robots: { index: false },
77
}
88

9+
export const dynamic = 'force-dynamic'
10+
911
export default Invite

apps/sim/app/manifest.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import type { MetadataRoute } from 'next'
22
import { getBrandConfig } from '@/ee/whitelabeling'
33

4+
export const dynamic = 'force-dynamic'
5+
46
export default function manifest(): MetadataRoute.Manifest {
57
const brand = getBrandConfig()
68

apps/sim/app/unsubscribe/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,6 @@ export const metadata: Metadata = {
66
robots: { index: false },
77
}
88

9+
export const dynamic = 'force-dynamic'
10+
911
export default Unsubscribe

apps/sim/lib/core/config/env.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
import { createEnv } from '@t3-oss/env-nextjs'
2-
import { env as runtimeEnv } from 'next-runtime-env'
32
import { z } from 'zod'
43

54
/**
6-
* Universal environment variable getter that works in both client and server contexts.
7-
* - Client-side: Uses next-runtime-env for runtime injection (supports Docker runtime vars)
8-
* - Server-side: Falls back to process.env when runtimeEnv returns undefined
9-
* - Provides seamless Docker runtime variable support for NEXT_PUBLIC_ vars
5+
* Reads NEXT_PUBLIC_* env vars in both client and server contexts.
6+
* Client reads `window.__ENV` (populated by `<PublicEnvScript>`); server reads `process.env`.
7+
* We do not use next-runtime-env's `env()` helper because it calls `unstable_noStore()`,
8+
* which Next 16.2+ rejects outside a request scope.
109
*/
11-
const getEnv = (variable: string) => runtimeEnv(variable) ?? process.env[variable]
10+
const getEnv = (variable: string): string | undefined => {
11+
if (typeof window === 'undefined') return process.env[variable]
12+
return window.__ENV?.[variable] ?? process.env[variable]
13+
}
1214

1315
// biome-ignore format: keep alignment for readability
1416
export const env = createEnv({

0 commit comments

Comments
 (0)