Loading content…
Loading content…
Master enterprise optimization: next/font, next/script, next/dynamic, bundle analyzing, Winston/Pino production logging, and Turborepo monorepos
<link> tags cause Cumulative Layout Shift (CLS) because the browser renders fallback fonts before the real font finishes downloading over the network.
Next.js solves this by downloading fonts at build time and hosting them locally.// app/layout.tsx
import { Inter, Outfit } from "next/font/google";
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter", // Custom CSS variable name
display: "swap"
});
const outfit = Outfit({
subsets: ["latin"],
variable: "--font-outfit",
display: "swap"
});
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className={`${inter.variable} ${outfit.variable}`}>
<body className="font-sans">{children}</body>
</html>
);
}
next/script component optimizes scripts using four strategies:beforeInteractive: Loads before any Next.js code. Use for critical scripts (like security bots).afterInteractive (Default): Loads after the page becomes interactive. Use for standard analytics.lazyOnload: Loads during browser idle time. Use for heavy widgets (chatbots).worker: Loads inside a Web Worker thread (experimental, keeps main thread free).import Script from "next/script";
export default function App() {
return (
<div>
{/* Standard Google Analytics tag loaded after hydration */}
<Script
src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"
strategy="afterInteractive"
/>
{/* Heavy chatbot widget loaded during browser idle */}
<Script
src="https://example.com/chat-widget.js"
strategy="lazyOnload"
/>
</div>
);
}
next/dynamic to shrink the initial bundle size."use client";
import { useState } from "react";
import dynamic from "next/dynamic";
// Load heavy Chart component only when requested (disables Server-Side pre-rendering)
const HeavyChart = dynamic(() => import("@/components/HeavyChart"), {
ssr: false,
loading: () => <p>Loading chart widget...</p>
});
export default function AnalyticsDashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div className="p-6">
<button onClick={() => setShowChart(true)} className="bg-indigo-600 text-white p-2 rounded">
Load Analytics Report
</button>
{showChart && <HeavyChart />}
</div>
);
}
npm install -D @next/bundle-analyzerimport withBundleAnalyzer from "@next/bundle-analyzer";
const nextConfig = {
// Your base config options here
};
export default withBundleAnalyzer({
enabled: process.env.ANALYZE === "true",
})(nextConfig);
ANALYZE=true npm run build to open dynamic HTML charts showing exactly how much bytes each library contributes.console.log statements are synchronous and block execution in production Node.js servers. Additionally, they do not collect structured metadata (timestamps, environment, error details).// lib/logger.ts
import pino from "pino";
const isProduction = process.env.NODE_ENV === "production";
export const logger = pino({
level: isProduction ? "info" : "debug",
transport: isProduction
? undefined
: {
target: "pino-pretty", // human-readable terminal output for dev
options: { colorize: true }
}
});
// app/api/checkout/route.ts
import { NextResponse } from "next/server";
import { logger } from "@/lib/logger";
export async function POST(request: Request) {
try {
const { items, userId } = await request.json();
// Log structured context
logger.info({ userId, itemsCount: items.length }, "Checkout process started");
// Checkout database logic...
return NextResponse.json({ success: true });
} catch (error: any) {
logger.error({ error: error.message }, "Checkout process failed");
return NextResponse.json({ error: "Failed to process order" }, { status: 500 });
}
}
Common Pitfall
/my-turborepo
package.json
turbo.json
/apps
/dashboard → Next.js app (imports @repo/ui)
/landing-page → Next.js app (imports @repo/ui)
/packages
/ui → Shared Tailwind/React component library
/tsconfig → Shared TypeScript config files
Optimization & Scale Checklist
next/font to avoid CLS.next/script with lazyOnload.Marking it complete updates your roadmap progress percentage.