Files
stack/v1/packages/mosaic/framework/skills/nuxt/references/rendering-modes.md
T

4.7 KiB

name, description
name description
rendering-modes Universal rendering, client-side rendering, and hybrid rendering in Nuxt

Rendering Modes

Nuxt supports multiple rendering modes: universal (SSR), client-side (CSR), and hybrid rendering.

Universal Rendering (Default)

Server renders HTML, then hydrates on client:

// nuxt.config.ts - this is the default
export default defineNuxtConfig({
  ssr: true,
});

Benefits:

  • Fast initial page load (HTML is ready)
  • SEO-friendly (content is in HTML)
  • Works without JavaScript initially

How it works:

  1. Server executes Vue code, generates HTML
  2. Browser displays HTML immediately
  3. JavaScript loads and hydrates the page
  4. Page becomes fully interactive

Client-Side Rendering

Render entirely in the browser:

// nuxt.config.ts
export default defineNuxtConfig({
  ssr: false,
});

Benefits:

  • Simpler development (no SSR constraints)
  • Cheaper hosting (static files only)
  • Works offline

Use cases:

  • Admin dashboards
  • SaaS applications
  • Apps behind authentication

SPA Loading Template

Provide loading UI while app hydrates:

<!-- app/spa-loading-template.html -->
<div class="loading">
  <div class="spinner"></div>
  <p>Loading...</p>
</div>

<style>
  .loading {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100vh;
  }
  .spinner {
    width: 40px;
    height: 40px;
    border: 4px solid #f3f3f3;
    border-top: 4px solid #00dc82;
    border-radius: 50%;
    animation: spin 1s linear infinite;
  }
  @keyframes spin {
    to {
      transform: rotate(360deg);
    }
  }
</style>

Hybrid Rendering

Mix rendering modes per route using route rules:

// nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // Static pages - prerendered at build
    '/': { prerender: true },
    '/about': { prerender: true },

    // ISR - regenerate in background
    '/blog/**': { isr: 3600 }, // Cache for 1 hour
    '/products/**': { swr: true }, // Stale-while-revalidate

    // Client-only rendering
    '/admin/**': { ssr: false },
    '/dashboard/**': { ssr: false },

    // Server-rendered (default)
    '/api/**': { cors: true },
  },
});

Route Rules Reference

Rule Description
prerender: true Pre-render at build time
ssr: false Client-side only
swr: number | true Stale-while-revalidate caching
isr: number | true Incremental static regeneration
cache: { maxAge: number } Cache with TTL
redirect: string Redirect to another path
cors: true Add CORS headers
headers: object Custom response headers

Inline Route Rules

Define per-page:

<script setup lang="ts">
defineRouteRules({
  prerender: true,
});
</script>

Prerendering

Generate static HTML at build time:

// nuxt.config.ts
export default defineNuxtConfig({
  // Prerender specific routes
  routeRules: {
    '/': { prerender: true },
    '/about': { prerender: true },
    '/posts/*': { prerender: true },
  },
});

Or use nuxt generate:

nuxt generate

Programmatic Prerendering

// nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'prerender:routes'({ routes }) {
      // Add dynamic routes
      const posts = await fetchPostSlugs();
      for (const slug of posts) {
        routes.add(`/posts/${slug}`);
      }
    },
  },
});

Or in pages:

// server/api/posts.ts or a plugin
prerenderRoutes(['/posts/1', '/posts/2', '/posts/3']);

Edge-Side Rendering

Render at CDN edge servers:

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'cloudflare-pages', // or 'vercel-edge', 'netlify-edge'
  },
});

Supported platforms:

  • Cloudflare Pages/Workers
  • Vercel Edge Functions
  • Netlify Edge Functions

Conditional Rendering

Use import.meta.server and import.meta.client:

<script setup>
if (import.meta.server) {
  // Server-only code
  console.log('Running on server');
}

if (import.meta.client) {
  // Client-only code
  console.log('Running in browser');
}
</script>

For components:

<template>
  <ClientOnly>
    <BrowserOnlyComponent />
    <template #fallback>
      <p>Loading...</p>
    </template>
  </ClientOnly>
</template>