Skip to content

Upgrade to Next 16, React 19, and AuthKit v4#16

Merged
chaance merged 4 commits into
mainfrom
chance/update-dependencies
Jul 17, 2026
Merged

Upgrade to Next 16, React 19, and AuthKit v4#16
chaance merged 4 commits into
mainfrom
chance/update-dependencies

Conversation

@chaance

@chaance chaance commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR updates all major dependencies to their latest versions and migrates the implementation to the corresponding new APIs.

Dependency updates

  • Next.js 15.5.8^16.2.10 (+ eslint-config-next 16)
  • React / React DOM ^18.3.1^19.2.7 (+ @types/react/@types/react-dom 19)
  • @workos-inc/authkit-nextjs ^0.16.1^4.2.0
  • @workos-inc/node ^7.33.0^10.7.0
  • @workos-inc/widgets 1.0.01.15.0
  • stripe ^16.12.0^22.3.2
  • jose ^5.9.3^6.2.3 (no breaking changes affecting our implementation)
  • eslint ^8^9 (flat config)
  • convex / convex-helpers (regenerated files changed)
  • @types/node ^20^24 (added .nvmrc to run against current LTS)
  • Replaced individual @radix-ui/react-avatar / @radix-ui/react-tooltip
    with the consolidated radix-ui package

Implementation changes

NextJS

  • Added/updated scaffold files generated by fresh npx create next-app
  • Renamed src/middleware.tssrc/proxy.ts

AuthKit v4

  • Renamed authkitMiddleware()authkitProxy()
  • Removed AuthKitProvider from the root layout
  • Replaced inline getSignInUrl/getSignUpUrl calls with dedicated /sign-in and /sign-up route handlers that redirect to the auth URLs (cookies can only be written in server actions)
  • Switched getSession()authkit(request) and hardened the session user guard

WorkOS Node SDK v10

  • workos.portal.generateLinkworkos.adminPortal.generateLink
  • GeneratePortalLinkIntentGenerateLinkIntent
  • workos.widgets.getToken(...)workos.widgets.createToken(...) (now returns { token })

React 19 / theming

  • Vendored next-themes into src/lib/next-themes.tsx. This was adapted for React 19 to avoid dev errors on startup (server-rendered inline scripts no longer execute, so the theme script no-ops after hydration).

