DEV Community

Cover image for Next.js and the Evil Web Vitals - a ranking killer?
BB Dev Team
BB Dev Team

Posted on

Next.js and the Evil Web Vitals - a ranking killer?

Building a large-scale platform in a highly competitive industry means two things:

  1. Visibility is survival (SEO, SSR, structured data).
  2. Performance is ranking (Core Web Vitals decide who’s on top).

In this article, we’ll share why we chose Next.js 15 (with Turbo & App Router) as the foundation of our frontend, how we leverage it for SEO, SSR, and PWA, and how we overcame performance challenges like INP (Interaction to Next Paint) and FCP (First Contentful Paint).


Why Next.js for Our Platform?

Our requirements are very specific:

  • Server-Side Rendering (SSR): Critical for SEO-heavy pages that need to be indexed fast.
  • Static Generation + Revalidation: Balance between speed and freshness of content.
  • Progressive Web App (PWA): We use Serwist to add offline caching, push notifications, and app-like behavior.
  • Internationalization (i18n): Our audience is global – Next.js handles routing and language detection elegantly.
  • API Routes: Simplify integration with backend services.
  • App Router + Turbo: Cleaner routing, faster builds, and improved scalability.

Next.js gives us the best of both worlds: SSR for crawlers and static caching for humans.


The Challenges in Our Industry

We operate in a very competitive market where SEO is extremely hard-fought. Every millisecond and every snippet of metadata counts.

  • Load Times: Bounce rates skyrocket if users wait.
  • Crawling Efficiency: Google punishes slow sites with poor Web Vitals.
  • Meta Data Wars: Correct OpenGraph tags decide whether a page gets shared.
  • Structured Data: JSON-LD is essential for rich snippets in search results.

That’s why we implemented a full SEO toolkit in our platform:

  • Dynamic meta tags and OpenGraph tags.
  • Structured data with JSON-LD (Article, BreadcrumbList, VideoObject, etc.).
  • Automated sitemap.xml and RSS feeds.
  • Canonical tags to avoid duplicate indexing.

Performance Problems with Next.js

Next.js is powerful, but it comes with challenges:

  • INP (Interaction to Next Paint): Hydration can delay interaction readiness.
  • FCP (First Contentful Paint): JavaScript-heavy pages delay first paint.
  • Bundle Size: Next.js includes a lot of code by default. If you don’t monitor carefully, it explodes.
  • next/image Issues: While useful, it’s often too slow for our needs and adds extra runtime overhead.

At scale, these issues directly impact rankings and conversions.


How We Tamed the Web Vitals

We approached performance systematically:

1. Aggressive Caching

  • CDN Caching for static assets.
  • Incremental Static Regeneration (ISR): Pages revalidate every X seconds → always fresh, never slow.
export async function getStaticProps() {
  return {
    props: {}, 
    revalidate: 60, // page is regenerated every 60 seconds
  };
}
Enter fullscreen mode Exit fullscreen mode

2. Partial Caching with Revalidate Keys

Since we are a platform with highly dynamic content, full caching is not possible.

Our solution: partial caching.

  • Static sections of a page (layout, headers, footers, metadata) are cached aggressively.
  • Dynamic sections (recommendations, user feeds, trending data) are updated frequently.
  • We rely heavily on revalidate keys to refresh only the parts that change.
// Example with revalidateTag in Next.js 15
import { revalidateTag } from "next/cache";

export async function POST(req: Request) {
  const body = await req.json();
  revalidateTag(`user-${body.userId}`); // revalidate only specific user cache
  return Response.json({ revalidated: true });
}
Enter fullscreen mode Exit fullscreen mode

This approach allows us to serve fast cached pages while keeping dynamic data fresh.

3. The Golden Strategy: Async Loading

Our principle is simple: load everything asynchronously except what’s visible above-the-fold on first render.

  • Critical UI (headers, navigation, hero content) loads instantly.
  • Everything else (widgets, tracking, secondary UI) is loaded asynchronously.
  • This ensures fast FCP/LCP while still reducing bundle size and execution time.

4. Image Optimization in the Backend

Instead of relying only on next/image, our backend pre-generates images in multiple formats (WebP, AVIF, JPEG).

This reduces runtime overhead and ensures the best format is delivered instantly via CDN.

5. Library & UI Optimization

We spend many hours profiling and optimizing third-party libraries, especially UI components.

Unnecessary dependencies are removed, and only the minimal code required is bundled.

6. Bundle Size Control

  • We use next-bundle-analyzer to keep bundle size under control.
  • Strict commit guidelines ensure no debug or unused code slips in.
  • Every KB matters, especially since Next.js ships with a heavy base.

7. Fonts: Loaded Correctly via NestJS + Next.js

Fonts can drastically impact FCP. Our strategy combines NestJS static serving with Next.js optimization:

  • Fonts are self-hosted and served via NestJS static middleware.
  • Correct Cache-Control headers ensure long-term caching.
  • Next.js 15’s @next/font is used for automatic font-subsetting and preload links.
  • Above-the-fold text fonts are preloaded, while secondary fonts are loaded async.

Results: Best Possible Web Vitals

After multiple iterations we reached:

  • INP: consistently under Google’s “good” threshold.
  • FCP: improved by caching, async loading, and font optimization.
  • LCP (Largest Contentful Paint): optimized via backend image pre-generation, partial caching, and CDN.

Our Web Vitals are now among the best in our industry, directly improving SEO and user retention.


Conclusion

In a competitive industry, SEO + Performance = Survival.

  • Next.js 15 (with Turbo and App Router) gives us SSR, SEO tools, PWA support (via Serwist), and scalability.
  • The ecosystem around metadata (meta tags, OpenGraph, JSON-LD, sitemaps, RSS) makes it the best choice for visibility.
  • Performance challenges (INP, FCP, hydration, bundle size, next/image) are real, but solvable with caching, async loading, backend image optimization, and careful bundle management.
  • Partial caching with revalidate keys is the secret weapon for dynamic platforms like ours.
  • Fonts must be handled carefully – preloaded for above-the-fold, async for the rest.

Next.js may bring “evil” Web Vitals problems out of the box, but with the right strategy, you can tame them and even turn them into a competitive advantage.

Top comments (0)