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 |
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/h3"
lazyEventHandler→defineLazyEventHandleruseBase→withBase
Error Utils
-- import { createError, isError } from "nitro/h3"
++ import { HTTPError } from "nitro/h3"
++ 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.