惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

量子位
F
Fortinet All Blogs
J
Java Code Geeks
Y
Y Combinator Blog
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
M
MIT News - Artificial intelligence
腾讯CDC
Last Week in AI
Last Week in AI
The Cloudflare Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
Microsoft Security Blog
Microsoft Security Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
P
Proofpoint News Feed
博客园 - 叶小钗
Recent Announcements
Recent Announcements
T
Tailwind CSS Blog
Engineering at Meta
Engineering at Meta
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
人人都是产品经理
人人都是产品经理
L
LangChain Blog
博客园 - 司徒正美
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
5 Performance Mistakes Quietly Slowing Down Your Next.js ...
nxfold · 2026-06-21 · via DEV Community

nxfold

Next.js gives you a fast site almost for free. The framework does a lot of heavy lifting out of the box, which is exactly why most performance problems aren't dramatic. There's no error, nothing crashes. Your Lighthouse score just sits at 72 and you're not sure why.

Below are five mistakes I see constantly in real client codebases, ordered roughly by how often they show up. Each one is small on its own. Stacked together, they're the difference between a site that feels instant and one that feels sluggish. All examples use the App Router.

1. Turning whole pages into Client Components

In the App Router, every component is a Server Component by default. The moment you add "use client" at the top of a file, that component and everything it imports gets bundled and shipped to the browser. The common mistake is slapping "use client" on an entire page just because one small piece needs interactivity.

// ❌ The whole page ships to the client just for one button
"use client";

export default function ProductPage({ product }) {
  return (

      {product.name}
      {product.description}


  );
}

The fix is to push "use client" down to the leaves. Keep the page on the server and isolate only the interactive part.

// ✅ Page stays server-rendered; only the button is a Client Component
export default function ProductPage({ product }) {
  return (

      {product.name}
      {product.description}


  );
}

// AddToCartButton.tsx
"use client";

export function AddToCartButton({ id }: { id: string }) {
  return <button onClick={() => addToCart(id)}>Add to cart;
}

The page's content renders on the server and arrives as HTML. Only the tiny button carries JavaScript. Multiply this across a whole app and your bundle shrinks dramatically.

2. Using <img> instead of next/image

This one is everywhere. A plain <img> tag ships the full-size file, blocks rendering, and does nothing clever about format or screen size. On image-heavy pages it's usually the single biggest drag on load time.

// ❌ Full-size file, no lazy loading, no modern format

next/image handles resizing, lazy loading, and modern formats like WebP automatically. It also reserves space for the image so your layout doesn't jump while it loads, which directly helps your Cumulative Layout Shift score.

// ✅ Resized, lazy-loaded, modern format, no layout shift
import Image from "next/image";

<Image
  src="/hero.jpg"
  alt="Hero"
  width={1200}
  height={600}
  priority // use only for above-the-fold images like a hero
/>

One note: priority tells Next.js to load the image immediately instead of lazily. Use it for the hero or anything visible on first paint, and leave it off everything else.

3. Importing entire libraries for one function

It's easy to write import _ from "lodash" and reach for one helper. The problem is that depending on your setup, you can pull a big chunk of the library into your bundle to use a single function.

// ❌ Risks bundling far more than you use
import _ from "lodash";
_.debounce(fn, 300);

Import the specific function instead. Your bundler only includes what you actually reference.

// ✅ Only the function you need
import debounce from "lodash/debounce";
debounce(fn, 300);

Run @next/bundle-analyzer once and you'll usually find one or two libraries quietly eating most of your bundle. It takes ten minutes to set up and it's the fastest way to find easy wins.

4. Building data-fetching waterfalls

Async/await reads cleanly, which is exactly why this mistake hides so well. Each await pauses until the previous one finishes, so three independent fetches run one after another instead of together.

// ❌ Sequential — each request waits for the one before it
const user = await getUser(id);
const orders = await getOrders(id);
const reviews = await getReviews(id);

If none of these depend on each other, fire them all at once with Promise.all. The total time drops from the sum of all three to roughly the slowest single one.

// ✅ Parallel — all three start immediately
const [user, orders, reviews] = await Promise.all([
  getUser(id),
  getOrders(id),
  getReviews(id),
]);

Only keep things sequential when a later request genuinely needs data from an earlier one. Otherwise, parallelize.

5. Not lazy-loading heavy components

Some components are expensive: charts, rich text editors, maps, video players, big modals. If they sit at the top of your import list, they load with the rest of the page even when the user can't see them yet.

// ❌ Heavy chart loads with the initial page
import HeavyChart from "@/components/HeavyChart";

next/dynamic lets you load a component only when it's actually needed, keeping it off the critical path.

// ✅ Loads on demand, with a fallback while it arrives
import dynamic from "next/dynamic";

const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
  loading: () => Loading chart,
});

This is especially worth it for anything below the fold or behind an interaction, like a modal that only opens on click. There's no reason to pay for that JavaScript on first load.

Putting it together

None of these require a rewrite. Most are a few lines changed in files you already have. A realistic order to tackle them:

  1. Run the bundle analyzer to see what's actually heavy.
  2. Swap every <img> for next/image.
  3. Fix any obvious data-fetching waterfalls with Promise.all.
  4. Pull "use client" down to the smallest components that need it.
  5. Lazy-load the expensive stuff with next/dynamic.

Do those five and re-run Lighthouse. A 70-something score usually jumps well into the 90s, and more importantly the site just feels quicker.


Written by the team at NxFold, a Dubai-based studio building fast, custom websites and web apps on Next.js and React. We're always happy to talk shop — find us at nxfold.com.