Why is Next.js so slow for developers?

“I worked on a project massive reload time, like 45-60 seconds.”

“Nextjs 13 is insanely slow in dev environment”

“Why is Next.js so slow for developers?”

There has been a lot of talk about how Next.js is so slow, and these are just some of the comments I found online while trying to fix our struggle on Atlas when it comes to developer experience. Our development environment was unbearably slow. For every code change, we had to wait a full minute just to see it reflected on screen. Worse yet, Next.js suffered from memory leaks that forced us to restart our dev server every 30 minutes—a process that was itself painfully slow.

In this article, I'll guide you through how I resolved our slow dev server and eliminated this frustrating pain point.

Our setup

Our project is structured as a monorepo. The main application (portal-app) is built with Next.js 13.5 (using the App Router) and TypeScript. It depends on a local frontend package, which serves as our UI library. The frontend package declares react (and other shared libraries) as peerDependencies in its package.json, meaning that portal-app—or any other consumer—must install and provide the actual React version.

├── apps (consumers)
│   ├── backend
│   └── portal-app
├── packages (consumees)
│   ├── common
│   └── frontend

Barrel files and circular dependencies

Barrel files are widely recognized as problematic for development because they create circular dependencies. Our frontend app alone contained approximately 300 circular imports.

The solution to this problem was implementing Subpath exports and restructuring all our exports within the portal-app.

Previously, we were using tsconfig.json paths to import our frontend package, which meant that our bundler had to scan the entire package for changes. By switching to Subpath exports, we could define explicit entry points for our package. This created a much cleaner dependency graph, which in turn made our local development server even faster because the bundler knew exactly which files to check for updates.

Additionally, we needed to identify which third-party libraries were using barrel files and add them to the optimizePackageImports field in our Next.js config. This included libraries like Mantine and BlockNote.

I also used madge to track and locate all of the circular dependencies in our codebase so we could systematically remove them.

Migration to the latest version of Next.js (13.5 ⇒ 15.3)

Next.js version 13.5 suffered from numerous issues, particularly a significant memory leak problem. Turbopack was also far from production-ready in this version, with many bugs making it impractical for development.

Since Next.js 15 uses React 19, I needed to update all dependencies with peer dependencies below React 19 to versions compatible with React 19. I also had to update both our frontend package and portal-app to support the latest features, which required substantial changes.

Migration from webpack to turbopack 🚀

next dev --turbopack

To support Turbopack, I needed to modify my Next.js configuration. We had previously encountered issues with aliases, but I resolved these through the Subpath exports implementation I had already completed for our frontend package.

The main adjustment I had to make was changing how we import SVGs and adding loader support for Turbopack.

Conclusion

With this update we got:

  • Up to 50% faster local server startup
  • Up to 90% faster code updates with Fast Refresh
  • Eliminated memory leaks, removing the need to restart our local development server