Skip to main content
Frameworks

Nuxt on Easel

Deploy Nuxt applications to Easel with SSR, streaming, API routes, hybrid rendering, and Nitro route rules.

Easel builds Nuxt with its standard Nitro server engine and deploys the output with no adapter. Public assets and prerendered pages go to the CDN, server rendering and Nitro handlers run in Functions, and route rules choose rendering and caching per path. For how the platform fits together, see How Easel works.

At a glance

Support levelProduction-ready
RenderingStatic, client-rendered, SSR, streaming, and hybrid
Server runtimeNode.js
Server engineNitro
Adapter requiredNo
Automatic detectionYes
Most recently tested withNuxt 3.15.x

Deploy a Nuxt application

Easel detects Nuxt projects automatically. You can deploy an existing application without changing its Nuxt configuration.

Create a project and connect the Git repository, or deploy with the CLI:

easel deploy

Easel installs dependencies with the detected package manager, runs nuxt build, and provisions what the Nitro output needs. You can override the install command, build command, root directory, and Node.js version in build configuration.

Don't set a Nitro preset

Easel selects the compatible Nitro output when it runs the build. Setting nitro.preset, NITRO_PRESET, or SERVER_PRESET yourself can produce output for another platform. Other Nitro options, including route rules, plugins, prerender settings, storage, and server assets, work as usual.

What Easel deploys

During the production build, Nitro turns the application into a server entry, route metadata, prerendered pages, and public assets.

Nuxt outputEasel resource
Files in public/CDN assets
Nuxt client bundlesCDN assets
Prerendered pagesCDN assets
Server-rendered pagesEasel Functions
Routes in server/api/Easel Functions
Routes in server/routes/Easel Functions
Server middlewareApplication function
Nitro pluginsApplication function
isr and swr routesCDN, regenerated by Functions
Redirects and headersRequest-routing configuration

Supported features

FeatureSupportNotes
Nuxt pages and layoutsSupportedIncludes file-based routing, nested layouts, and error pages
Server-side renderingSupportedDynamic pages run in Easel Functions
Client-side renderingSupportedIncludes applications configured with ssr: false
Static generationSupportedGenerated output is deployed to the CDN
Hybrid renderingSupportedControlled through route rules
Streaming responsesSupportedNitro and h3 response streams are forwarded incrementally
Server API routesSupportedIncludes handlers in server/api/
Server routesSupportedIncludes handlers in server/routes/
Server middlewareSupportedRuns inside the application function
Nitro pluginsSupportedRun when a Function instance starts
PrerenderingSupportedIncludes link crawling and explicit prerender routes
Route redirects and headersSupportedIncludes rules configured through route rules
ISR and SWR route rulesSupportedCached on the CDN and regenerated after expiration
Cached handlersPartialNitro's cache storage is per instance unless you mount an external driver
Runtime configurationSupportedServer and public runtime configuration are available
Cookies and sessionsSupportedStandard Nitro and h3 cookie APIs are available
Server-Sent EventsSupportedSubject to function limits
@nuxt/imagePartialStatic assets work. Dynamic provider compatibility depends on the provider
Nitro storagePartialMemory and external drivers work. Local persistent storage doesn't
Scheduled tasksNot supportedCall an authenticated route from an external scheduler
WebSocketsNot supportedEasel Functions don't accept long-lived WebSocket upgrades

Rendering modes

Nuxt renders pages on the server by default. Routes that need request-time execution run through the Nitro server entry in a Function, and response streams are forwarded as Nitro produces them.

pages/products.vue
<script setup lang="ts">
const { data: products } = await useFetch("/api/products");
</script>

<template>
  <ul>
    <li v-for="product in products" :key="product.id">
      {{ product.name }}
    </li>
  </ul>
</template>

Set ssr: false in nuxt.config.ts for a client-rendered application. Easel deploys the generated assets to the CDN and falls back to the entry page for application routes. Server routes in the Nitro build still deploy as Functions.

nuxt generate prerenders the whole application into .output/public and Easel deploys it straight to the CDN. A generated deployment has no server rendering or server routes. Use nuxt build when you need them.

Route rules

Route rules choose rendering, caching, redirects, and headers per path:

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    "/": { prerender: true },
    "/blog/**": { swr: 3600 },
    "/products/**": { isr: 600 },
    "/admin/**": { ssr: false },
    "/old-page": {
      redirect: { to: "/new-page", statusCode: 308 },
    },
    "/assets/**": {
      headers: {
        "cache-control": "public, max-age=31536000, immutable",
      },
    },
  },
});
  • prerender renders the route during the build and deploys it to the CDN.
  • ssr: false renders the route in the browser.
  • isr and swr deploy the route as incrementally regenerated output. Easel caches it on the CDN, serves the cached copy, and regenerates it in a Function after the interval. See revalidation.
  • redirect and headers become request-routing configuration.

