Next.js 15 for Enterprise: New Features and Migration Strategies
Next.js 15 introduces significant improvements for enterprise applications, including better performance, enhanced developer experience, and improved caching strategies. This guide covers the key features and migration strategies for large-scale applications.
What Are the Key Features in Next.js 15?
Turbopack Stable
The Rust-based bundler is now production-ready:
# Enable Turbopack for development
next dev --turbo
# Performance improvements
# - Up to 95% faster local server startup
# - Up to 68% faster code updates
# - Significant memory usage reductionReact 19 Integration
Next.js 15 fully supports React 19 features:
// Server Components with Actions
async function addToCart(formData: FormData) {
'use server';
const productId = formData.get('productId');
const quantity = formData.get('quantity');
await db.cart.add({ productId, quantity });
revalidatePath('/cart');
}
export default function ProductPage({ product }) {
return (
<form action={addToCart}>
<input type="hidden" name="productId" value={product.id} />
<input type="number" name="quantity" defaultValue={1} min={1} />
<SubmitButton />
</form>
);
}
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? 'Adding...' : 'Add to Cart'}
</button>
);
}Improved Caching Model
More predictable and controllable caching:
// app/products/[id]/page.tsx // Static by default - cached at build time export default async function ProductPage({ params }) { const product = await getProduct(params.id); return <ProductView product={product} />; } // Dynamic data fetching with cache control async function getProduct(id: string) { const res = await fetch(, { next: { revalidate: 3600, // Revalidate every hour tags: ['products',https://api.example.com/products/${id}product-${id}], }, }); return res.json(); } // On-demand revalidation // app/api/revalidate/route.ts export async function POST(request: Request) { const { tag } = await request.json(); revalidateTag(tag); return Response.json({ revalidated: true, now: Date.now() }); }
How Do You Structure Enterprise Next.js Applications?
Monorepo Setup with Turborepo
apps/
โโโ web/ # Main web application
โโโ admin/ # Admin dashboard
โโโ docs/ # Documentation site
packages/
โโโ ui/ # Shared UI components
โโโ config/ # Shared configurations
โโโ database/ # Database client and schemas
โโโ utils/ # Shared utilities
// turbo.json
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["*/.env.local"],
"pipeline": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/", "!.next/cache/"]
},
"dev": {
"cache": false,
"persistent": true
},
"lint": {},
"test": {}
}
}Feature Module Pattern
src/
โโโ app/ # Next.js app router
โโโ features/
โ โโโ auth/
โ โ โโโ components/
โ โ โ โโโ LoginForm.tsx
โ โ โ โโโ SignupForm.tsx
โ โ โโโ actions/
โ โ โ โโโ auth.ts
โ โ โโโ hooks/
โ โ โ โโโ useAuth.ts
โ โ โโโ types.ts
โ โโโ products/
โ โ โโโ components/
โ โ โโโ actions/
โ โ โโโ hooks/
โ โโโ checkout/
โโโ shared/
โ โโโ components/ # Shared UI components
โ โโโ hooks/ # Shared hooks
โ โโโ utils/ # Utility functions
โโโ lib/ # External integrations
How Do You Handle Authentication at Scale?
NextAuth.js Integration
// lib/auth.ts
import NextAuth from 'next-auth';
import { DrizzleAdapter } from '@auth/drizzle-adapter';
import { db } from '@/lib/database';
import Credentials from 'next-auth/providers/credentials';
import Google from 'next-auth/providers/google';
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: DrizzleAdapter(db),
session: { strategy: 'jwt' },
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
Credentials({
credentials: {
email: { type: 'email' },
password: { type: 'password' },
},
async authorize(credentials) {
const user = await db.query.users.findFirst({
where: eq(users.email, credentials.email),
});
if (!user || !verifyPassword(credentials.password, user.password)) {
return null;
}
return { id: user.id, email: user.email, role: user.role };
},
}),
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.role = user.role;
}
return token;
},
async session({ session, token }) {
session.user.role = token.role;
return session;
},
},
});
Protected Routes Middleware
// middleware.ts
import { auth } from '@/lib/auth';
export default auth((req) => {
const isLoggedIn = !!req.auth;
const isAdminRoute = req.nextUrl.pathname.startsWith('/admin');
const isApiRoute = req.nextUrl.pathname.startsWith('/api');
// API routes handle their own auth
if (isApiRoute) return;
// Redirect unauthenticated users
if (!isLoggedIn && isAdminRoute) {
return Response.redirect(new URL('/login', req.nextUrl));
}
// Check admin role for admin routes
if (isAdminRoute && req.auth?.user?.role !== 'admin') {
return Response.redirect(new URL('/unauthorized', req.nextUrl));
}
});
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).)'],
};
What Performance Optimizations Are Essential?
Parallel Data Fetching
// app/dashboard/page.tsx
export default async function DashboardPage() {
// Parallel data fetching
const [user, stats, notifications, recentOrders] = await Promise.all([
getUser(),
getStats(),
getNotifications(),
getRecentOrders(),
]);
return (
<Dashboard>
<UserHeader user={user} />
<StatsGrid stats={stats} />
<NotificationList notifications={notifications} />
<OrdersTable orders={recentOrders} />
</Dashboard>
);
}Streaming with Suspense
import { Suspense } from 'react';
export default async function Page() {
const user = await getUser(); // Fast, required for layout
return (
<Layout user={user}>
<Suspense fallback={<StatsSkeleton />}>
<Stats /> {/ Can load independently /}
</Suspense>
<Suspense fallback={<ChartSkeleton />}>
<RevenueChart /> {/ Heavy component /}
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentOrders /> {/ Database query */}
</Suspense>
</Layout>
);
}Image Optimization
import Image from 'next/image';
export function ProductImage({ product }) {
return (
<Image
src={product.image}
alt={product.title}
width={800}
height={600}
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
placeholder="blur"
blurDataURL={product.blurDataUrl}
priority={product.featured}
/>
);
}How Do You Monitor Next.js Applications?
OpenTelemetry Integration
// instrumentation.ts
export async function register() {
if (process.env.NEXT_RUNTIME === 'nodejs') {
const { NodeSDK } = await import('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = await import(
'@opentelemetry/auto-instrumentations-node'
);
const sdk = new NodeSDK({
serviceName: 'my-next-app',
instrumentations: [getNodeAutoInstrumentations()],
});
sdk.start();
}
}
Next.js 15 provides the foundation for building high-performance enterprise applications. By leveraging these features and following best practices, you can create applications that scale reliably and deliver exceptional user experiences.