Skip to content

Performance optimization

Performance Optimization

When to Use

Apply these optimizations to reduce build times, improve page load speed, and minimize API requests.

Decision

Optimization Impact Implementation
Sparse fieldsets 50-80% smaller responses Use fields parameter
Resource caching Faster builds, fewer requests Cache menus/blocks during build
ISR over SSG Faster builds for large sites Use revalidate or tags
Image optimization 60-80% smaller images Next.js Image component
Page limit override Fetch >50 resources Configure next_jsonapi.size_max
Includes vs separate requests Reduce HTTP requests Use include parameter

Pattern

Sparse fieldsets (only fetch needed fields):

const articles = await drupal.getResourceCollection("node--article", {
  params: {
    "fields[node--article]": "title,created,path",
    "fields[user--user]": "display_name",
  },
})

Cache global resources during build:

import { PHASE_PRODUCTION_BUILD } from "next/constants"

const menu = await drupal.getMenu("main", {
  withCache: process.env.NEXT_PHASE === PHASE_PRODUCTION_BUILD,
  cacheKey: "menu:main",
})

Use includes for relationships:

// GOOD: One request
const article = await drupal.getResource("node--article", uuid, {
  params: {
    include: "field_image,uid,field_tags",
  },
})

// BAD: Multiple requests
const article = await drupal.getResource("node--article", uuid)
const author = await drupal.getResource("user--user", article.uid.id)
const image = await drupal.getResource("media--image", article.field_image.id)

ISR with fallback blocking for large sites:

export async function generateStaticParams() {
  // Only generate top 100 most visited pages
  const topPages = await getTopPages(100)
  return topPages.map(page => ({ slug: page.segments }))
}

export const dynamicParams = true // Enable fallback: blocking
export const revalidate = 3600 // Revalidate after 1 hour

Image optimization:

import Image from "next/image"

<Image
  src={imageUrl}
  width={800}
  height={600}
  sizes="(max-width: 768px) 100vw, 800px"
  priority={isAboveFold} // Only for above-the-fold images
/>

Page limit override:

# Drupal: sites/default/services.yml
parameters:
  next_jsonapi.size_max: 100
// Next.js: Must use sparse fieldsets with path field
const pages = await drupal.getResourceCollection("node--page", {
  params: {
    "fields[node--page]": "path,title", // path field triggers override
    "page[limit]": 100,
  },
})

Common Mistakes

  • Not using sparse fieldsets — Fetches all fields. WHY: Body field can be massive, often unnecessary.
  • Pre-generating all paths for large sites — Build timeout. WHY: Use fallback: 'blocking' or ISR instead.
  • Not caching menus and blocks — Refetch on every page build. WHY: Global resources rarely change.
  • Using SSR when ISR is sufficient — Slower page loads. WHY: ISR provides nearly same freshness with caching.
  • Loading images without dimensions — Layout shift. WHY: Next.js needs width/height for optimization.
  • Including unused relationships — Larger responses. WHY: Only include what you render.

See Also

  • Fetching Content
  • Building Pages
  • Media and Images
  • drupal-jsonapi.md