Test combinations of route rules, and experimental Nitro options, in a preview deployment.

Server routes and middleware

Handlers under server/api and server/routes are bundled into the Nitro application and run in Functions:

server/api/products/[id].get.ts
export default defineEventHandler(async (event) => {
  const id = getRouterParam(event, "id");
  const product = await getProduct(id);

  setResponseHeader(event, "cache-control", "public, s-maxage=60");

  return product;
});

Server middleware in server/middleware runs inside the application Function before the matching route or page render. Logic that should run before a Function is invoked, such as blocking traffic, belongs in the Firewall instead.

Nitro plugins

Nitro plugins run each time Easel starts a new Function instance, not once per deployment. Connections and in-memory state a plugin creates belong to that instance and aren't shared.

server/plugins/timing.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook("request", (event) => {
    event.context.startedAt = performance.now();
  });
});

Runtime configuration

Define private and public keys in nuxt.config.ts, then set their values with NUXT_-prefixed environment variables:

nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    databaseUrl: "", // NUXT_DATABASE_URL
    public: {
      apiOrigin: "", // NUXT_PUBLIC_API_ORIGIN
    },
  },
});

Private keys are available only to server code through useRuntimeConfig(). Keys under public are exposed to the browser, so don't put secrets there. See environment variables for scopes and how changes apply.

Prerendering

Configure Nitro's prerenderer in nuxt.config.ts:

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    prerender: {
      routes: ["/", "/about", "/pricing"],
      crawlLinks: true,
    },
  },
});

Prerendered routes deploy as CDN assets. The crawler only finds routes linked from another prerendered page, so list other dynamic routes explicitly or push them from the nitro:config hook. Data sources used at build time must be reachable from Easel's build environment.

Caching and storage

defineCachedEventHandler, defineCachedFunction, and cache route rules without swr use Nitro's cache storage. By default that's memory in one Function instance, so entries aren't shared and disappear when the instance stops. The same applies to useStorage() with the memory or filesystem driver.

Mount an external driver for anything that must be shared or durable:

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    storage: {
      cache: {
        driver: "redis",
        url: process.env.REDIS_URL,
      },
    },
  },
});

For responses the CDN can cache, prefer isr or swr route rules, or set Cache-Control as described in CDN caching. Reading files bundled with serverAssets through useStorage("assets:server") works.

Images

Files in public/ deploy to the CDN. @nuxt/image compatibility depends on the provider. Providers that return external image URLs work. Providers that need a local image server or provider-specific runtime behavior need testing in a preview deployment.

Scheduled tasks

Nitro scheduled tasks aren't registered as Easel schedules. Expose the work as an authenticated server route and call it from an external scheduler:

server/api/internal/cleanup.post.ts
export default defineEventHandler(async (event) => {
  const authorization = getRequestHeader(event, "authorization");

  if (authorization !== `Bearer ${process.env.CRON_SECRET}`) {
    throw createError({ statusCode: 401 });
  }

  await removeExpiredRecords();

  return { completed: true };
});

Make the handler idempotent.

Project configuration

SettingDefault
Install commandDetected from the package manager
Build commandPackage script or nuxt build
Output directoryDetected from Nitro output
Development commandPackage script or nuxt dev
Node.js versionProject or platform default

In a monorepo, set the root directory to the folder containing the Nuxt application's package.json and nuxt.config. See monorepos.

Keep using nuxt dev locally, and use a preview deployment to check production Nitro output, route rules, and caching.

Known limitations

  • WebSockets. Nitro WebSocket handlers don't work on Functions. See Functions runtime.
  • Provider-specific Nitro features. Features built for another Nitro deployment provider may behave differently on Easel.
  • Experimental features. Experimental Nuxt and Nitro features can change their deployment output. Validate them in a preview first.

Troubleshooting

Easel can't find the server entry

Check that the build runs nuxt build and that no custom command moves or replaces .output. Remove NITRO_PRESET, SERVER_PRESET, or nitro.preset.

The project deploys without server rendering

Check that the build command is nuxt build, not nuxt generate, and that ssr: false isn't set globally or for the route.

A route rule doesn't apply

Check that the pattern matches the deployed URL and that a more specific rule doesn't override it. Inline route rules need the experimental option your Nuxt version requires. Otherwise, define the rule in nuxt.config.

A runtime config value is empty

Use the NUXT_-prefixed name that matches the key, such as NUXT_PUBLIC_API_ORIGIN for runtimeConfig.public.apiOrigin.

A cached handler returns different results per request

Nitro's default cache storage is per instance. Mount a shared driver for cache, or use an isr or swr route rule.

Compatibility policy

Easel tests this guide against the version in At a glance. Test new Nuxt and Nitro releases, modules, and image providers in a preview deployment first. See the compatibility policy.

Next steps