← Back to Developer Blog
💻 DeveloperMarch 11, 20268 min read

Next.js 15: The Features That Actually Matter

Next.js 15 changed a lot. Here's what affects your projects, what breaks, and when to upgrade.

By Raspib Technology Team

Next.js 15: The Features That Actually Matter

Next.js 15 is here with React 19 support, async request APIs, and a bunch of changes that will break your build.

Here's what you need to know.

The Breaking Changes (Deal With These First)

1. Async Request APIs

This will break your code:

// Next.js 14 - Works
import { cookies, headers } from 'next/headers';

export default function Page() {
  const cookieStore = cookies();
  const headersList = headers();
  
  return <div>{headersList.get('user-agent')}</div>;
}
// Next.js 15 - Required
import { cookies, headers } from 'next/headers';

export default async function Page() {
  const cookieStore = await cookies();
  const headersList = await headers();
  
  return <div>{headersList.get('user-agent')}</div>;
}

Every component using cookies() or headers() needs to be async now.

Why: Better performance, proper async handling, fewer edge case bugs.

Real Impact: We had to update 23 components across 3 projects. Took about 2 hours per project.

2. Caching Changes

Next.js 14 cached everything by default. Next.js 15? Nothing cached by default.

// Next.js 14: Cached automatically
async function getData() {
  const res = await fetch('https://api.example.com/data');
  return res.json();
}

// Next.js 15: Not cached (fetches every time)
async function getData() {
  const res = await fetch('https://api.example.com/data');
  return res.json();
}

// Want caching? Be explicit:
async function getData() {
  const res = await fetch('https://api.example.com/data', {
    cache: 'force-cache' // or 'no-store'
  });
  return res.json();
}

Why This Matters: Your API calls might suddenly hit the server every time. Could slow down your app or hit rate limits.

Fix: Add explicit caching to fetch calls that should be cached.

3. Route Handlers Changed

// Next.js 14
export async function GET(request: Request) {
  return Response.json({ data: 'hello' });
}

// Next.js 15 - Same, but context changed
export async function GET(
  request: Request,
  context: { params: Promise<{ id: string }> }
) {
  const { id } = await context.params;
  return Response.json({ id });
}

Params are now async. Update your route handlers.

Features You'll Actually Use

1. Turbopack (Finally Stable)

# Development with Turbopack
next dev --turbo

Speed improvements we measured:

  • Cold start: 8s → 2s
  • Hot reload: 1.2s → 0.3s
  • Large project: 15s → 4s

That's 11 seconds saved every time you refresh. Adds up fast.

Catch: Some webpack plugins don't work with Turbopack. Check compatibility first.

2. Partial Prerendering (PPR)

Mix static and dynamic content on same page:

export const experimental_ppr = true;

export default async function ProductPage({ params }) {
  // Static part (prerendered)
  const product = await getProduct(params.id);
  
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      
      {/* Dynamic part (rendered on request) */}
      <Suspense fallback={<Skeleton />}>
        <UserSpecificPrice productId={params.id} />
      </Suspense>
    </div>
  );
}

Page loads instantly with static content. Dynamic parts stream in.

Real Use Case: E-commerce product pages. Product info is static, but price/availability changes per user.

3. Server Actions Improvements

Server Actions are more stable and faster:

'use server';

export async function createOrder(formData: FormData) {
  const product = formData.get('product');
  const quantity = formData.get('quantity');
  
  // Validate
  if (!product || !quantity) {
    return { error: 'Missing fields' };
  }
  
  // Save to database
  const order = await db.orders.create({
    product,
    quantity: parseInt(quantity),
  });
  
  revalidatePath('/orders');
  
  return { success: true, orderId: order.id };
}

No API routes needed. Form submits directly to server function.

We use this for: Contact forms, order forms, booking systems. Cuts code in half.

4. Improved Error Handling

Better error messages in development:

// Before: Cryptic error
// Error: Cannot read property 'name' of undefined

// Now: Helpful error
// Error in /app/products/[id]/page.tsx
// Cannot read property 'name' of undefined
// at line 23: <h1>{product.name}</h1>
// Suggestion: Check if product exists before accessing properties

Saves debugging time. Especially helpful for junior devs.

Performance Improvements

We tested 3 production apps:

App 1 (E-commerce):

  • Build time: 45s → 32s
  • Page load: 1.2s → 0.8s
  • Lighthouse score: 87 → 94

App 2 (Booking Platform):

  • Build time: 38s → 25s
  • Page load: 1.5s → 1.0s
  • Lighthouse score: 82 → 91

App 3 (Admin Dashboard):

  • Build time: 52s → 38s
  • Page load: 1.8s → 1.2s
  • Lighthouse score: 79 → 88

Consistent 20-30% improvement across the board.

Migration Guide

Step 1: Check Dependencies

npm outdated

Update packages that don't support Next.js 15 yet.

Step 2: Update Next.js

npm install next@15 react@19 react-dom@19

Step 3: Update TypeScript (if using)

npm install -D typescript@5.5 @types/react@19 @types/react-dom@19

Step 4: Fix Async APIs

Search for cookies() and headers():

# Find all usages
grep -r "cookies()" src/
grep -r "headers()" src/

Make those components async and await the calls.

Step 5: Fix Caching

Add explicit cache options to fetch calls:

// For data that changes rarely
fetch(url, { cache: 'force-cache' })

// For data that changes often
fetch(url, { cache: 'no-store' })

// For data that should revalidate
fetch(url, { next: { revalidate: 3600 } })

Step 6: Test Everything

npm run build
npm run start

Click through your entire app. Check console for errors.

Common Issues After Upgrade

Issue 1: "cookies is not a function"

// Wrong
const cookieStore = cookies();

// Right
const cookieStore = await cookies();

And make the component async.

Issue 2: Data Not Updating

Probably caching. Add cache: 'no-store' to fetch:

const res = await fetch(url, { cache: 'no-store' });

Issue 3: Build Errors

Check your middleware. Next.js 15 changed how middleware works with headers:

// middleware.ts
export function middleware(request: NextRequest) {
  const response = NextResponse.next();
  
  // Set headers on response, not request
  response.headers.set('x-custom', 'value');
  
  return response;
}

What We're Using

At Raspib, all new Next.js projects use version 15:

Using:

  • Turbopack for dev (so much faster)
  • Server Actions (less API code)
  • Async request APIs (cleaner code)
  • Explicit caching (better control)

Not Using Yet:

  • PPR (still experimental)
  • Some advanced features (waiting for stability)

Should You Learn It?

If you're learning Next.js: Start with 15. No point learning old patterns.

If you know Next.js 14: Upgrade side projects first. Get comfortable with changes. Then upgrade client work.

If you're building for clients: Wait 1-2 months. Let the ecosystem catch up. Then upgrade during maintenance window.

Real Talk

Next.js 15 is better than 14. Faster, cleaner, more explicit.

But the async changes will break your code. Budget time for migration.

Don't upgrade Friday afternoon. Do it Monday morning when you have time to fix issues.


Building with Next.js?

We build Next.js apps for Nigerian businesses. E-commerce, booking platforms, dashboards.

📞 WhatsApp: +234 708 711 0468
📧 info@raspibtech.com
📍 Lagos Island

Related:

Need Help with Your Project?

Let's discuss how Raspib Technology can help transform your business

Related Articles

Next.js 15 Features Guide - What Changed and Why