· frameworks · 5 min read
10 Next.js Tips You Didn't Know About: Unlocking Hidden Features
Discover 10 lesser-known Next.js features - from edge route handlers and middleware tricks to font optimization, smarter caching and streaming server components - that can meaningfully boost performance and developer experience.

Introduction
Next.js is packed with features - some obvious, some hidden in the docs. This article highlights 10 lesser-known tips and patterns that can improve your app’s performance, UX and developer productivity. Each tip includes what it does, why it helps, and a practical example.
Tip 1 - Auto-optimized fonts with next/font
Why it helps
Serving fonts efficiently reduces layout shift and improves load time. next/font
automatically optimizes font delivery and inlines critical CSS for your fonts.
How to use
// app/layout.jsx (App Router)
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
variable: '--font-inter',
display: 'swap',
});
export default function RootLayout({ children }) {
return (
<html lang="en" className={inter.variable}>
<body>{children}</body>
</html>
);
}
Notes
- Works with Google-hosted fonts and local fonts.
- Avoid flash of invisible text by using
display: 'swap'
or other strategies.
Docs: https://nextjs.org/docs/basic-features/font-optimization
Tip 2 - Route handlers + Edge Functions for ultra-fast APIs
Why it helps
Route handlers (in the app directory) let you run server code with the Edge runtime, reducing latency by running close to users.
Example
// app/api/hello/route.js
export const runtime = 'edge'
export async function GET(request) {
return new Response(JSON.stringify({ hello: 'from edge' }), {
headers: { 'content-type': 'application/json' }
})
}
When to use
- Low-latency features like geolocation, authentication checks, or personalization.
- Lightweight compute and fast response requirements.
Docs: https://nextjs.org/docs/app/building-your-application/routing/route-handlers
Tip 3 - Middleware for branching, A/B tests and smart caching
Why it helps
Middleware runs at the edge before a request reaches your app. Use it for redirects, A/B tests, locale routing, authentication gates, and altering request/response headers.
Example
// middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
const { pathname } = request.nextUrl;
if (pathname.startsWith('/old-path')) {
const url = request.nextUrl.clone();
url.pathname = '/new-path';
return NextResponse.rewrite(url);
}
return NextResponse.next();
}
Caveats
- Runs on the Edge runtime - limited Node APIs.
- Keep it fast and small to avoid adding latency.
Docs: https://nextjs.org/docs/advanced-features/middleware
Tip 4 - Use next/image
’s AVIF/modern formats, priority
, and fill
Why it helps
The next/image
component optimizes images automatically (format, size, lazy loading). Choosing modern formats (AVIF/WebP), priority
for LCP images, and fill
for responsive backgrounds boosts perceived performance.
Example
import Image from 'next/image';
export default function Hero() {
return (
<div style={{ position: 'relative', height: '60vh' }}>
<Image
src="/hero.jpg"
alt="Hero"
fill
priority // important for LCP
sizes="(max-width: 768px) 100vw, 50vw"
/>
</div>
);
}
Docs: https://nextjs.org/docs/basic-features/image-optimization
Tip 5 - Incremental Static Regeneration (ISR) with precise revalidation
Why it helps
ISR keeps the benefits of static pages while allowing fresh content. In the App Router or fetch API, pass next: { revalidate: <seconds> }
to control caching at a fine-grained level.
Example (App Router using fetch):
export default async function Page() {
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 60 },
});
const data = await res.json();
return <div>{/* render data */}</div>;
}
When to pick it
- CMS-driven pages that update periodically.
- Avoid rebuilding the whole site for content changes.
Tip 6 - Use next/script
to control third-party scripts loading
Why it helps
Third-party scripts can block the main thread. next/script
offers loading strategies (beforeInteractive, afterInteractive, lazyOnload) to prevent blocking and improve CLS/LCP.
Example
import Script from 'next/script';
export default function Analytics() {
return (
<>
<Script
src="https://example-cdn/analytics.js"
strategy="lazyOnload"
onLoad={() => console.log('analytics loaded')}
/>
</>
);
}
Docs: https://nextjs.org/docs/basic-features/script
Tip 7 - Dynamic import with SSR control and Suspense
Why it helps
Split heavy components into chunks, load them only when needed, and reduce initial bundle size. You can also opt out of SSR for components that only run in the browser.
Example
import dynamic from 'next/dynamic';
import { Suspense } from 'react';
const Map = dynamic(() => import('../components/Map'), { ssr: false });
export default function Page() {
return (
<Suspense fallback={<div>Loading map...</div>}>
<Map />
</Suspense>
);
}
Docs: https://nextjs.org/docs/advanced-features/dynamic-import
Tip 8 - Streaming Server Components (App Router) for faster Time-to-First-Byte
Why it helps
Server components can stream HTML to the client as pieces become available. This reduces Time-to-First-Byte and allows the browser to start rendering earlier.
Pattern
- Compose your UI as async server components.
- Use suspense boundaries and smaller islands for client-side interactivity.
Example
// app/page.jsx
export default async function Page() {
const posts = await fetch('...').then(r => r.json());
return (
<>
<Header />
<main>
<Suspense fallback={<div>Loading posts...</div>}>
<PostsList posts={posts} />
</Suspense>
</main>
</>
);
}
Docs: https://nextjs.org/docs/app/building-your-application/routing#server-components
Tip 9 - Fine-grained fetch caching and revalidation
Why it helps
Next.js’ built-in fetch integrates caching and revalidation options. Use next: { revalidate }
, cache: 'force-cache'
, or cache: 'no-store'
to control freshness per request rather than per page.
Example
// cache for 5 minutes
const res = await fetch('https://api.example.com/data', {
next: { revalidate: 300 },
});
Why prefer this
- Avoids large static rebuilds.
- Lets you cache different data sources with different TTLs.
Docs: https://nextjs.org/docs/basic-features/data-fetching/fetching
Tip 10 - Rewrites, Redirects and Headers for security, SEO and CORS workarounds
Why it helps
Use rewrites to proxy API requests (avoid CORS), redirects for SEO changes, and headers to add security policies or caching rules without touching server code.
Example (next.config.js)
module.exports = {
async rewrites() {
return [
{
source: '/api/:path*',
destination: 'https://third-party.example/:path*',
},
];
},
async headers() {
return [
{
source: '/(.*)',
headers: [{ key: 'X-Frame-Options', value: 'DENY' }],
},
];
},
};
Docs: https://nextjs.org/docs/app/api-reference/next-config-js/headers-rewrites
Conclusion
These tips are practical ways to get more out of Next.js beyond the basics: faster pages with edge handlers, smarter fetching and caching, better third-party script handling, and modern font/image techniques. Try one or two on a small page first, measure before/after, and roll them into your app incrementally.
References
- Next.js Font Optimization - https://nextjs.org/docs/basic-features/font-optimization
- Route Handlers + Edge Runtime - https://nextjs.org/docs/app/building-your-application/routing/route-handlers
- Middleware - https://nextjs.org/docs/advanced-features/middleware
- Image Optimization - https://nextjs.org/docs/basic-features/image-optimization
- ISR - https://nextjs.org/docs/pages/building-your-application/data-fetching/incremental-static-regeneration
- Script component - https://nextjs.org/docs/basic-features/script
- Dynamic imports - https://nextjs.org/docs/advanced-features/dynamic-import
- Server Components/Streaming - https://nextjs.org/docs/app/building-your-application/routing#server-components
- Fetch caching - https://nextjs.org/docs/basic-features/data-fetching/fetching
- Rewrites/Headers - https://nextjs.org/docs/app/api-reference/next-config-js/headers-rewrites