Tooling / config

  • Flat eslint.config.mjs for ESLint 9; next linteslint
  • Added next.config.ts, .nvmrc (Node 24), pnpm-workspace.yaml, .prettierignore
  • tsconfig.json: target es5 → ES2017, jsx preserve → react-jsx, include .next/dev/types and **/*.mts
  • .gitignore now ignores all .env* files except .env.example
  • scripts/setup.ts:
    • masks secret prompts (LLMs will read console output)
    • merges into an existing .env.local instead of overwriting, improves error output

Test plan

  • pnpm install succeeds
  • pnpm build passes
  • pnpm lint passes
  • Sign in / sign up flows work end to end
  • Dashboard, users (widgets), and settings/audit-logs admin portal links load
  • Theme toggle works with no hydration warnings/flash
  • pnpm setup writes/updates .env.local correctly

@chaance chaance changed the title Update all dependencies Upgrade to Next 16, React 19, and AuthKit v4 Jul 16, 2026
@greptile-apps

greptile-apps Bot commented Jul 16, 2026

Copy link
Copy Markdown

Greptile Summary

This PR upgrades the starter kit to Next.js 16, React 19, AuthKit v4, WorkOS Node SDK v10, and ESLint 9, migrating all implementation to the corresponding new APIs. The changes are thorough and well-executed across all affected surfaces.

  • AuthKit v4 migration: authkitMiddleware()authkitProxy() (now in src/proxy.ts); getSession()authkit(request); getSignInUrl()/getSignUpUrl() moved from Server Component render into dedicated Route Handlers (/sign-in, /sign-up) so PKCE cookies can be written correctly; AuthKitProvider updated to import from the /components subpath.
  • WorkOS Node SDK v10: workos.portalworkos.adminPortal, GeneratePortalLinkIntentGenerateLinkIntent, widgets.getToken()widgets.createToken() (returns { token }), all applied consistently across settings, audit logs, and the users widget.
  • React 19 / Next 16 compatibility: next-themes vendored as a local fork with a useIsHydrated() guard so the inline theme-detection <script> no longer causes dev warnings under the new server-script execution model.

Confidence Score: 5/5

Safe to merge — all AuthKit v4, Node SDK v10, and Next.js 16 API surfaces are correctly updated with no regressions visible in the changed paths.

Every major breaking-change call site was updated consistently: the proxy file, session retrieval, sign-in/sign-up URL generation (correctly moved to Route Handlers), Admin Portal API renames, and the widgets token shape change. The vendored next-themes fork addresses the React 19 inline-script behaviour change with a well-understood useSyncExternalStore pattern. The setup script now uses a public API for secret masking. No hardcoded secrets, no TLS bypasses, and no sensitive field logging were introduced.

No files require special attention.

Important Files Changed

Filename Overview
src/proxy.ts Renamed from middleware.ts; correctly adopts authkitProxy() for Next.js 16+ and updates matcher patterns
src/app/layout.tsx AuthKitProvider now imported from the /components subpath (v4 requirement) and correctly wraps children; ThemeProvider switched to the local fork
src/app/sign-in/route.ts New Route Handler for sign-in; correctly places getSignInUrl() in a Route Handler so PKCE cookies can be written
src/app/sign-up/route.ts New Route Handler for sign-up; mirrors sign-in route and correctly defers getSignUpUrl() to a Route Handler context
src/app/router/route.ts Switches from getSession() to authkit(request); hardens session guard by checking both session and session.user before proceeding
src/app/dashboard/users/page.tsx Updated widgets API call from getToken() to createToken(), correctly destructures { token } from the new return shape
src/app/dashboard/settings/page.tsx Updated from portal.generateLink/GeneratePortalLinkIntent to adminPortal.generateLink/GenerateLinkIntent per Node SDK v10
src/actions/getAuditLogPortalLink.ts Mirrors settings page: adminPortal.generateLink + GenerateLinkIntent.AuditLogs, consistent with Node SDK v10 rename
src/lib/next-themes/components.tsx Vendored fork of next-themes adapted for React 19; ThemeScript uses useIsHydrated() to suppress the inline script after hydration, sidestepping the React 19 server-script execution change
src/lib/use-is-hydrated.ts Clean useSyncExternalStore-based hydration flag; server snapshot returns false, client snapshot returns true
scripts/setup.ts Replaced private readline._writeToOutput masking with @inquirer/prompts; env merging logic preserves existing .env.local structure instead of overwriting
src/app/components/sign-in-button.tsx Replaced direct getSignInUrl()/getSignUpUrl() calls (invalid in Server Components under Next 15+/16) with static /sign-in and /sign-up links that delegate to the new Route Handlers

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Browser
    participant NextProxy as src/proxy.ts (authkitProxy)
    participant SignInRoute as /sign-in route handler
    participant RouterRoute as /router route handler
    participant WorkOS as WorkOS AuthKit

    Browser->>NextProxy: GET /dashboard
    NextProxy->>NextProxy: authkit(request) — check session
    NextProxy-->>Browser: 307 → /sign-in (no session)

    Browser->>SignInRoute: GET /sign-in
    SignInRoute->>WorkOS: getSignInUrl() — sets PKCE cookie
    WorkOS-->>SignInRoute: signInUrl string
    SignInRoute-->>Browser: 307 → WorkOS hosted login

    Browser->>WorkOS: Sign in
    WorkOS-->>Browser: "302 → /auth/callback?code=..."
    Browser->>NextProxy: GET /auth/callback (handleAuth)
    NextProxy->>WorkOS: Exchange code for session
    WorkOS-->>NextProxy: session + tokens
    NextProxy-->>Browser: Set session cookie, 302 → /router

    Browser->>RouterRoute: GET /router
    RouterRoute->>RouterRoute: "authkit(request) → { session }"
    RouterRoute->>RouterRoute: "guard session & session.user"
    RouterRoute-->>Browser: 302 → /dashboard (with role/org resolved)
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Browser
    participant NextProxy as src/proxy.ts (authkitProxy)
    participant SignInRoute as /sign-in route handler
    participant RouterRoute as /router route handler
    participant WorkOS as WorkOS AuthKit

    Browser->>NextProxy: GET /dashboard
    NextProxy->>NextProxy: authkit(request) — check session
    NextProxy-->>Browser: 307 → /sign-in (no session)

    Browser->>SignInRoute: GET /sign-in
    SignInRoute->>WorkOS: getSignInUrl() — sets PKCE cookie
    WorkOS-->>SignInRoute: signInUrl string
    SignInRoute-->>Browser: 307 → WorkOS hosted login

    Browser->>WorkOS: Sign in
    WorkOS-->>Browser: "302 → /auth/callback?code=..."
    Browser->>NextProxy: GET /auth/callback (handleAuth)
    NextProxy->>WorkOS: Exchange code for session
    WorkOS-->>NextProxy: session + tokens
    NextProxy-->>Browser: Set session cookie, 302 → /router

    Browser->>RouterRoute: GET /router
    RouterRoute->>RouterRoute: "authkit(request) → { session }"
    RouterRoute->>RouterRoute: "guard session & session.user"
    RouterRoute-->>Browser: 302 → /dashboard (with role/org resolved)
Loading

Reviews (3): Last reviewed commit: "fix setup script" | Re-trigger Greptile

Comment thread src/app/layout.tsx
Comment thread src/lib/next-themes/components.tsx
Comment thread src/lib/next-themes/components.tsx
Comment thread scripts/setup.ts Outdated

export async function SignInButton({ large, signUp }: { large?: boolean; signUp?: boolean }) {
const { user } = await withAuth();
const authorizationUrl = signUp ? await getSignUpUrl() : await getSignInUrl();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These functions can write to cookies, which is forbidden in Next 16 server components. This has to be done in server actions.

@nicknisi
nicknisi self-requested a review July 17, 2026 16:50
return (
<Button asChild size={large ? '3' : '2'}>
<Link href={authorizationUrl}>{signUp ? 'Sign Up' : 'Sign In'}</Link>
<Link href={signUp ? '/sign-up' : '/sign-in'}>{signUp ? 'Sign Up' : 'Sign In'}</Link>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

question: any reason this isn't using authorizationUrl anymore?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

const authorizationUrl = signUp ? await getSignUpUrl() : await getSignInUrl();

It's because getSignUpUrl and getSignInUrl both set cookies. Next 16 only allows setting cookies via server actions or route handlers. As originally written, this resulted in a runtime error in development (probably a good idea to update the SDK docs here).

@chaance
chaance merged commit 58a4a2d into main Jul 17, 2026
3 checks passed
@chaance
chaance deleted the chance/update-dependencies branch July 17, 2026 18:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants