Upgrade to Next 16 and clear every dependency advisory - #74
Merged
Conversation
@emotion/*, @react-three/*, three and @types/three had no imports anywhere in the repo. eslint and eslint-config-next sat in dependencies rather than devDependencies and were never invoked — linting runs through Biome via lint-staged, and `next lint` is gone in Next 16 — so they only served to pull in the minimatch/js-yaml/flatted/ajv advisory chain. Also bumps axios and unpins postcss to clear their high-severity advisories, and drops the `export` script (`next export` was removed in Next 14). Removes 328 packages. pnpm audit goes from 104 advisories to 47; build output is byte-identical at 105 kB First Load JS.
Next 15.4.10 was on an unmaintained patch line carrying 25 open advisories, the earliest of which was fixed in 15.4.11. Moving to 16.3.1 clears all of them rather than chasing the 15.5 backport, which would still have required the next-intl 4 migration to get here later. Migration notes: - middleware.ts becomes proxy.ts with a named `proxy` export, per the Next 16 rename. next-intl still exports createMiddleware, so the handler itself is unchanged. - next-intl 4 infers messages from i18n/request.ts, so the explicit `messages` prop and the getMessages() call are gone. The type augmentation moves from the global IntlMessages interface to next-intl's AppConfig. - globals.css sets scroll-behavior: smooth on html, and Next 16 no longer overrides that during route transitions, so html opts back in with data-scroll-behavior="smooth". - next-themes 0.4 writes color-scheme onto html before hydration, which React reports as a mismatch, so html gets suppressHydrationWarning. - favicon.ico moves to public/. As an app-root metadata route it had no root layout to attach to and Next 16 failed to resolve it; the layout already declared the icon explicitly. - tsconfig gains the mandatory jsx: react-jsx and the .next/dev types path, and the stale next.config.mjs include is corrected to .ts. Turbopack is now the default bundler: the production build drops from 36.7s to 11.7s, compile from 12.0s to 4.1s. pnpm audit goes from 47 advisories to 13, none of which are in next, next-intl or axios. Verified: build green, all five locale routes resolve through proxy.ts with the custom /kr prefix intact, Korean and Chinese messages load, live IQ token data still fetches, client-side navigation works and the browser console is clean.
Bumps the remaining direct dependencies to current majors/patches and drops gsap, @gsap/react and @radix-ui/react-menubar, none of which are imported anywhere in the tree. The pnpm.overrides block clears the last transitive advisories, all of them reached only through tailwindcss 3's build-time deps. `pnpm audit` now reports no known vulnerabilities, down from 104 at the start of the upgrade.
posthog-js 1.418 introduced strict_script_versioning, defaulting to "fallback": it requests /static/<version>/<extension>.js first and only retries the unversioned URL after that fails. The assets CDN does not serve the versioned path, so every page load spent two 404s before loading surveys and dead-click autocapture.
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The /ingest paths were `redirects` with permanent: true, so the browser was handed the posthog.com URL directly — the exact thing `api_host: "/ingest"` exists to avoid. Rewrites proxy them server-side instead. The layout also carried `<link rel="canonical" />` with no href, which did nothing. Canonicals can't live in the layout because it has no pathname and would point every route at /, so both pages now build their own from next-intl's getPathname, along with a reciprocal hreflang set.
framer-motion 13, lucide-react 1, react-icons 5 and a posthog-js patch. All four have live consumers; icons and the mobile menu verified rendering. prettier is removed rather than bumped. Biome already formats via lint-staged, and the `format` script passed `--loglevel`, a flag prettier 3 renamed, so the script was on track to break on the next major anyway. It now runs biome. Holding the remaining majors for their own changes: tailwindcss 4 is a config migration that also retires the pnpm.overrides block, typescript 7 is the native rewrite, biome 2 needs `biome migrate` plus repo-wide format churn, and @types/node should track the Node 24 runtime rather than jump to 26. react-intersection-observer is left alone deliberately: its only consumer, InViewAnimateBottom, has no importers, and it destructures useInView() without attaching the ref, so inView was permanently false and the useAnimation controls were never bound to a component. Bumping dead code that never ran buys nothing.
This was referenced Aug 20, 2026
Brings in the HiIQ page rewrite from #73. The only conflict was the import block in app/[locale]/hiiq/page.tsx: the page now composes from components/layouts/hiiq/* instead of section-kit, so the old section-kit import goes and `alternatesFor` stays. Git had already merged the body, keeping generateMetadata's canonical alongside the new section list.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes https://github.com/IQIndustries/issues/issues/5216 & https://github.com/IQIndustries/issues/issues/5217 when this PR merged
Pins the app to Next 16.3.1 and takes
pnpm auditfrom 104 advisories to 0.15.4.x is an unmaintained patch line — the maintained lines are 16.x and the 15.5.23 backport. 25 of the original advisories were against
nextitself, the earliest fixed in 15.4.11. Going to 16 rather than the backport avoids sitting on an aging line and pulls the next-intl v4 migration forward instead of deferring it.What changed
cf3516f— drop unused deps and the stale eslint setupRemoves
@emotion/react,@emotion/styled,@react-three/drei,@react-three/fiber,threeand@types/three, none of which were imported.eslintandeslint-config-nextwere independenciesand entirely unused — linting runs through Biome, andnext lintis removed in 16 — so.eslintrc.jsongoes with them. Also drops the deadnext exportscript and bumpsaxios(30+ advisories) and the pinnedpostcss(high-severity path traversal). 328 packages removed.6d02cb5— Next 16, React 19 and next-intl 4src/middleware.ts→src/proxy.tswith a namedproxyexport, per the Next 16 rename.global.d.tsmoves to theAppConfigaugmentation, andNextIntlClientProviderno longer takesmessages— it inherits them fromi18n/request.ts.@types/*. Optional for Next 16, but the repo was already carrying@types/react19 against React 18.data-scroll-behavior="smooth"on<html>, sinceglobals.csssetsscroll-behavior: smoothand Next 16 no longer overrides it during route transitions.suppressHydrationWarningon<html>— next-themes 0.4 writesclassandcolor-schemebefore hydration.src/app/favicon.ico→public/favicon.ico. As an app-root metadata route it failed the build outright (Cannot find module for page: /favicon.ico) because there is no rootlayout.tsx, only[locale]/layout.tsx. The layout already declares the icon explicitly.cdf2af9— leaf bumps, dead deps, pinned build transitivesGSAP turned out to be unused — no imports anywhere — so
gsap,@gsap/reactand@radix-ui/react-menubarare gone. Thepnpm.overridesblock clears the last transitive advisories, all reachable only through tailwindcss 3's build-time deps (sucrase, chokidar, postcss-load-config); it carries a note to delete it with the Tailwind 4 upgrade.de858e3— stop PostHog probing versioned asset pathsposthog-js 1.418 added
strict_script_versioning, defaulting to"fallback": it requests/static/<version>/<extension>.jsfirst and only retries the unversioned URL after that fails. The assets CDN does not serve the versioned path, so every page load spent two 404s before loading surveys and dead-click autocapture.68398e0— proxy PostHog through/ingest, emit real canonicalsTwo pre-existing defects rather than upgrades, both in the same area as the commit above.
/ingest/*wasredirectswithpermanent: true. posthog-js is initialised withapi_host: "/ingest", so those paths have to be proxied server-side; a redirect handed the browserus.i.posthog.comand defeated the proxy outright. Nowrewrites.permanent: truealso meant browsers cached the redirect indefinitely, so returning visitors would have kept bypassing the proxy the longer it sat.The layout separately carried
<link rel="canonical" />with nohref, which did nothing. It can't be fixed in place — a layout has no access to the pathname, so any canonical it emits would point/hiiqat/. Both pages now derive their own from next-intl'sgetPathname, plus a reciprocal hreflang set.getPathnamewas already available fromcreateNavigationand simply wasn't destructured, so the locale prefix rules stay ini18n/routing.tsinstead of being duplicated. The hiddenkolocale is skipped — it aliaseskron the same URL, so listing both would emit two hreflang tags for one page.One judgement call: hreflang codes come from the existing
localizationvalues inmessages/_schema.ts, so English isen-USrather than plainen. Region-specific is narrower than needed for a site with one English variant, but reusing that field avoids standing up a second source of truth for locale metadata. Easy to revisit.37e7e86— bump live-code majors, replace prettier with biomeframer-motion 12 → 13, lucide-react 0.525 → 1.33, react-icons 4 → 5, plus a posthog-js patch. All four have live consumers, so all four were checked in the browser rather than just compiled.
prettier is removed rather than bumped. Biome already formats through lint-staged, and the
formatscript passed--loglevel, a flag prettier 3 renamed — so it was going to break on that upgrade regardless. The script now runsbiome format.react-intersection-observeris deliberately left on 9.x. Its only consumer isInViewAnimateBottom, which has no importers, and inside ituseInView()is destructured without ever attaching theref— soinViewwas permanentlyfalseand theuseAnimationcontrols were never bound to a component. That code path has never run. Bumping it buys nothing; see the dead-code note below.Results
pnpm auditpnpm buildwallTurbopack is the default bundler for both
devandbuildin 16. There is no custom webpack config in this repo, so that came for free.Not every original advisory was live against this app — the Server Actions ones need
"use server"(none here), the rewrites SSRF needs dynamic destinations (ours are two static entries), and the self-hosted image-optimizer and custom-server advisories are moot on Vercel. The ones that did apply were RSC cache poisoning and cache confusion, which matter most for a CDN-cached public site, plus Server Components DoS and connection exhaustion.Verification
pnpm buildgreen;pnpm auditreports no known vulnerabilities.lang:/→en,/kr→kr,/zh→zh,/hiiq→en,/kr/hiiq→kr.Linkworks.display: noneat desktop width), and the mobile menu opens at 375px with its react-icons close icon drawn./ingestproxies same-origin, confirmed two ways. In the browser,performance.getEntriesByType('resource')shows three/ingest/*requests (config, surveys, dead-clicks) and zero toposthog.com. By request:fetch('/ingest/static/array.js', {redirect: 'error'})returns 200 fromlocalhost:3000withcontent-type: application/javascript.redirect: 'error'throws on any 3xx, so this cannot pass unless the request is genuinely proxied./braindao.org/krbraindao.org/kr/zhbraindao.org/zh/hiiqbraindao.org/hiiq/hiiq,/kr/hiiq,/zh/hiiq/kr/hiiqbraindao.org/kr/hiiqEvery page self-canonicalises and the hreflang sets are reciprocal, which is what Google requires to honour them.
Deliberately left out
Kept as a version bump plus the two adjacent bug fixes. Follow-ups worth their own PRs:
experimental.cacheComponents, andunstable_cache→use cache.pnpm.overridesblock.src/components/ui/popover.tsx,card.tsxandcontainer-scroll-animation.tsxhave no importers. Popover is the notable one:@radix-ui/react-popoveris a direct dependency reachable only through that dead file, so the dep cannot go without deleting the component.openGraph.urlis still hardcoded to the English URL on both pages, so every locale advertises the same og:url. Same family as the canonical fix, but it is a separate call about og metadata.pnpm.overrides), typescript 5.9 → 7 (the native rewrite), biome 1 → 2 (biome migrateplus repo-wide format churn), and @types/node, which should track the Node 24 runtime rather than jump to 26.src/components/transitions/InViewAnimateBottom.tsxis dead too — no importers — and removing it is what unblocks droppingreact-intersection-observer. Same call as the unusedui/components above, so grouped with them rather than done piecemeal here.