SSR & Data Fetching

Preview — 3 of 10 questions

What are the differences between SSR, SSG, and ISR rendering modes in Nuxt 3?

javascript
// nuxt.config.ts — hybrid rendering (per-route rules)
export default defineNuxtConfig({
  routeRules: {
    // SPA — no server rendering at all (client-side only)
    '/dashboard/**': { ssr: false },

    // SSR — rendered on every request (fresh data, slower)
    '/checkout': { ssr: true },

    // SSG (static) — pre-rendered at build time (fastest, stale)
    '/': { prerender: true },
    '/about': { prerender: true },

    // ISR — cached for N seconds, then regenerated in background
    '/blog/**': { isr: 60 },    // Refresh after 60 seconds
    '/products/**': { isr: 300 } // Refresh after 5 minutes
  }
})

// Per-page rendering (alternative to routeRules):
// pages/blog/[slug].vue
definePageMeta({
  // This page is pre-rendered at build time
})
AThey are different names for the same rendering approach
BSSR renders on each request; SSG pre-renders at build time; ISR re-generates pages in the background after a stale threshold
CSSR is client-side only; SSG is server-side only; ISR combines both
DSSG is only available in Nuxt 2; Nuxt 3 uses SSR and ISR exclusively

What is the difference between useFetch and the native fetch API in Nuxt?

javascript
<script setup>
// useFetch — SSR-aware, reactive, deduplicates server/client fetches
const { data, pending, error, refresh, execute } = await useFetch('/api/products', {
  // Request options
  method: 'GET',
  query: { category: 'electronics', page: 1 },
  headers: { Authorization: `Bearer ${token}` },

  // Nuxt-specific options
  key: 'products-electronics', // Cache key for deduplication
  lazy: false,                 // true = don't block navigation (show loading state)
  server: true,                // false = only fetch on client
  immediate: true,             // false = don't fetch until execute() is called

  // Transform the response
  transform: (data) => data.items,

  // Pick specific fields (reduces JSON payload)
  pick: ['id', 'name', 'price'],

  // Error handling
  onResponseError({ response }) {
    console.error('API error:', response.status)
  }
})

// Reactive query — refetches when query changes
const page = ref(1)
const { data: pageData } = await useFetch('/api/products', {
  query: { page } // Reactive! Refetches when page.value changes
})

// Manual refresh
async function loadNextPage() {
  page.value++
  // useFetch automatically refetches because page is reactive
}
</script>
AuseFetch is SSR-aware: it runs on the server during SSR and transfers the result to the client to avoid duplicate requests; it also provides reactive loading/error states
BuseFetch is exactly the same as fetch but with auto-import
CuseFetch only works on the client — use fetch for server-side data fetching
DuseFetch caches results indefinitely; fetch never caches

When should you use useAsyncData instead of useFetch?

javascript
<script setup>
// useFetch — simple URL fetching
const { data: product } = await useFetch(`/api/products/${id}`)

// useAsyncData — custom async logic
const { data: enrichedProduct } = await useAsyncData(
  `product-${id}-enriched`, // Cache key
  async () => {
    // Multiple API calls combined
    const [product, reviews, inventory] = await Promise.all([
      $fetch(`/api/products/${id}`),
      $fetch(`/api/reviews?productId=${id}`),
      $fetch(`/api/inventory/${id}`)
    ])

    // Custom transform
    return {
      ...product,
      reviews: reviews.items,
      inStock: inventory.quantity > 0,
      averageRating: reviews.items.reduce((sum, r) => sum + r.rating, 0) / reviews.items.length
    }
  },
  {
    // Options — same as useFetch
    watch: [() => id.value], // Re-fetch when id changes
    transform: (data) => markRaw(data) // Prevent deep reactivity if large
  }
)

// Using with a Pinia store (server action)
const { data: userData } = await useAsyncData('user', async () => {
  if (import.meta.server) {
    // Direct DB access on server — no HTTP needed
    const { $db } = useNuxtApp()
    return await $db.user.findUnique({ where: { id: event.context.userId } })
  }
  return $fetch('/api/user')
})
</script>
AuseAsyncData is deprecated — always use useFetch
BuseAsyncData is faster because it skips HTTP — use it for performance-critical fetching
CuseAsyncData is for client-side only; useFetch handles SSR
DUse useAsyncData when the data fetching logic is not a simple URL fetch — e.g., querying a database directly, calling multiple APIs, or using custom logic

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.