DEV Community

Cover image for I Upgraded My 2023 Portfolio to Next.js 16 and Tailwind 4: Everything That Broke
Maria Mendonca
Maria Mendonca

Posted on

I Upgraded My 2023 Portfolio to Next.js 16 and Tailwind 4: Everything That Broke

Back in 2023, when AI was just starting to boom, building a developer portfolio from scratch was the thing to do. I was a junior developer, so I built one: a simple single-page site with my basic details.

Now that I’m job hunting, I wanted to polish it and add a few things. Then npm install greeted me with 27 vulnerabilities, one of them critical. I figured npm audit fix would sort it out, but it only got me partway. The remaining warnings suggested --force, which I did, and my application produced a string of errors that were so complicated I decided to take help from AI, and its suggestions produced so many errors I had to re-clone my website from git and start from scratch.

Starting fresh taught me one thing in particular: npm audit fix --force was never going to fix this. It just pushes breaking changes through blindly. The real fix was a proper upgrade to Next.js 16 and Tailwind CSS 4, done step by step on a branch. Everything that broke along the way, and how I fixed it, is below, so you can skip the Googling.

Before you start

One thing my 5 years of Software Development career has taught me is: always have a backup. Any feature or upgrade to be done should have a copy in your branch. Any upgrade or new feature should happen on its own branch, so the working version stays untouched. Dependency upgrades across a framework and a CSS engine can break both the build and the design, and I’d already learned that the hard way with --force.

So I committed a snapshot first and did the whole upgrade on a separate branch:

git checkout -b upgrade-dep
git add -A && git commit -m "pre-upgrade snapshot"
Enter fullscreen mode Exit fullscreen mode

Step 1: Understand npm audit before touching anything

My first mistake was treating npm audit as a to-do list to clear as fast as possible. The second time around, I read it first.

npm audit fix took me from 27 vulnerabilities down to 13. The rest wanted npm audit fix --force, which is exactly what broke my project the first time. It accepts breaking major-version changes automatically, and for one chain it would even have downgraded eslint-config-next, the wrong direction for a Next.js 16 upgrade.

So this time I asked a different question: does this vulnerability ship to my users? Most of what remained was build-time tooling (Tailwind v3’s dependencies and eslint-config-next), not code that runs in the browser. You can check that for yourself:

npm audit --omit=dev
Enter fullscreen mode Exit fullscreen mode

The upgrades in the next steps cleared most of the list. The last 5 (a braces chain under eslint-config-next) had no patched release, so I left them alone. They only affect lint tooling on my machine, not the deployed site.

Step 2: Upgrade Next.js with the official codemod

I tried npx next upgrade first, but it didn’t work for me. A quick search, confirmed against the Next.js docs, pointed me to the codemod from the canary channel instead:

npx @next/codemod@canary upgrade latest
Enter fullscreen mode Exit fullscreen mode

It bumps next, react, react-dom, and eslint-config-next, then offers optional codemods (middleware-to-proxy, remove-experimental-ppr, and so on). A quick grep told me my project used none of those features, so they were harmless.

Step 3: Upgrade Tailwind, and this is where it got interesting

npx @tailwindcss/upgrade
Enter fullscreen mode Exit fullscreen mode

Problem 1: "yarn: not found"

The upgrader tried yarn add tailwindcss@latest, failed, and then crashed while migrating templates because it couldn't resolve Tailwind. It picks its package manager from lockfiles, and I had a stale yarn.lock in an npm project.

Fix: delete the stale lockfile and install the packages with npm yourself first:

npm i tailwindcss@latest @tailwindcss/postcss@latest postcss@latest
npx @tailwindcss/upgrade
Enter fullscreen mode Exit fullscreen mode

Problem 2: "Cannot apply unknown utility class border-b-slate-700"

The upgrader reads @apply rules in your CSS and aborts if it can't generate one. My earlier half-migrated state caused it. To get past it, I temporarily replaced the @apply with plain CSS. That was a mistake I made worth sharing: I only copied the border color and forgot the width and padding.

