Next.js 15 brings several advanced features and improvements that significantly enhance both performance and developer experience. Here’s a detailed breakdown of the key updates with examples:
1. Partial Pre-Rendering (PPR) with Detailed Use Case
Concept: PPR allows combining static (SSG) and dynamic (SSR) rendering in a single page. This is particularly useful in scenarios where you want to optimize performance for SEO while fetching dynamic content after the page loads.
Example: E-Commerce Product Page
Consider a product page that statically renders the basic product details like title and description, but fetches up-to-date prices dynamically from an API.
import { Suspense } from 'react';
import StaticProductDetails from '@/components/StaticProductDetails';
import DynamicPrice from '@/components/DynamicPrice';
export const experimental_ppr = true; // Enabling Partial Pre-rendering
export default function ProductPage({ productId }) {
return (
<>
<StaticProductDetails productId={productId} /> {/* Static */}
<Suspense fallback={<div>Loading price...</div>}>
<DynamicPrice productId={productId} /> {/* Dynamic */}
</Suspense>
</>
);
}
Here, the StaticProductDetails component is fetched at build time, so it loads instantly, whereas the DynamicPrice is fetched on the client side for the freshest data.
To enable PPR, the next.config.js should look like this:
const nextConfig = {
experimental: {
ppr: 'incremental', // Enable partial pre-rendering incrementally
},
};
module.exports = nextConfig;
2. Enhanced Caching Mechanisms
Concept: In Next.js 15, fetch() no longer caches data by default, offering more fine-grained control over data freshness.
Example: API Fetch with Explicit Caching Strategy
Consider a news website where certain articles are frequently updated (breaking news), while others remain relatively static (editorials). You can explicitly define how each API fetch behaves using the new caching options.
// Fetch breaking news with no cache
const breakingNews = await fetch('https://...', {
cache: 'no-store', // No caching, always fetch the latest
});
// Fetch editorials with caching for better performance
const editorials = await fetch('https://...', {
cache: 'force-cache', // Cache for quick loading
});
In this example, breaking news will always be up-to-date, while editorials are cached for faster loading times.
3. after() API for Post-Response Operations
Concept: The after() API allows executing tasks (like logging or analytics) after the client has received the response, optimizing perceived performance by deferring non-critical work.
Example: Logging User Actions After Response
Imagine you're running a blog where user visits are logged to track popular posts, but you don’t want this logging to delay the user’s page load.
import { unstable_after as after } from 'next/server';
import { logPageView } from '@/utils/logger';
export default function BlogPage({ content }) {
after(() => {
logPageView(); // Log page view after the response has been sent
});
return <div>{content}</div>;
}
This example ensures that the user gets the page as quickly as possible, while the logging happens in the background after the page is loaded.
4. Turbopack for Fast Builds
Concept: Turbopack, the new build system for Next.js, written in Rust, significantly speeds up local development, reducing build times and improving developer productivity.
Example: Using Turbopack in Development
To enable Turbopack when starting a new project, simply run:
npx create-next-app --turbo
To enable it for an existing project, add the following to your package.json:
{
"scripts": {
"dev": "next dev --turbo"
}
}
Turbopack greatly improves build times, especially for large-scale projects with many dependencies, making it a great tool for enterprise-level applications.
5. Improved Hydration Error Debugging
Concept: Hydration errors occur when the server-rendered HTML doesn’t match the React-generated HTML on the client. Next.js 15 makes it easier to debug by showing clearer error messages with exact discrepancies.
Example: Hydration Mismatch with State
Let's say your component incorrectly initializes state on the client:
function Counter() {
const [count, setCount] = useState(0); // State differs on the server
useEffect(() => setCount(5), []); // State set to 5 after component mounts
return <p>{count}</p>; // Mismatch during hydration
}
In Next.js 15, the hydration error will clearly show the mismatch between the initial render (0) and the updated state (5), making it easier to trace and fix issues.
6. Advanced Integration with React 19
Concept: Next.js 15 requires React 19 and fully supports the new React Compiler, which automates component optimization, eliminating the need for hooks like useMemo or useCallback for performance.
Example: React 19’s useActionState
With React 19, you can leverage the new hooks like useActionState for managing actions within your components efficiently.
import { useActionState } from 'react';
function ActionComponent() {
const [actionState, startAction] = useActionState(async () => {
// some async operation
});
return (
<div>
<button onClick={startAction}>Start Action</button>
{actionState.status === 'loading' && <p>Loading...</p>}
</div>
);
}
This hook simplifies managing action states (e.g., loading, success, error) without needing to manually track these states with useState.
7. Improved Developer Experience with create-next-app
Concept: Next.js 15 revamps the create-next-app tool, offering an option to start with minimal boilerplate or use Turbopack for improved build times.
Example: Minimal Setup with --empty
You can now start with a clean slate, reducing setup time by creating an empty Next.js project:
npx create-next-app --empty
This generates a basic "Hello World" project without unnecessary files, ideal for developers who want to start from scratch.
8. Step-by-Step Guide to Creating an Advanced Next.js 15 Project
Here’s a detailed guide on setting up and creating a Next.js 15 project with all the advanced features discussed earlier. We’ll cover the project creation and configuration.
1. Create a New Next.js 15 Project
First, let’s start by creating the project using create-next-app, which is the official starter kit for Next.js.
To install Next.js 15, run the following command in your terminal:
npx create-next-app@rc my-nextjs-project --turbo
This command does the following:
--turbo enables Turbopack, the new Rust-based bundler, which speeds up builds and hot-reloading during local development.
Alternatively, if you want a minimal project setup (without boilerplate), use the --empty flag:
npx create-next-app@rc my-nextjs-project --empty
This creates a clean Next.js app with just a "Hello World" page.
2. Set Up Your next.config.js with Advanced Features
After the project is set up, you can enable the experimental features discussed earlier by configuring your next.config.js file.
Here’s a full example of a next.config.js file with several advanced features enabled:
/** @type {import('next').NextConfig} */
const nextConfig = {
experimental: {
ppr: 'incremental', // Enable Partial Pre-rendering
after: true, // Enable after() API for post-response tasks
reactCompiler: true, // Enable React 19 Compiler optimizations
},
images: {
formats: ['image/avif', 'image/webp'], // Optimize images with Next.js
},
future: {
strictPostcssConfiguration: true, // Ensure strict CSS settings
},
};
module.exports = nextConfig;
3. Add Necessary Pages and Components
Create a basic structure for your project with the following components:
a. pages/index.js - Main page:
export default function Home() {
return <h1>Welcome to Next.js 15</h1>;
}
b. components/StaticComponent.js - Static component for PPR:
export default function StaticComponent() {
return <div>Static content here</div>;
}
c. components/DynamicComponent.js - Dynamic component fetched client-side:
import { Suspense } from 'react';
export default function DynamicComponent() {
return <div>Dynamic content loaded!</div>;
}
d. pages/product.js - PPR example page:
import { Suspense } from 'react';
import StaticComponent from '@/components/StaticComponent';
import DynamicComponent from '@/components/DynamicComponent';
export const experimental_ppr = true;
export default function ProductPage() {
return (
<>
<StaticComponent />
<Suspense fallback={<div>Loading dynamic content...</div>}>
<DynamicComponent />
</Suspense>
</>
);
}
e. Testing the Setup
npm run dev
Open http://localhost:3000 to see your project running. This includes Partial Pre-rendering, Turbopack, and more advanced optimizations.
Features Covered:
- Partial Pre-rendering (PPR)
- after() API for post-response operations
- Caching optimizations
- Turbopack integration for faster builds
- React 19 and Compiler optimizations
These examples cover a wide array of use cases, showing how you can leverage Next.js 15 to build faster, more optimized web applications while improving both the user and developer experience. From PPR for hybrid rendering, to using the after() API for deferred tasks, Next.js 15 introduces tools that significantly push the boundaries of modern web development
Next.js by Vercel - The React Framework.
Reference: nextjs15
Top comments (2)
This is a great summary! Thank you for sharing
Thank you, I'm glad you found it helpful.