1 min read

Nuxt on Vercel

Learn how to use Vercel's features with Nuxt.
Table of Contents

Nuxt is an open-source framework that streamlines the process of creating modern Vue apps. It offers server-side rendering, SEO features, automatic code splitting, prerendering, and more out of the box. It also has an extensive catalog of community-built modules, which allow you to integrate popular tools with your projects.

You can deploy Nuxt static and server-side rendered sites on Vercel with no configuration required.

To get started with Nuxt on Vercel:

  • If you already have a project with Nuxt, install Vercel CLI and run the vercel command from your project's root directory
  • Clone one of our Nuxt example repos to your favorite git provider and deploy it on Vercel with the button below:

Vercel deployments can integrate with your git provider to generate preview URLs for each pull request you make to your Nuxt project.

The following table outlines the differences between nuxt build and nuxt generate on Vercel:

Featurenuxt buildnuxt generate
Default build commandYesNo
Supports all Vercel features out of the boxYesNo, must set nitro.static to true
Supports SSRYesNo
Supports SSGYes, with nuxt configYes
Supports ISRYesNo

In general, nuxt build is likely best for most use cases. Consider using nuxt generate to build fully static sites.

You can configure your Nuxt deployment by creating a Nuxt config file in your project's root directory. It can be a TypeScript, JavaScript, or MJS file, but the Nuxt team recommends using TypeScript. Using TypeScript will allow your editor to suggest the correct names for configuration options, which can help mitigate typos.

Your Nuxt config file should default export defineNuxtConfig by default, which you can add an options object to.

The following is an example of a Nuxt config file with no options defined:

nuxt.config.ts
export default defineNuxtConfig({
  // Config options here
});

See the Nuxt Configuration Reference docs for a list of available options.

With the routeRules config option, you can:

  • Create redirects
  • Modify a route's response headers
  • Enable ISR
  • Deploy specific routes statically
  • Deploy specific routes with SSR
  • and more

At the moment, there is no way to configure route deployment options within your page components, but development of this feature is in progress.

The following is an example of a Nuxt config that:

  • Creates a redirect
  • Modifies a route's response headers
  • Opts a set of routes into client-side rendering
nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/examples/*': { redirect: '/redirect-route' },
    '/modify-headers-route': { headers: { 'x-magic-of': 'nuxt and vercel' } },
    // Enables client-side rendering
    '/spa': { ssr: false },
  },
});

To learn more about routeRules:

Serverless Functions enable developers to write functions that uses resources that scale up and down based on traffic demands. This prevents them from failing during peak hours, but keeps them from running up high costs during periods of low activity.

Nuxt deploys routes defined in /server/api, /server/routes, and /server/middleware as one server-rendered Serverless Function by default. Nuxt Pages, APIs, and Middleware routes get bundled into a single Vercel Function.

The following is an example of a basic API Route in Nuxt:

server/api/hello.ts
export default defineEventHandler(() => 'Hello World!');

You can test your API Routes with nuxt dev.

Edge Functions are a fast, scalable solution for delivering dynamic content to users. By default, Edge Functions get deployed globally, and invoked in one of Vercel's Edge regions near your site's visitors.

To use Edge Functions with Nuxt SSR routes on Vercel, you can:

  • Create a NITRO_PRESET environment variable and set its value to vercel-edge
  • Or add nitro: { preset: 'vercel-edge' } to your nuxt.config.ts:
    nuxt.config.ts
    export default defineNuxtConfig({
      nitro: {
        preset: 'vercel-edge',
      },
    });
You cannot enable Edge Functions for individual routes.

The Nuxt team recommends using environment variables for deployments depending on CI/CD. You can set environment variables in your project's dashboard on Vercel under Settings ➞ Environment Variables. See Declaring Environment Variables to learn more.

You can specify which regions your Edge Functions should get invoked in with your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    preset: 'vercel-edge',
    vercel: {
      regions: ['iad1'],
    },
  },
});

See our runtime comparison table to understand whether Edge or Serverless is best for your use-case.

You can read and write server files with Nuxt on Vercel. One way to do this is by using Nitro with Vercel Functions and the Vercel KV driver. Use Nitro's server assets to include files in your project deployment. Assets within server/assets get included by default.

To access server assets, you can use Nitro's storage API:

server/api/storage.ts
export default defineEventHandler(async () => {
  // https://nitro.unjs.io/guide/assets#server-assets
  const assets = useStorage('assets:server');
  const users = await assets.getItem('users.json');
  return {
    users,
  };
});

To write files, mount KV storage with the Vercel KV driver:

Update your nuxt.config.ts file.

nuxt.config.ts
export default defineNuxtConfig({
  $production: {
    nitro: {
      storage: {
        data: { driver: 'vercelKV' },
      },
    },
  },
});

Use with the storage API.

server/api/storage.ts
export default defineEventHandler(async (event) => {
  const dataStorage = useStorage('data');
  await dataStorage.setItem('hello', 'world');
  return {
    hello: await dataStorage.getItem('hello'),
  };
});

See an example code repository.

Middleware is code that executes before a request gets processed. Because Middleware runs before the cache, it's an effective way of providing personalization to statically generated content.

Nuxt has two forms of Middleware:

In Nuxt, modules defined in /server/middleware will get deployed as server middleware. Server middleware should not have a return statement or send a response to the request.

Server middleware is best used to read data from or add data to a request's context. Doing so allows you to handle authentication or check a request's params, headers, url, and more.

The following example demonstrates Middleware that:

  • Checks for a cookie
  • Tries to fetch user data from a database based on the request
  • Adds the user's data and the cookie data to the request's context
server/middleware/auth.ts
import { getUserFromDBbyCookie } from 'some-orm-package';
 
export default defineEventHandler(async (event) => {
  // The getCookie method is available to all
  // Nuxt routes by default. No need to import.
  const token = getCookie(event, 'session_token');
 
  // getUserFromDBbyCookie is a placeholder
  // made up for this example. You can fetch
  // data from wherever you want here
  const { user } = await getUserFromDBbyCookie(event.request);
 
  if (user) {
    event.context.user = user;
    event.context.session_token = token;
  }
});

You could then access that data in a page on the frontend with the useRequestEvent hook. This hook is only available in routes deployed with SSR. If your page renders in the browser, useRequestEvent will return undefined.

The following example demonstrates a page fetching data with useRequestEvent:

example.vue
<script>
  const event = useRequestEvent();
  const user = ref(event.context?.user);
</script>
 
<template>
    <div v-if="user">
      <h1>Hello, {{ user.name }}!</h1>
    </div>
    <div v-else>
      <p>Authentication failed!</p>
    </div>
</template>

Nuxt's route middleware runs before navigating to a particular route. While server middleware runs in Nuxt's Nitro engine, route middleware runs in Vue.

Route middleware is best used when you want to do things that server middleware can't, such as redirecting users, or preventing them from navigating to a route.

The following example demonstrates route middleware that redirects users to a secret route:

middleware/redirect.ts
export default defineNuxtRouteMiddleware((to) => {
  console.log(
    `Heading to ${to.path} - but I think we should go somewhere else...`,
  );
 
  return navigateTo('/secret');
});

By default, route middleware code will only run on pages that specify them. To do so, within the <script> tag for a page, you must call the definePageMeta method, passing an object with middleware: 'middleware-filename' set as an option.

The following example demonstrates a page that runs the above redirect middleware:

redirect.vue
<script>
definePageMeta({
  middleware: 'redirect'
})
</script>
 
<template>
  <div>
    You should never see this page
  </div>
</template>

To make a middleware global, add the .global suffix before the file extension. The following is an example of a basic global middleware file:

example-middleware.global.ts
export default defineNuxtRouteMiddleware(() => {
  console.log('running global middleware');
});

See a detailed example of route middleware in Nuxt's Middleware example docs.

Middleware with Nuxt on Vercel enables you to:

  • Redirect users, and prevent navigation to routes
  • Run authentication checks on the server, and pass results to the frontend
  • Scope middleware to specific routes, or run it on all routes

Learn more about Middleware

Server-Side Rendering (SSR) allows you to render pages dynamically on the server. This is useful for pages where the rendered data needs to be unique on every request. For example, checking authentication or looking at the location of an incoming request.

Nuxt allows you to deploy your projects with a strategy called Universal Rendering. In concrete terms, this allows you to deploy your routes with SSR by default and opt specific routes out in your Nuxt config.

When you deploy your app with Universal Rendering, it renders on the server once, then your client-side JavaScript code gets interpreted in the browser again once the page loads.

On Vercel, Nuxt apps are server-rendered by default

SSR with Nuxt on Vercel:

  • Scales to zero when not in use
  • Scales automatically with traffic increases
  • Allows you to opt individual routes out of SSR with your Nuxt config

Learn more about SSR

If you deploy with nuxt build, you can opt nuxt routes into client-side rendering using routeRules by setting ssr: false as demonstrated below:

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // Use client-side rendering for this route
    '/client-side-route-example': { ssr: false },
  },
});

To deploy a fully static site on Vercel, build your project with nuxt generate. You should set nitro.static to true to enable Vercel features such as redirects, Image Optimization, and more when using nuxt generate:

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    static: true,
  },
});

If you deploy with nuxt build, You can statically generate Nuxt routes at build time using the prerender option in your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // prerender index route by default
    '/': { prerender: true },
    // prerender this route and all child routes
    '/prerender-multiple/**': { prerender: true },
  },
});

To verify that a route is prerendered at build time, check window.NUXT.prerenderedAt. You can also use the Vue Telescope browser extension .

Incremental Static Regeneration (ISR) allows you to create or update content without redeploying your site. ISR has two main benefits for developers: better performance and faster build times.

To enable ISR in a Nuxt route, add a routeRules option to your nuxt.config.ts, as shown in the example below:

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    // all routes (by default) will be revalidated every 60 seconds, in the background
    '/**': { isr: 60 },
    // this page will be generated on demand and then cached permanently
    '/static': { isr: true },
    // this page is statically generated at build time and cached permanently
    '/prerendered': { prerender: true },
    // this page will be always fresh
    '/dynamic': { isr: false },
  },
});

You should use the isr option rather than swr to enable ISR in a route. The isr option enables Nuxt to use Vercel's Edge Cache.

using ISR with Nuxt on Vercel offers:

  • Better performance with our global Edge Network
  • Zero-downtime rollouts to previously statically generated pages
  • Global content updates in 300ms
  • Generated pages are both cached and persisted to durable storage

Learn more about ISR with Nuxt.

You can define redirects and response headers with Nuxt on Vercel in your nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/examples/*': { redirect: '/redirect-route' },
    '/modify-headers-route': { headers: { 'x-magic-of': 'nuxt and vercel' } },
  },
});

Image Optimization helps you achieve faster page loads by reducing the size of images and using modern image formats.

When deploying to Vercel, images are automatically optimized on demand, keeping your build times fast while improving your page load performance and Core Web Vitals.

To use Image Optimization with Nuxt on Vercel, follow the Image Optimization quickstart by selecting Nuxt from the dropdown.

Using Image Optimization with Nuxt on Vercel:

  • Requires zero-configuration for Image Optimization when using nuxt/image
  • Helps your team ensure great performance by default
  • Keeps your builds fast by optimizing images on-demand

Learn more about Image Optimization

Dynamic social card images allow you to create a unique image for pages of your site. This is great for sharing links on the web through social platforms or text messages.

To generate dynamic social card images for Nuxt projects, you can use nuxt-og-image. It uses the main Nuxt/Nitro Server-side rendering(SSR) function.

The following example demonstrates using Open Graph (OG) image generation with nuxt-og-image:

  1. Create a new OG template
components/OgImage/Template.vue
<script setup lang="ts">
  withDefaults(defineProps<{
    title?: string
  }>(), {
    title: 'title',
  })
</script>
<template>
  <div class="h-full w-full flex items-start justify-start border-solid border-blue-500 border-[12px] bg-gray-50">
    <div class="flex items-start justify-start h-full">
      <div class="flex flex-col justify-between w-full h-full">
        <h1 class="text-[80px] p-20 font-black text-left">
          {{ title }}
        </h1>
        <p class="text-2xl pb-10 px-20 font-bold mb-0">
          acme.com
        </p>
      </div>
    </div>
  </div>
</template>
  1. Use that OG image in your pages. Props passed get used in your open graph images.
pages/index.vue
<script lang="ts" setup>
defineOgImageComponent('Template', {
  title: 'Is this thing on?'
})
</script>

To see your generated image, run your project and use Nuxt DevTools. Or you can visit the image at its URL /__og-image__/image/og.png.

Learn more about OG Image Generation with Nuxt.

The Nuxt team does not recommend deploying legacy versions of Nuxt (such as Nuxt 2) on Vercel, except as static sites. If your project uses a legacy version of Nuxt, you should either:

If you still want to use legacy Nuxt versions with Vercel, you should only do so by building a static site with nuxt generate. We do not recommend deploying legacy Nuxt projects with server-side rendering.

See our Frameworks documentation page to learn about the benefits available to all frameworks when you deploy on Vercel.

Learn more about deploying Nuxt projects on Vercel with the following resources:

Last updated on March 2, 2023