---
title: "Performance"
description: "Optimize your WPNuxt site for speed and Core Web Vitals"
canonical_url: "https://wpnuxt.com/guide/performance"
---
# Performance

> Optimize your WPNuxt site for speed and Core Web Vitals

This guide covers strategies for building fast WPNuxt sites, from rendering mode selection to query optimization and Core Web Vitals.

## SSR vs SSG

<table>
<thead>
  <tr>
    <th>
      
    </th>
    
    <th>
      SSR (Server-Side Rendering)
    </th>
    
    <th>
      SSG (Static Site Generation)
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <strong>
        Build time
      </strong>
    </td>
    
    <td>
      Fast (no pre-rendering)
    </td>
    
    <td>
      Slower (fetches all content at build)
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Time to first byte
      </strong>
    </td>
    
    <td>
      ~50-200ms (with caching)
    </td>
    
    <td>
      ~10-50ms (static files)
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Content freshness
      </strong>
    </td>
    
    <td>
      Real-time (with SWR)
    </td>
    
    <td>
      Stale until rebuild
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Best for
      </strong>
    </td>
    
    <td>
      Frequently updated sites, many pages
    </td>
    
    <td>
      Blogs, documentation, marketing sites
    </td>
  </tr>
  
  <tr>
    <td>
      <strong>
        Hosting
      </strong>
    </td>
    
    <td>
      Requires Node.js server
    </td>
    
    <td>
      Any static host (CDN)
    </td>
  </tr>
</tbody>
</table>

For most WordPress sites, **SSR with caching** offers the best balance of freshness and performance. See the [SSG guide](https://wpnuxt.com/deploy/ssg) if your content changes infrequently.

## Lazy Loading Composables

Use `lazy: true` for content that isn't needed for the initial render:

```vue
<script setup lang="ts">
// Blocks navigation — use for primary content
const { data: post } = await useNodeByUri({ uri: route.path })

// Doesn't block navigation — use for secondary content
const { data: sidebar, pending } = usePosts(
  { limit: 5 },
  { lazy: true }
)
</script>
```

This lets the page render immediately with the primary content while secondary content loads in the background.

### When to Use Each Mode

<table>
<thead>
  <tr>
    <th>
      Content
    </th>
    
    <th>
      Option
    </th>
    
    <th>
      Effect
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Main page content
    </td>
    
    <td>
      <code>
        await
      </code>
      
       (default)
    </td>
    
    <td>
      Blocks navigation, included in SSR HTML
    </td>
  </tr>
  
  <tr>
    <td>
      Sidebar, related posts
    </td>
    
    <td>
      <code>
        lazy: true
      </code>
    </td>
    
    <td>
      Page renders immediately, loads in background
    </td>
  </tr>
  
  <tr>
    <td>
      Interactive widgets
    </td>
    
    <td>
      <code>
        server: false
      </code>
    </td>
    
    <td>
      Skips SSR, fetches client-side only
    </td>
  </tr>
  
  <tr>
    <td>
      User-triggered content
    </td>
    
    <td>
      <code>
        immediate: false
      </code>
    </td>
    
    <td>
      No fetch until <code>
        execute()
      </code>
      
       is called
    </td>
  </tr>
</tbody>
</table>

See [Fetching Data](https://wpnuxt.com/guide/fetching-data) for full options documentation.

## Image Optimization

Images are typically the largest assets on a page. Key optimizations:

1. **Set explicit dimensions** to prevent Cumulative Layout Shift (CLS):```vue
<NuxtImg
  :src="imageUrl"
  :width="featuredImage.mediaDetails?.width"
  :height="featuredImage.mediaDetails?.height"
/>
```
2. **Lazy load below-the-fold images:**```vue
<NuxtImg :src="imageUrl" loading="lazy" />
```
3. **Use modern formats** with `<NuxtPicture>` for automatic WebP/AVIF:```vue
<NuxtPicture :src="imageUrl" format="avif,webp" />
```
4. **Use an image provider** (Cloudflare, Vercel, Imgix) for on-the-fly resizing and optimization.

See [Images](https://wpnuxt.com/guide/images) for the full image handling guide.

## Route Rules for Per-Route Caching

Configure different caching strategies per route:

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  routeRules: {
    // Homepage: revalidate frequently
    '/': { swr: 60 },

    // Blog posts: cache longer, SWR for freshness
    '/blog/**': { swr: 3600 },

    // Static pages: cache aggressively
    '/about': { swr: 86400 },

    // API routes: short cache
    '/api/**': { cache: { maxAge: 60 } },
  },
})
```

<callout type="info">

Route rules are applied by Nitro at the edge/server level and work independently from WPNuxt's GraphQL caching. Both layers complement each other.

</callout>

## Query Optimization

### Use Fragments to Limit Fields

Default fragments include common fields. For listings where you only need titles and URIs, create a lighter fragment:

```graphql
# extend/queries/fragments/PostListItem.gql
fragment PostListItem on Post {
  id
  title
  uri
  date
  excerpt
  featuredImage {
    node {
      sourceUrl
      altText
    }
  }
}
```

### Set Appropriate Limits

Always set a `first` value in list queries to avoid fetching more data than needed:

```ts
// Fetch only what you need
const { data: posts } = await usePosts({ limit: 10 })

// Don't fetch 100 posts if you show 5
const { data: recent } = await usePosts({ limit: 5 })
```

### Avoid Redundant Fetches

Use `clientCache: true` (the default) to prevent re-fetching the same data during client-side navigation. See [Caching](https://wpnuxt.com/guide/caching) for cache configuration details.

## Lighthouse Tips

### Largest Contentful Paint (LCP)

- Use SSR (default) so content is in the initial HTML
- Avoid `lazy: true` for above-the-fold content
- Preload hero images with `<NuxtImg preload />`
- Use an image CDN for optimized delivery

### Cumulative Layout Shift (CLS)

- Always provide `width` and `height` for images
- Use `placeholder` on `<NuxtImg>` for loading states
- Avoid content that loads and shifts the layout (use `lazy: true` only for below-the-fold sections)

### Total Blocking Time (TBT)

- Minimize client-side JavaScript with `server: true` (default)
- Use `lazy: true` to spread hydration work across time
- Avoid large client-side data transformations — do them server-side in the GraphQL query or in a `transform` option

## Quick Wins Checklist

- [ ] Enable server-side caching (`wpNuxt.cache.enabled: true` — on by default)
- [ ] Use `lazy: true` for sidebar and secondary content
- [ ] Set explicit image dimensions to prevent CLS
- [ ] Use `loading="lazy"` on below-the-fold images
- [ ] Limit query results with `first`/`limit` parameters
- [ ] Use an image provider for production (Cloudflare, Vercel, etc.)
- [ ] Configure route rules for per-page caching strategies


## Sitemap

See the full [sitemap](https://wpnuxt.com/sitemap.md) for all pages.