Problem 3: postcss.config.js still pointed at the v3 plugin

In v4 the PostCSS plugin moved to its own package, and autoprefixer is built in:

// before
module.exports = {
  plugins: { tailwindcss: {}, autoprefixer: {} },
};

// after
module.exports = {
  plugins: { "@tailwindcss/postcss": {} },
};
Enter fullscreen mode Exit fullscreen mode

And your CSS entry point changes from the three @tailwind directives to:

@import "tailwindcss";
Enter fullscreen mode Exit fullscreen mode

Step 4: The build passed, but the design was broken

The colors and fonts survived. The spacing was gone: no top padding, no section dividers.

The cause was a custom .wrapper class that originally did this:

.wrapper {
  @apply border-b-[1px] border-b-slate-700 py-20;
}
Enter fullscreen mode Exit fullscreen mode

My workaround reduced it to:

.wrapper {
  border-bottom-color: #334155;
}
Enter fullscreen mode Exit fullscreen mode

A color with no border width and no padding draws nothing. Restoring it fixed every section at once:

@layer components {
  .wrapper {
    @apply border-b border-slate-700 py-20;
  }
}
Enter fullscreen mode Exit fullscreen mode

Tailwind 4 gotchas worth checking in your own project

  • Cascade layers: Utilities live in a CSS layer now, so an unlayered reset like * { margin: 0; padding: 0 } beats every py-* and mt-*. Put resets in @layer base.
  • Default border color changed from gray-200 to currentColor.
  • Renamed utilities: shadow-sm becomes shadow-xs, shadow becomes shadow-sm, rounded becomes rounded-sm, outline-none becomes outline-hidden, ring becomes ring-3.
  • Config moved to CSS. Custom colors in tailwind.config.js need @config or a @theme block.
  • Old arbitrary-value syntax: max-w-[theme(screens.xl)] still worked, but max-w-screen-xl is the v4-friendly version.

Step 5: Deploy errors that didn't show up locally

Local npm run build passed. Vercel did not.

EOVERRIDE on npm install

npm error Override for @types/react@^19.3.0 conflicts with direct dependency
Enter fullscreen mode Exit fullscreen mode

An overrides entry in package.json didn't match the version in devDependencies. Locally it worked because node_modules was already resolved, but Vercel installs fresh.
Fix: remove the override, or make it a reference:

"overrides": { "@types/react": "$@types/react" }
Enter fullscreen mode Exit fullscreen mode

Lesson: before pushing a big upgrade, reproduce Vercel's environment:

rm -rf node_modules package-lock.json
npm install
npm run build
Enter fullscreen mode Exit fullscreen mode

target=ES5 has been removed

After updating TypeScript, the build failed on my 2023 tsconfig.json:

error TS5108: Option 'target=ES5' has been removed.
Enter fullscreen mode Exit fullscreen mode

Fix: set "target": "ES2017". Next compiles your code with its own toolchain, so this only affects type-checking.

Async server components flagged by TypeScript

'Articles' cannot be used as a JSX component.
Its return type 'Promise<Element | null>' is not a valid JSX element.
Enter fullscreen mode Exit fullscreen mode

Old @types/react versions don't know about async server components. Updating typescript and @types/react fixed it.

Learn from me and do not do these mistakes

  1. Use a branch and a preview deployment.
  2. Don't run npm audit fix --force blindly. Read what it wants to change.
  3. Install packages yourself if the upgrader picks the wrong package manager.
  4. Check custom CSS classes after the Tailwind upgrade. That's where my spacing went.
  5. Test a fresh install before pushing, because Vercel doesn't have your cached node_modules.

The whole thing took my Sunday morning, and most of the errors were one-line fixes once I understood the cause.

Links: Portfolio | Code

Have you upgraded a Tailwind 3 project yet? What broke for you? Let me know in the comments.

Top comments (0)