Migration Guide
Nitro v3 introduces intentional backward-incompatible changes. This guide helps you migrate from Nitro v2.
nitropack is renamed to nitro
The NPM package nitropack (v2) has been renamed to nitro (v3).
Migration: Update the nitropack dependency to nitro in package.json:
{
"dependencies": {
-- "nitropack": "latest"
++ "nitro": "latest"
}
}
{
"dependencies": {
-- "nitropack": "latest"
++ "nitro": "npm:nitro-nightly"
}
}
Migration: Search your codebase and rename all instances of nitropack to nitro:
-- import { defineNitroConfig } from "nitropack/config"
++ import { defineNitroConfig } from "nitro/config"
nitro/runtime
Runtime utils had been moved to individual nitro/* subpath exports. Refer to docs for usage.
-- import { useStorage } from "nitropack/runtime/storage"
++ import { useStorage } from "nitro/storage"
Minimum Supported Node.js Version: 20
Nitro now requires a minimum Node.js version of 20, as Node.js 18 reaches end-of-life in April 2025.
Please upgrade to the latest LTS version (>= 20).
Migration:
- Check your local Node.js version using
node --versionand update if necessary. - If you use a CI/CD system for deployment, ensure that your pipeline is running Node.js 20 or higher.
- If your hosting provider manages the Node.js runtime, make sure it's set to version 20, 22, or later.
Type Imports
Nitro types are now only exported from nitro/types.
Migration: Import types from nitro/types instead of nitro:
-- import { NitroRuntimeConfig } from "nitropack"
++ import { NitroRuntimeConfig } from "nitro/types"
App Config Support Removed
Nitro v2 supported a bundled app config that allowed defining configurations in app.config.ts and accessing them at runtime via useAppConfig().
This feature had been removed.
Migration:
Use a regular .ts file in your server directory and import it directly.
Preset updates
Nitro presets have been updated for the latest compatibility.
Some (legacy) presets have been removed or renamed.
| Old Preset | New Preset |
|---|---|
node | node_middleware (export changed to middleware) |
cloudflare, cloudflare_worker, cloudflare_module_legacy | cloudflare_module |
deno-server-legacy | deno_server with Deno v2 |
netlify-builder | netlify or netlify_edge |
vercel-edge | vercel with Fluid compute enabled |
azure, azure_functions | azure_swa |
firebase | firebase_app_hosting |
iis | iis_handler |
deno | deno_deploy |
edgio | Discontinued |
cli | Removed due to lack of use |
service_worker | Removed due to instability |
Cloudflare Bindings Access
In Nitro v2, Cloudflare environment variables and bindings were accessible via event.context.cloudflare.env.
In Nitro v3, the Cloudflare runtime context is attached to the request's runtime object instead.
Migration:
-- const { cloudflare } = event.context
-- const binding = cloudflare.env.MY_BINDING
++ const { env } = event.req.runtime.cloudflare
++ const binding = env.MY_BINDING
Changed nitro subpath imports
Nitro v2 introduced multiple subpath exports, some of which have been removed or updated:
nitro/rollup,nitropack/core(usenitro/builder)nitropack/runtime/*(usenitro/*)nitropack/kit(removed)nitropack/presets(removed)
An experimental nitropack/kit was introduced but has now been removed. A standalone Nitro Kit package may be introduced in the future with clearer objectives.
Migration:
- Use
NitroModulefromnitro/typesinstead ofdefineNitroModulefrom the kit. - Prefer built-in Nitro presets (external presets are only for evaluation purposes).
H3 v2
Nitro v3 upgrades to H3 v2, which includes API changes. All H3 utilities are imported from nitro/h3.
Web Standards
H3 v2 is rewritten based on web standard primitives (URL, Headers, Request, and Response).
Access to event.node.{req,res} is only available in Node.js runtime. event.web is renamed to event.req (instance of web Request).
Response Handling
You should always explicitly return the response body or throw an error:
-- import { send, sendRedirect, sendStream } from "nitro/h3"
-- send(event, value)
-- sendStream(event, stream)
-- sendRedirect(event, location, code)
++ import { redirect } from "nitro/h3"
++ return value
++ return stream
++ return redirect(event, location, code)
Other changes:
sendError(event, error)→throw createError(error)sendNoContent(event)→return noContent(event)sendProxy(event, target)→return proxy(event, target)
Request Body
Most body utilities can be replaced with native event.req methods:
-- import { readBody, readRawBody, readFormData } from "nitro/h3"
++ // Use native Request methods
++ const json = await event.req.json()
++ const text = await event.req.text()
++ const formData = await event.req.formData()
++ const stream = event.req.body
Headers
H3 now uses standard web Headers. Header values are always plain string (no null, undefined, or string[]).
-- import { getHeader, setHeader, getResponseStatus } from "nitro/h3"
-- getHeader(event, "x-foo")
-- setHeader(event, "x-foo", "bar")
++ event.req.headers.get("x-foo")
++ event.res.headers.set("x-foo", "bar")
++ event.res.status // instead of getResponseStatus(event)
Handler Utils
-- import { eventHandler, defineEventHandler } from "nitro/h3"
++ import { defineHandler } from "nitro"
lazyEventHandler→defineLazyEventHandleruseBase→withBase
Error Utils
-- import { createError, isError } from "nitro/h3"
++ import { HTTPError } from "nitro"
++ throw new HTTPError({ status: 404, message: "Not found" })
++ HTTPError.isError(error)
Node.js Utils
-- import { defineNodeListener, fromNodeMiddleware, toNodeListener } from "nitro/h3"
++ import { defineNodeHandler, fromNodeHandler, toNodeHandler } from "nitro/h3"
Optional Hooks
If you were using useNitroApp().hooks outside of Nitro plugins before, it might be undefined. Use new useNitroHooks() to guarantee having an instance.