· frameworks  · 6 min read

Unlocking Next.js: Top 10 Hidden Features You Never Knew Existed

Discover 10 lesser-known Next.js features - from automatic static optimization and ISR to middleware, route handlers, next/script and next/font - with practical examples, tips and caveats to level up your Next.js apps.

Discover 10 lesser-known Next.js features - from automatic static optimization and ISR to middleware, route handlers, next/script and next/font - with practical examples, tips and caveats to level up your Next.js apps.

Next.js hides a surprising number of productivity- and performance-boosting features that many developers either overlook or only discover after hitting scaling or performance walls. This post walks through 10 of those lesser-known capabilities with practical examples, tips and gotchas so you can start using them today.

Why these matter

These features help you ship faster, make pages faster, simplify architecture (edge functions, route handlers) and optimize runtime behavior (image/script handling, ISR). Each section includes code snippets and links to the official docs.


1) Automatic Static Optimization - when your pages are silently static

Next.js automatically statically optimizes pages that have no server-only data fetching (no getServerSideProps, no getInitialProps). That means a page is generated at build time without any explicit getStaticProps call.

Tip: if you want a plain React component to be static, don’t add server-only data fetching or getInitialProps. Conversely, adding getInitialProps (or getServerSideProps) opts that page into server rendering and disables automatic static optimization.

Example (static by default):

// pages/about.js
export default function About() {
  return <div>About - static optimized automatically</div>;
}

Docs: https://nextjs.org/docs/advanced-features/automatic-static-optimization

Caveat: even small mistakes (using getServerSideProps or getInitialProps) will force SSR. Use getStaticProps for explicit static generation when you need data at build time.


2) Incremental Static Regeneration (ISR) + On-demand revalidation

ISR lets you update static pages after build time by providing a revalidate interval in getStaticProps.

export async function getStaticProps() {
  const posts = await fetch('https://api.example.com/posts').then(r =>
    r.json()
  );
  return { props: { posts }, revalidate: 60 }; // stale-while-revalidate every 60s
}

On-demand revalidation lets you trigger rebuilds programmatically (useful for CMS webhooks). A serverless API route can call Next.js revalidation APIs, e.g. res.revalidate('/path') (Pages Router) or revalidatePath() in App Router.

Example (Pages Router API route):

// pages/api/revalidate.js
export default async function handler(req, res) {
  // Protect with a token
  if (req.query.secret !== process.env.MY_SECRET) {
    return res.status(401).json({ message: 'Invalid token' });
  }
  await res.revalidate('/posts');
  return res.json({ revalidated: true });
}

Docs: https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration

Tip: For modern App Router, revalidatePath() and revalidateTag() give fine-grained control (see docs). Protect revalidation endpoints with secrets to avoid abuse.


3) next/script - precise control over third-party script loading

next/script gives you optimized loading strategies for scripts (prevent layout shifts and blocking). Strategies: beforeInteractive, afterInteractive (default), and lazyOnload.

import Script from 'next/script';

export default function Page() {
  return (
    <>
      <Script
        src="https://example.com/analytics.js"
        strategy="lazyOnload"
        onLoad={() => console.log('analytics loaded')}
      />
    </>
  );
}

Why it matters: placing third-party scripts incorrectly often hurts CLS and LCP. next/script avoids render-blocking and ensures proper ordering when needed.

Docs: https://nextjs.org/docs/basic-features/script

Caveat: beforeInteractive is powerful but should be used sparingly (it injects scripts early and can be blocking if abused).


4) next/image - more than just responsive images

The <Image /> component automatically optimizes images (responsive sizes, modern formats, lazy loading) and supports features many developers miss:

  • placeholder="blur" and blurDataURL for low-quality placeholders
  • priority to mark LCP images
  • sizes and srcSet control for custom breakpoints
  • Loader plugins and unoptimized for special cases

Example:

import Image from 'next/image';

<Image
  src="/images/hero.jpg"
  alt="Hero"
  width={1200}
  height={800}
  placeholder="blur"
  blurDataURL="data:image/..."
  priority
/>;

Docs: https://nextjs.org/docs/basic-features/image-optimization

Tip: Combine with next.config.js images.domains or use remote patterns. For heavy CMS use, consider a custom loader if your CDN provides optimized images.


5) Edge Middleware - run code at the CDN edge

Middleware runs before requests reach your app and executes on the Edge Runtime. Use it for A/B tests, authentication redirects, locale detection, and more.

Example (basic auth redirect):

// middleware.js (root)
import { NextResponse } from 'next/server';

export function middleware(request) {
  const token = request.cookies.get('auth');
  if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
    return NextResponse.redirect(new URL('/login', request.url));
  }
  return NextResponse.next();
}

export const config = { matcher: ['/dashboard/:path*'] };

Docs: https://nextjs.org/docs/advanced-features/middleware

