Skip to content

MPA Mode

Learn about Multi-Page Application (MPA) mode in VitePress and when to use it.

What is MPA Mode?

Traditional SPA vs MPA

VitePress by default generates a Single Page Application (SPA), but also supports Multi-Page Application (MPA) mode:

SPA Mode (Default):

  • Client-side routing
  • Faster navigation after initial load
  • Shared JavaScript context
  • Better for interactive applications

MPA Mode:

  • Server-side routing
  • Each page is a separate HTML file
  • No shared JavaScript context
  • Better for content-heavy sites
  • Improved SEO and performance

When to Use MPA Mode

Consider MPA mode when:

  • You have a large content-heavy site
  • SEO is critical
  • You want faster initial page loads
  • You don't need client-side interactivity
  • You want better performance on low-end devices

Enabling MPA Mode

Configuration

Enable MPA mode in your config:

javascript
// .vitepress/config.js
export default {
  mpa: true,
  
  // Other configuration options
  title: 'My MPA Site',
  description: 'A multi-page application built with VitePress'
}

Build Output

In MPA mode, VitePress generates:

dist/
├── index.html
├── guide/
│   ├── index.html
│   ├── getting-started.html
│   └── configuration.html
├── api/
│   └── reference.html
└── assets/
    ├── style.css
    └── app.js

Differences from SPA Mode

In MPA mode, navigation triggers full page reloads:

javascript
// SPA mode - client-side routing
router.push('/guide/getting-started')

// MPA mode - full page navigation
window.location.href = '/guide/getting-started.html'

JavaScript Context

Each page has its own JavaScript context:

javascript
// In SPA mode, this persists across pages
let globalState = { count: 0 }

// In MPA mode, this resets on each page load
let pageState = { count: 0 }

Vue Components

Components work differently in MPA mode:

vue
<!-- This component is re-initialized on each page -->
<script setup>
import { ref, onMounted } from 'vue'

const count = ref(0)

// This runs on every page load in MPA mode
onMounted(() => {
  console.log('Component mounted')
})
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="count++">Increment</button>
  </div>
</template>

Performance Considerations

Initial Load Performance

MPA mode typically has better initial load performance:

javascript
// .vitepress/config.js
export default {
  mpa: true,
  
  // Optimize for MPA performance
  vite: {
    build: {
      rollupOptions: {
        output: {
          manualChunks: undefined // Disable code splitting
        }
      }
    }
  }
}

Caching Strategy

Implement proper caching for MPA sites:

javascript
// .vitepress/config.js
export default {
  mpa: true,
  
  vite: {
    build: {
      rollupOptions: {
        output: {
          // Generate consistent file names for better caching
          entryFileNames: 'assets/[name].js',
          chunkFileNames: 'assets/[name].js',
          assetFileNames: 'assets/[name].[ext]'
        }
      }
    }
  }
}

SEO Optimization

Meta Tags

Each page gets its own meta tags:

markdown
---
title: Getting Started Guide
description: Learn how to get started with our platform
head:
  - - meta
    - name: keywords
      content: getting started, tutorial, guide
  - - meta
    - property: og:title
      content: Getting Started Guide
  - - meta
    - property: og:description
      content: Learn how to get started with our platform
---

# Getting Started

Your content here...

Structured Data

Add structured data to individual pages:

markdown
---
head:
  - - script
    - type: application/ld+json
    - |
      {
        "@context": "https://schema.org",
        "@type": "Article",
        "headline": "Getting Started Guide",
        "author": {
          "@type": "Person",
          "name": "John Doe"
        },
        "datePublished": "2024-01-15"
      }
---

Sitemap Generation

Generate comprehensive sitemaps:

javascript
// .vitepress/config.js
export default {
  mpa: true,
  
  sitemap: {
    hostname: 'https://example.com',
    transformItems: (items) => {
      // Add additional metadata for MPA pages
      return items.map(item => ({
        ...item,
        changefreq: 'weekly',
        priority: item.url === '/' ? 1.0 : 0.8
      }))
    }
  }
}

Limitations and Trade-offs

No Client-side Routing

Navigation is slower due to full page reloads:

javascript
// This won't work in MPA mode
import { useRouter } from 'vitepress'

const router = useRouter()
router.push('/new-page') // Not available in MPA mode

No Shared State

State doesn't persist between pages:

javascript
// This pattern doesn't work in MPA mode
import { reactive } from 'vue'

// Global state is lost on page navigation
const globalStore = reactive({
  user: null,
  preferences: {}
})

Limited Interactivity

Complex interactive features are more challenging:

vue
<!-- This component resets on every page load -->
<script setup>
import { ref } from 'vue'

// This state is lost when navigating to another page
const formData = ref({
  name: '',
  email: ''
})
</script>

Hybrid Approaches

Progressive Enhancement

Start with MPA and add interactivity:

javascript
// .vitepress/config.js
export default {
  mpa: true,
  
  vite: {
    define: {
      __MPA_MODE__: true
    }
  }
}
vue
<script setup>
import { ref, onMounted } from 'vue'

const enhanced = ref(false)

onMounted(() => {
  // Add progressive enhancement
  if (typeof window !== 'undefined') {
    enhanced.value = true
  }
})
</script>

<template>
  <div>
    <!-- Basic functionality for MPA -->
    <form v-if="!enhanced" action="/submit" method="post">
      <input name="email" type="email" required>
      <button type="submit">Submit</button>
    </form>
    
    <!-- Enhanced functionality when JavaScript loads -->
    <form v-else @submit.prevent="handleSubmit">
      <input v-model="email" type="email" required>
      <button type="submit">Submit</button>
    </form>
  </div>
</template>

Selective SPA Islands

Use SPA mode for specific sections:

javascript
// .vitepress/config.js
export default {
  mpa: true,
  
  // Define SPA-like behavior for specific routes
  vite: {
    define: {
      __SPA_ROUTES__: JSON.stringify(['/dashboard', '/app'])
    }
  }
}

Migration Strategies

From SPA to MPA

When migrating from SPA to MPA:

  1. Audit Dependencies: Remove client-side routing dependencies
  2. Update Navigation: Change internal links to use full URLs
  3. Refactor State Management: Move to page-level state
  4. Update Components: Make components stateless where possible

From MPA to SPA

When migrating from MPA to SPA:

  1. Add Client-side Routing: Implement Vue Router
  2. Implement State Management: Add global state management
  3. Update Navigation: Use programmatic navigation
  4. Optimize Bundle Splitting: Implement code splitting

Best Practices

Performance

  • Minimize JavaScript bundle size
  • Optimize images and assets
  • Implement proper caching headers
  • Use CDN for static assets

SEO

  • Ensure each page has unique meta tags
  • Implement structured data
  • Generate comprehensive sitemaps
  • Optimize for Core Web Vitals

User Experience

  • Provide loading indicators for forms
  • Implement proper error handling
  • Ensure accessibility compliance
  • Test on various devices and connections

Development

  • Use TypeScript for better development experience
  • Implement proper testing strategies
  • Monitor performance metrics
  • Document MPA-specific patterns

VitePress Development Guide