| name | turbopack |
| description | [Applies to: **/*.{js,jsx}] This guide provides opinionated, actionable best practices for developing high-performance, maintainable, and secure applications using Turbopack as the default bundler in Next.js 16+. |
| source | cursor_mdc |
turbopack Best Practices
Turbopack is the default, Rust-based incremental bundler for Next.js 16+, offering unparalleled speed through function-level caching, lazy bundling, and a unified build graph. To fully leverage its power, adhere to these modern best practices.
1. Code Organization and Structure
Turbopack thrives on a well-structured project. Standardize your directory layout to maximize its incremental compilation benefits.
1.1 Standardized Directory Structure
Always place application source code under src/. This provides a clear boundary and helps Turbopack understand your project's scope.
❌ BAD: Scattered files
✅ GOOD: Centralized src/
1.2 TypeScript First
Turbopack has built-in, highly optimized support for TypeScript and JSX/TSX via SWC. Always use TypeScript to catch errors early and enhance developer experience.
❌ BAD: Plain JavaScript
export function add(a, b) {
return a + b;
}
✅ GOOD: Type-safe TypeScript
export function add(a: number, b: number): number {
return a + b;
}
1.3 Component Granularity
Design components to be small, pure, and focused on a single responsibility. This maximizes Turbopack's function-level caching and improves Fast Refresh times.
❌ BAD: Large, monolithic component
'use client';
import { useState, useEffect } from 'react';
import { fetchUserData, updateUserProfile } from '@/services/api';
export default function UserProfilePage({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => { }, [userId]);
const handleSubmit = () => { };
return (
);
}
✅ GOOD: Small, composable components
import { Suspense } from 'react';
import UserProfileForm from '@/components/UserProfileForm';
import UserOrdersList from '@/components/UserOrdersList';
import { fetchUserData } from '@/services/api';
export default async function ProfilePage() {
const user = await fetchUserData();
return (
<div className="container">
<h1>Welcome, {user.name}</h1>
<Suspense fallback={<p>Loading profile...</p>}>
<UserProfileForm initialData={user} />
</Suspense>
<Suspense fallback={<p>Loading orders...</p>}>
);
}
;
{ useState } ;
{ updateUserProfile } ;
() {
[name, setName] = (initialData.);
[email, setEmail] = (initialData.);
= () => {
e.();
({ name, email });
();
};
(
);
}
2. Common Patterns and Anti-patterns
Leverage Next.js 16 features that align with Turbopack's strengths, and avoid patterns that hinder its optimizations.
2.1 Embrace Next.js App Router and Server Components
Next.js 16 defaults to the App Router and Server Components. This is the optimal architecture for Turbopack, as it enables the unified graph and efficient server/client bundling.
❌ BAD: Sticking to Pages Router for new features
✅ GOOD: Utilize App Router with Server Components
import { fetchDashboardData } from '@/services/api';
export default async function DashboardPage() {
const data = await fetchDashboardData();
return (
<div>
<h1>Dashboard</h1>
<p>Data: {data.summary}</p>
</div>
);
}
2.2 Explicit Runtime and Caching Directives
Guide Turbopack's incremental computation and caching with explicit flags.
export const runtime = 'edge' for Edge Functions.
export const dynamic = 'force-static' for full static rendering.
export const revalidate = 60 for Incremental Static Regeneration (ISR).
"use cache" for fine-grained component caching (Next.js 16+).
❌ BAD: Implicit behavior, relying on defaults for critical paths
export async function GET() { }
✅ GOOD: Explicitly define runtime for performance-critical APIs
export const runtime = 'edge';
export async function GET() {
return new Response('Hello from the Edge!');
}
And for component caching:
"use cache";
export default function CachedHeader() {
return <header>My Cached App Header</header>;
}
2.3 Intentional Code Splitting
Turbopack supports lazy bundling. Use next/dynamic or standard dynamic import() for large, non-critical components or libraries to reduce initial bundle size.
❌ BAD: Importing heavy components eagerly
import Chart from 'heavy-chart-library';
export default function Dashboard() {
return <Chart data={...} />;
}
✅ GOOD: Dynamically import components
import dynamic from 'next/dynamic';
const Chart = dynamic(() => import('heavy-chart-library'), {
ssr: false,
loading: () => <p>Loading chart...</p>,
});
export default function Dashboard() {
return (
<div>
<h1>Dashboard Overview</h1>
<Chart data={...} />
</div>
);
}
3. Performance Considerations
Turbopack is built for speed. Align your code with its incremental nature.
3.1 Maximize Function-Level Caching
Design modules with small, pure functions. Turbopack caches results at this granular level, significantly speeding up rebuilds when only small parts of the codebase change.
❌ BAD: Functions with side effects or large dependencies
let globalConfig = {};
export function processData(data) {
return processed;
}
✅ GOOD: Pure, isolated functions
export function processData(data: any[], config: any): any[] {
return processed;
}
3.2 Scope CSS and Assets
Import CSS and other assets directly within the components that use them. This allows Turbopack to effectively lazy bundle and optimize asset loading. Use CSS Modules for component-scoped styles.
❌ BAD: Global CSS imports for component-specific styles
import '../styles/button.css';
export default function RootLayout({ children }) { }
✅ GOOD: CSS Modules for component-specific styles
.primary {
background-color: blue;
color: white;
padding: 8px 16px;
border-radius: 4px;
}
import styles from './MyButton.module.css';
export default function MyButton() {
return <button className={styles.primary}>Click me</button>;
}
3.3 Leverage Next.js 16 Cache Components
Enable cacheComponents: true in next.config.ts and use the "use cache" directive to cache component outputs. This is a powerful feature for instant navigation with Partial Pre-rendering (PPR).
const nextConfig = {
cacheComponents: true,
};
export default nextConfig;
Then, in your components:
"use cache";
import Image from 'next/image';
export default function ProductCard({ product }) {
return (
<div className="product-card">
<Image src={product.image} alt={product.name} width={200} height={200} />
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
);
}
4. Common Pitfalls and Gotchas
Be aware of these common issues when working with Turbopack.
4.1 Avoid Direct Webpack Configuration
Turbopack aims to abstract away Webpack. Do not attempt to configure Webpack directly when using Turbopack, as it will be ignored or lead to unexpected behavior. Configure Turbopack via next.config.js options where available.
❌ BAD: Attempting to modify Webpack config directly
module.exports = {
webpack: (config, { isServer }) => {
config.plugins.push(new MyWebpackPlugin());
return config;
},
};
✅ GOOD: Use Next.js 16's next.config.js options for Turbopack
const nextConfig = {
turbopack: {
resolveAlias: {
'@my-custom-alias': '/path/to/custom/module',
},
root: '../../',
},
};
module.exports = nextConfig;
4.2 Filesystem Root for Monorepos
If you use linked dependencies (e.g., npm link, yarn link, pnpm link) that reside outside your project's root, Turbopack will not resolve them by default. Adjust the turbopack.root option in next.config.js.
❌ BAD: Linked dependencies not resolving
// project-root/
// └── src/
// └── app/page.tsx
//
// sibling-package/ (linked via npm link)
// └── index.ts
import { someFunction } from 'sibling-package';
✅ GOOD: Configure turbopack.root
const nextConfig = {
turbopack: {
root: '../../',
},
};
module.exports = nextConfig;
4.3 Sass Functions
Turbopack's Rust-based architecture does not support custom Sass functions (sassOptions.functions) that rely on JavaScript execution. If you need this, you must opt out of Turbopack for that specific project using --webpack.
❌ BAD: Custom Sass functions with Turbopack
module.exports = {
sassOptions: {
functions: {
'my-custom-func($value)': (value) => { },
},
},
};
✅ GOOD: Avoid custom Sass functions or use Webpack explicitly
next dev --webpack
Otherwise, refactor your Sass to avoid custom JS functions.
5. Testing Approaches
Integrate Turbopack into your testing pipeline to ensure compiled output behaves as expected.
5.1 Unit and Integration Tests
Run your tests against the compiled output. Turbopack provides built-in source map support for debugging, making it easier to pinpoint issues in your original source code.
{
"scripts": {
"test": "jest --passWithNoTests",
"test:watch": "jest --watch",
"build": "next build"
}
}
Ensure your testing setup (e.g., Jest, Vitest) is configured to handle TypeScript and JSX/TSX, which Turbopack processes.
5.2 End-to-End (E2E) Testing
Always run E2E tests against the Turbopack-bundled production build (next build then next start). This validates the entire application flow, including how assets are loaded and code is split by Turbopack.
{
"scripts": {
"build": "next build",
"start": "next start",
"e2e": "playwright test"
}
}
Run npm run build && npm run start in a CI environment, then execute your Playwright or Cypress tests against the running application.
By adhering to these guidelines, your team will build high-performance, maintainable, and secure Next.js applications that fully leverage the power of Turbopack.