Caveat: Edge Runtime has some limitations (no Node APIs like fs). Keep logic simple and fast to avoid added latency.


6) Route Handlers (App Router) and Runtime selection

With the App Router you can create route.ts/route.js files under app/* to define server endpoints (similar to API routes) with streaming, caching, and edge runtime support.

// app/api/hello/route.ts
import { NextResponse } from 'next/server';

export const runtime = 'edge';

export async function GET(request: Request) {
  return NextResponse.json({ hello: 'world' });
}

Benefits: smaller surface area, direct usage of Request/Response web APIs, and the ability to mark runtime = 'edge' for CDN-level execution.

Docs: https://nextjs.org/docs/app/building-your-application/routing/router-handlers

Tip: Use route handlers for API endpoints that should live close to users (auth checks, small transforms) and still integrate with App Router features like revalidatePath.


7) next/font - zero-config font optimization

next/font can automatically optimize Google Fonts and local fonts, handle subsetting and avoid layout shifts.

Google Fonts example:

import { Inter } from 'next/font/google';
const inter = Inter({ subsets: ['latin'], variable: '--font-inter' });

export default function App({ children }) {
  return <main className={inter.className}>{children}</main>;
}

Local font example:

import localFont from 'next/font/local';
const myFont = localFont({ src: './fonts/MyFont.woff2', display: 'swap' });

Docs: https://nextjs.org/docs/basic-features/font-optimization

Caveat: next/font is powerful but be mindful of licensing when bundling local fonts. For Google fonts, next/font/google fetches and optimizes fonts at build time.


next/link prefetches pages automatically when a link is visible in the viewport. But you can control it:

  • prefetch={false} - opt out of prefetching (useful for pages behind auth or many links)
  • shallow routing - change the URL without running data fetching methods again

Example (shallow routing):

import Link from 'next/link';
import { useRouter } from 'next/router';

function MyComponent() {
  const router = useRouter();
  return (
    <button
      onClick={() => router.push('/posts?page=2', undefined, { shallow: true })}
    >
      Next page
    </button>
  );
}

Docs: https://nextjs.org/docs/api-reference/next/link

Tip: disabling prefetch can reduce bandwidth on pages with many external or rarely used links.


9) Preview Mode - securely preview draft content

Preview Mode lets you render draft content (e.g., CMS drafts) without rebuilding the site. You enable preview via a secured API route that sets a preview cookie.

Example:

// pages/api/preview.js
export default function handler(req, res) {
  if (req.query.secret !== process.env.PREVIEW_SECRET)
    return res.status(401).end();
  res.setPreviewData({});
  res.writeHead(307, { Location: '/posts/preview-page' });
  res.end();
}

In your data fetching (Pages), check context.preview to fetch draft content.

Docs: https://nextjs.org/docs/advanced-features/preview-mode

Caveat: always protect preview endpoints with a secret and remove preview cookies (use res.clearPreviewData()) after use.


10) Redirects, Rewrites, and Headers via next.config.js

Instead of using a server, you can configure redirects, rewrites, and custom headers declaratively in next.config.js.

Example:

// next.config.js
module.exports = {
  async redirects() {
    return [
      {
        source: '/old-blog/:slug',
        destination: '/blog/:slug',
        permanent: true,
      },
    ];
  },
  async rewrites() {
    return [
      { source: '/api/:path*', destination: 'https://api.external.com/:path*' },
    ];
  },
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [{ key: 'X-Frame-Options', value: 'DENY' }],
      },
    ];
  },
};

Docs: https://nextjs.org/docs/api-reference/next.config.js/redirects

Tip: Rewrites are great for proxying APIs during SSR without exposing client-side API keys, and headers are handy to set security policies at the edge.


Quick checklist to get started using these features

  • Audit pages that unintentionally use server-only APIs and recover automatic static optimization where needed.
  • Add revalidate to selected static pages and create protected revalidation endpoints for CMS webhooks.
  • Replace raw script tags with next/script and mark LCP images with priority in next/image.
  • Move small auth/redirect logic to Middleware for lower-latency checks.
  • Explore route handlers when migrating to the App Router and use runtime = 'edge' for CDN execution.
  • Use next/font for consistent, optimized fonts and to avoid FOIT/CLS.
  • Configure redirects/rewrites/headers in next.config.js to simplify deployment rules.

Further reading

These hidden features can dramatically improve performance, developer experience and infrastructure simplicity. Try a couple on a small page and measure impact - next-level wins often come from composing a few of these capabilities together.

Back to Blog

Related Posts

View All Posts »
5 Hidden Features of AdonisJS You Didn't Know Existed

5 Hidden Features of AdonisJS You Didn't Know Existed

Discover five lesser-known AdonisJS features - route param/model binding, named routes & URL generation, Lucid computed/serialization tricks, advanced validator schema patterns, and streaming-safe file uploads - with practical examples and tips to make your apps faster to build and easier to maintain.