# The Full-Stack Vue Framework > Build fast, production-ready web apps with Vue. File-based routing, auto-imports, and server-side rendering, all configured out of the box. ## When to use this Use these docs when you are writing, reviewing or debugging code for a Nuxt application: scaffolding a project, configuring `nuxt.config.ts`, picking a rendering mode per route, writing pages, layouts, components, composables, server routes, middleware and plugins, fetching data with `useFetch` and `useAsyncData`, adding or authoring a module, upgrading between major versions, and deploying to a specific provider. They cover Nuxt itself. For Vue, Vite, Nitro or UnJS APIs, go to their own documentation, and for questions about a specific module, to the module's own docs, which the directory links. ## How to fetch these docs Every documentation, blog and deploy page is also Markdown. Append `.md` to a URL, or send `Accept: text/markdown` on the HTML one. is the whole documentation in a single file, and lists every page. For search and structured lookups, call the MCP server at (streamable HTTP), described by . The REST endpoints under `/api/v1` are public, read-only and need no credentials: the module directory, its health data, and the core and ecosystem teams. describes them. ## Features - **Zero Configuration**: Start coding with Vue or Typescript immediately — Nuxt handles all the setup for you. - **Rendering Modes**: Server-side rendering, client-side rendering, static-site generation, you decide, up to the page level. - **Routing & Layouts**: Use our file-based routing system to build complex url-based views while reusing components for performance. - **Data Fetching**: Make your Vue component async and await your data. Nuxt provides powerful composables for universal data fetching. - **Error Handling**: Catch errors in your application with our built-in handlers and custom error pages. - **Transitions**: Implement smooth transitions between layouts, pages, and components. - **Assets & Style**: Benefit from automatic image, font, and script optimizations with built-in support. - **SEO & Meta Tags**: Create production-ready applications that are fully indexable by search engines. - **Modular**: Extend Nuxt features with 200+ modules to ship your application faster. - **Middleware**: Protect or add custom logic (localization, A/B testing) before rendering pages. - **Type-safe with TypeScript**: Write type-safe code with automatically generated types and tsconfig.json. ## Getting started ```bash npx nuxi@latest init cd npm install npm run dev ``` - Introduction: - Installation: - Configuration: ## Explore - Documentation: - Modules: - Templates: - Showcase: - Deploy: - Blog: - Changelog: - Team: ## Community - GitHub: - Discord: - X (Twitter): - Bluesky: ## Resources for Agents - [API catalog: every service document this site publishes](https://nuxt.com/.well-known/api-catalog) - [Sitemap (XML)](https://nuxt.com/sitemap.xml) - [Sitemap (Markdown): every page on the site](https://nuxt.com/sitemap.md) - [MCP server card: MCP endpoint at https://nuxt.com/mcp](https://nuxt.com/.well-known/mcp/server-card.json) - [MCP endpoint (streamable HTTP)](https://nuxt.com/mcp) - [llms.txt: index of the documentation for LLMs](https://nuxt.com/llms.txt) - [llms-full.txt: the full documentation as a single file](https://nuxt.com/llms-full.txt) - [OpenAPI specification: machine-readable API surface](https://nuxt.com/openapi.json) - [Design system](https://nuxt.com/design.md) - [Documentation](https://nuxt.com/docs) Every page on this site is available as raw markdown: append `.md` to its URL or send `Accept: text/markdown`. # Introduction > Nuxt's goal is to make web development intuitive and performant with a great Developer Experience in mind. Nuxt is a free and [open-source framework](https://github.com/nuxt/nuxt) with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with [Vue.js](https://vuejs.org). We made everything so you can start writing `.vue` files from the beginning while enjoying hot module replacement in development and a performant application in production with server-side rendering by default. Nuxt has no vendor lock-in, allowing you to deploy your application [**everywhere, even on the edge**](https://nuxt.com/blog/nuxt-on-the-edge). ::tip If you want to play around with Nuxt in your browser, you can [try it out in one of our online sandboxes](https://nuxt.com/docs/4.x/getting-started/installation#play-online). :: ## Automation and Conventions Nuxt uses conventions and an opinionated directory structure to automate repetitive tasks and allow developers to focus on pushing features. The configuration file can still customize and override its default behaviors. - **File-based routing:** define routes based on the structure of your [`app/pages/` directory](https://nuxt.com/docs/4.x/directory-structure/app/pages). This can make it easier to organize your application and avoid the need for manual route configuration. - **Code splitting:** Nuxt automatically splits your code into smaller chunks, which can help reduce the initial load time of your application. - **Server-side rendering out of the box:** Nuxt comes with built-in SSR capabilities, so you don't have to set up a separate server yourself. - **Auto-imports:** write Vue composables and components in their respective directories and use them without having to import them with the benefits of tree-shaking and optimized JS bundles. - **Data-fetching utilities:** Nuxt provides composables to handle SSR-compatible data fetching as well as different strategies. - **Zero-config TypeScript support:** write type-safe code without having to learn TypeScript with our auto-generated types and `tsconfig.json`. - **Configured build tools:** we use [Vite](https://vite.dev) by default to support hot module replacement (HMR) in development and bundling your code for production with best-practices baked-in. Nuxt takes care of these and provides both frontend and backend functionality so you can focus on what matters: **creating your web application**. ## Server-Side Rendering Nuxt comes with built-in server-side rendering (SSR) capabilities by default, without having to configure a server yourself, which has many benefits for web applications: - **Faster initial page load time:** Nuxt sends a fully rendered HTML page to the browser, which can be displayed immediately. This can provide a faster perceived page load time and a better user experience (UX), especially on slower networks or devices. - **Improved SEO:** search engines can better index SSR pages because the HTML content is available immediately, rather than requiring JavaScript to render the content on the client-side. - **Better performance on low-powered devices:** it reduces the amount of JavaScript that needs to be downloaded and executed on the client-side, which can be beneficial for low-powered devices that may struggle with processing heavy JavaScript applications. - **Better accessibility:** the content is immediately available on the initial page load, improving accessibility for users who rely on screen readers or other assistive technologies. - **Easier caching:** pages can be cached on the server-side, which can further improve performance by reducing the amount of time it takes to generate and send the content to the client. Overall, server-side rendering can provide a faster and more efficient user experience, as well as improve search engine optimization and accessibility. As Nuxt is a versatile framework, it gives you the possibility to statically render your whole application to a static hosting with `nuxt generate`, disable SSR globally with the `ssr: false` option or leverage hybrid rendering by setting up the `routeRules` option. ::read-more{title="Nuxt rendering modes" to="https://nuxt.com/docs/4.x/guide/concepts/rendering"} :: ### Server engine The Nuxt server engine, [Nitro](https://nitro.build/), unlocks new full-stack capabilities. In development, it uses Rollup and Node.js workers for your server code and context isolation. It also generates your server API by reading files from `server/api/` and server middleware from `server/middleware/`. In production, Nitro builds your app and server into one universal `.output` directory. This output is light: minified and removed from any Node.js modules (except polyfills). You can deploy this output on any system supporting JavaScript, from Node.js, Serverless, Workers, Edge-side rendering or purely static. ::read-more{title="Nuxt server engine" to="https://nuxt.com/docs/4.x/guide/concepts/server-engine"} :: ### Production-ready A Nuxt application can be deployed on a Node or Deno server, pre-rendered to be hosted in static environments, or deployed to serverless and edge providers. ::read-more{title="Deployment section" to="https://nuxt.com/docs/4.x/getting-started/deployment"} :: ### Modular A module system allows you to extend Nuxt with custom features and integrations with third-party services. ::read-more{title="Nuxt Modules Concept" to="https://nuxt.com/docs/4.x/guide/concepts/modules"} :: ### Architecture Nuxt is composed of different [core packages](https://github.com/nuxt/nuxt/tree/main/packages): - Core engine: [nuxt](https://github.com/nuxt/nuxt/tree/main/packages/nuxt) - Bundlers: [@nuxt/vite-builder](https://github.com/nuxt/nuxt/tree/main/packages/vite), [@nuxt/rspack-builder](https://github.com/nuxt/nuxt/tree/main/packages/rspack) and [@nuxt/webpack-builder](https://github.com/nuxt/nuxt/tree/main/packages/webpack) - Command line interface: [@nuxt/cli](https://github.com/nuxt/cli) - Server engine: [nitro](https://github.com/nitrojs/nitro) - Development kit: [@nuxt/kit](https://github.com/nuxt/nuxt/tree/main/packages/kit) We recommend reading each concept to have a full vision of Nuxt capabilities and the scope of each package. # Installation > Get started with Nuxt quickly with our online starters or start locally with your terminal. ## Play Online If you just want to play around with Nuxt in your browser without setting up a project, you can use this online sandbox: ::card-group :::card ```yaml [props] title: Open on StackBlitz color: info icon: i-simple-icons-stackblitz to: https://stackblitz.com/github/nuxt/starter/tree/v4?file=app%2Fapp.vue target: _blank ``` Online playground with code editor and terminal access. ::: :: ## New Project ::steps ### Prerequisites - **Node.js** - [`22.x`](https://nodejs.org/en) or newer (but we recommend the [active LTS release](https://github.com/nodejs/release#release-schedule)) - **Text editor** - There is no IDE requirement, but we recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar) or [WebStorm](https://www.jetbrains.com/webstorm/), which, along with [other JetBrains IDEs](https://www.jetbrains.com/ides/), offers great Nuxt support right out-of-the-box. If you use another editor, such as Neovim, you can configure [Vue Language Server](https://github.com/vuejs/language-tools) support by following the [Vue Language Tools setup guides](https://github.com/vuejs/language-tools/wiki). - **Terminal** - In order to run Nuxt commands :::callout ::::collapsible{name="additional notes for an optimal setup"} - **Node.js**: Make sure to use an even numbered version (22, 24, etc.) - **Neovim**: When configuring the Vue TypeScript plugin, make sure `location` points to the `@vue/language-server` package directory, not its binary. See the [Neovim setup guide](https://github.com/vuejs/language-tools/wiki/Neovim) for a working configuration. - **WSL**: If you are using Windows and experience slow HMR, you may want to try using [WSL (Windows Subsystem for Linux)](https://learn.microsoft.com/en-us/windows/wsl/install) which may solve some performance issues. - **Windows slow DNS resolution**: Instead of using `localhost:3000` for local dev server on Windows, use `127.0.0.1` for much faster loading experience on browsers. :::: ::: ### Create a New Project Open a terminal (if you're using [Visual Studio Code](https://code.visualstudio.com), you can open an [integrated terminal](https://code.visualstudio.com/docs/terminal/basics)) and use the following command to create a new starter project: :::code-group{sync="pm"} ```bash [npm] npm create nuxt@latest ``` ```bash [yarn] yarn create nuxt ``` ```bash [pnpm] pnpm create nuxt@latest ``` ```bash [bun] bun create nuxt@latest ``` ```bash [deno] deno -A npm:create-nuxt@latest ``` ::: Open your project folder in Visual Studio Code: ```bash [Terminal] code ``` Or change directory into your new project from your terminal: ```bash cd ``` ### Development Server Now you'll be able to start your Nuxt app in development mode: :::code-group{sync="pm"} ```bash [npm] npm run dev -- -o ``` ```bash [yarn] yarn dev --open ``` ```bash [pnpm] pnpm dev -o ``` ```bash [bun] bun run dev -o # To use the Bun runtime during development # bun --bun run dev -o ``` ```bash [deno] deno run dev -o ``` ::: :::tip{icon="i-lucide-circle-check"} Well done! A browser window should automatically open for . ::: :::tip{icon="i-lucide-container"} If you're using Docker or want a consistent development environment across machines, consider using a dev container. [Learn more about dev containers.](https://nuxt.com/docs/4.x/guide/best-practices/devcontainers) ::: :: ## Next Steps Now that you've created your Nuxt project, you are ready to start building your application. ::read-more{title="Nuxt Concepts" to="https://nuxt.com/docs/4.x/guide/concepts"} :: # Configuration > Nuxt is configured with sensible defaults to make you productive. By default, Nuxt is configured to cover most use cases. The [`nuxt.config.ts`](https://nuxt.com/docs/4.x/directory-structure/nuxt-config) file can override or extend this default configuration. ## Nuxt Configuration The [`nuxt.config.ts`](https://nuxt.com/docs/4.x/directory-structure/nuxt-config) file is located at the root of a Nuxt project and can override or extend the application's behavior. A minimal configuration file exports the `defineNuxtConfig` function containing an object with your configuration. The `defineNuxtConfig` helper is globally available without import. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ // My Nuxt config }) ``` This file will often be mentioned in the documentation, for example to add custom scripts, register modules or change rendering modes. ::read-more{to="https://nuxt.com/docs/4.x/api/nuxt-config"} Every option is described in the **Configuration Reference**. :: ::note You don't have to use TypeScript to build an application with Nuxt. However, it is strongly recommended to use the `.ts` extension for the `nuxt.config` file. This way you can benefit from hints in your IDE to avoid typos and mistakes while editing your configuration. :: ### Environment Overrides You can configure fully typed, per-environment overrides in your nuxt.config ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ $production: { routeRules: { '/**': { isr: true }, }, }, $development: { // }, $env: { staging: { // }, }, }) ``` To select an environment when running a Nuxt CLI command, simply pass the name to the `--envName` flag, like so: `nuxt build --envName staging`. To learn more about the mechanism behind these overrides, please refer to the `c12` documentation on [environment-specific configuration](https://github.com/unjs/c12?tab=readme-ov-file#environment-specific-configuration). ::video-accordion{title="Watch a video from Alexander Lichter about the env-aware nuxt.config.ts" videoId="DFZI2iVCrNc"} :: ::note If you're authoring layers, you can also use the `$meta` key to provide metadata that you or the consumers of your layer might use. :: ### Environment Variables and Private Tokens The `runtimeConfig` API exposes values like environment variables to the rest of your application. By default, these keys are only available server-side. The keys within `runtimeConfig.public` and `runtimeConfig.app` (which is used by Nuxt internally) are also available client-side. Those values should be defined in `nuxt.config` and can be overridden using environment variables. ::code-group ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { // The private keys which are only available server-side apiSecret: '123', // Keys within public are also exposed client-side public: { apiBase: '/api', }, }, }) ``` ```ini [.env] # This will override the value of apiSecret NUXT_API_SECRET=api_secret_token ``` :: These variables are exposed to the rest of your application using the [`useRuntimeConfig()`](https://nuxt.com/docs/4.x/api/composables/use-runtime-config) composable. ```vue [app/pages/index.vue] ``` ::read-more{to="https://nuxt.com/docs/4.x/guide/going-further/runtime-config"} :: ## App Configuration The `app.config.ts` file, located in the source directory (by default `app/`), is used to expose public variables that can be determined at build time. Contrary to the `runtimeConfig` option, these cannot be overridden using environment variables. A minimal configuration file exports the `defineAppConfig` function containing an object with your configuration. The `defineAppConfig` helper is globally available without import. ```ts [app/app.config.ts] export default defineAppConfig({ title: 'Hello Nuxt', theme: { dark: true, colors: { primary: '#ff0000', }, }, }) ``` These variables are exposed to the rest of your application using the [`useAppConfig`](https://nuxt.com/docs/4.x/api/composables/use-app-config) composable. ```vue [app/pages/index.vue] ``` ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/app-config"} :: ## `runtimeConfig` vs. `app.config` As stated above, `runtimeConfig` and `app.config` are both used to expose variables to the rest of your application. To determine whether you should use one or the other, here are some guidelines: - `runtimeConfig`: Private or public tokens that need to be specified after build using environment variables. - `app.config`: Public tokens that are determined at build time, website configuration such as theme variant, title and any project config that are not sensitive. | Feature | `runtimeConfig` | `app.config` | | ------------------------- | --------------- | ------------ | | Client-side | Hydrated | Bundled | | Environment variables | ✅ Yes | ❌ No | | Reactive | ✅ Yes | ✅ Yes | | Types support | ✅ Partial | ✅ Yes | | Configuration per request | ❌ No | ✅ Yes | | Hot module replacement | ❌ No | ✅ Yes | | Non-primitive JS types | ❌ No | ✅ Yes | ## External Configuration Files Nuxt uses [`nuxt.config.ts`](https://nuxt.com/docs/4.x/directory-structure/nuxt-config) file as the single source of truth for configurations and skips reading external configuration files. During the course of building your project, you may have a need to configure those. The following table highlights common configurations and, where applicable, how they can be configured with Nuxt. | Name | Config File | How To Configure | | --------------------------------- | ----------------------- | --------------------------------------------------------------------------------------- | | [Nitro](https://nitro.build) | ~~`nitro.config.ts`~~ | Use [`nitro`](https://nuxt.com/docs/4.x/api/nuxt-config#nitro) key in `nuxt.config` | | [PostCSS](https://postcss.org) | ~~`postcss.config.js`~~ | Use [`postcss`](https://nuxt.com/docs/4.x/api/nuxt-config#postcss) key in `nuxt.config` | | [Vite](https://vite.dev) | ~~`vite.config.ts`~~ | Use [`vite`](https://nuxt.com/docs/4.x/api/nuxt-config#vite) key in `nuxt.config` | | [webpack](https://webpack.js.org) | ~~`webpack.config.ts`~~ | Use [`webpack`](https://nuxt.com/docs/4.x/api/nuxt-config#webpack) key in `nuxt.config` | Here is a list of other common config files: | Name | Config File | How To Configure | | -------------------------------------------- | --------------------- | ----------------------------------------------------------------------------- | | [TypeScript](https://www.typescriptlang.org) | `tsconfig.json` | [More Info](https://nuxt.com/docs/4.x/directory-structure/tsconfig) | | [ESLint](https://eslint.org) | `eslint.config.js` | [More Info](https://eslint.org/docs/latest/use/configure/configuration-files) | | [Prettier](https://prettier.io) | `prettier.config.js` | [More Info](https://prettier.io/docs/configuration.html) | | [Stylelint](https://stylelint.io) | `stylelint.config.js` | [More Info](https://stylelint.io/user-guide/configure/) | | [TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxtjs.org/tailwindcss/configuration/) | | [Vitest](https://vitest.dev) | `vitest.config.ts` | [More Info](https://vitest.dev/config/) | | [Vite+](https://viteplus.dev) | `vite.config.ts` | [More Info](https://viteplus.dev/config/) | If you use Vite+, its `vite.config.ts` configures Vite+ commands such as `vp lint`, `vp fmt`, and `vp test`. Nuxt doesn't read Vite options from this file, so configure Vite for your app with the [`vite`](https://nuxt.com/docs/4.x/api/nuxt-config#vite) key in `nuxt.config`. ## Vue Configuration ### With Vite If you need to pass options to `@vitejs/plugin-vue` or `@vitejs/plugin-vue-jsx`, you can do this in your `nuxt.config` file. - `vite.vue` for `@vitejs/plugin-vue`. Check [available options](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue). - `vite.vueJsx` for `@vitejs/plugin-vue-jsx`. Check [available options](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ vite: { vue: { customElement: true, }, vueJsx: { mergeProps: true, }, }, }) ``` ::read-more{to="https://nuxt.com/docs/4.x/api/nuxt-config#vite-vue"} :: ### With webpack If you use webpack and need to configure `vue-loader`, you can do this using `webpack.loaders.vue` key inside your `nuxt.config` file. The available options are [defined here](https://github.com/vuejs/vue-loader/blob/main/src/index.ts#L32-L62). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ webpack: { loaders: { vue: { hotReload: true, }, }, }, }) ``` ::read-more{to="https://nuxt.com/docs/4.x/api/nuxt-config#webpack-loaders"} :: ### Enabling Experimental Vue Features You may need to enable experimental features in Vue, such as `propsDestructure`. Nuxt provides an easy way to do that in `nuxt.config.ts`, no matter which builder you are using: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ vue: { propsDestructure: true, }, }) ``` #### experimental `reactivityTransform` migration from Vue 3.4 and Nuxt 3.9 Since Nuxt 3.9 and Vue 3.4, `reactivityTransform` has been moved from Vue to Vue Macros which has a [Nuxt integration](https://vue-macros.dev/guide/nuxt-integration.html). ::read-more{to="https://nuxt.com/docs/4.x/api/nuxt-config#vue"} :: # Views > Nuxt provides several component layers to implement the user interface of your application. ## `app.vue` ![The app.vue file is the entry point of your application](https://nuxt.com/assets/docs/getting-started/views/app.svg) By default, Nuxt will treat this file as the **entrypoint** and render its content for every route of the application. ```vue [app/app.vue] ``` ::tip If you are familiar with Vue, you might wonder where `main.js` is (the file that normally creates a Vue app). Nuxt does this behind the scene. :: ## Components ![Components are reusable pieces of UI](https://nuxt.com/assets/docs/getting-started/views/components.svg) Most components are reusable pieces of the user interface, like buttons and menus. In Nuxt, you can create these components in the [`app/components/`](https://nuxt.com/docs/4.x/directory-structure/app/components) directory, and they will be automatically available across your application without having to explicitly import them. ::code-group ```vue [app/app.vue] ``` ```vue [app/components/AppAlert.vue] ``` :: ## Pages ![Pages are views tied to a specific route](https://nuxt.com/assets/docs/getting-started/views/pages.svg) Pages represent views for each specific route pattern. Every file in the [`app/pages/`](https://nuxt.com/docs/4.x/directory-structure/app/pages) directory represents a different route displaying its content. To use pages, create an `app/pages/index.vue` file and add `` component to the [`app/app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app) (or remove `app/app.vue` for default entry). You can now create more pages and their corresponding routes by adding new files in the [`app/pages/`](https://nuxt.com/docs/4.x/directory-structure/app/pages) directory. ::code-group ```vue [app/pages/index.vue] ``` ```vue [app/pages/about.vue] ``` :: ::read-more{title="Routing Section" to="https://nuxt.com/docs/4.x/getting-started/routing"} :: ## Layouts ![Layouts are wrapper around pages](https://nuxt.com/assets/docs/getting-started/views/layouts.svg) Layouts are wrappers around pages that contain a common User Interface for several pages, such as header and footer displays. Layouts are Vue files using `` components to display the **page** content. The `app/layouts/default.vue` file will be used by default. Custom layouts can be set as part of your page metadata. ::note If you only have a single layout in your application, we recommend using [`app/app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app) with [``](https://nuxt.com/docs/4.x/api/components/nuxt-page) instead. :: ::code-group ```vue [app/app.vue] ``` ```vue [app/layouts/default.vue] ``` ```vue [app/pages/index.vue] ``` ```vue [app/pages/about.vue] ``` :: If you want to create more layouts and learn how to use them in your pages, find more information in the [Layouts section](https://nuxt.com/docs/4.x/directory-structure/app/layouts). ## Advanced: Extending the HTML Template ::note If you only need to modify the ``, you can refer to the [SEO and meta section](https://nuxt.com/docs/4.x/getting-started/seo-meta). :: You can have full control over the HTML template by adding a Nitro plugin that registers a hook. The callback function of the `render:html` hook allows you to mutate the HTML before it is sent to the client. ```ts [server/plugins/extend-html.ts] export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('render:html', (html, { event }) => { // This will be an object representation of the html template. console.log(html) html.head.push(``) }) // You can also intercept the response here. nitroApp.hooks.hook('render:response', (response, { event }) => { console.log(response) }) }) ``` ::read-more{to="https://nuxt.com/docs/4.x/guide/going-further/hooks"} :: # Assets > Nuxt offers two options for your assets. Nuxt uses two directories to handle assets like stylesheets, fonts or images. - The [`public/`](https://nuxt.com/docs/4.x/directory-structure/public) directory content is served at the server root as-is. - The [`app/assets/`](https://nuxt.com/docs/4.x/directory-structure/app/assets) directory contains by convention every asset that you want the build tool (Vite or webpack) to process. ## Public Directory The [`public/`](https://nuxt.com/docs/4.x/directory-structure/public) directory is used as a public server for static assets publicly available at a defined URL of your application. You can get a file in the [`public/`](https://nuxt.com/docs/4.x/directory-structure/public) directory from your application's code or from a browser by the root URL `/`. ### Example For example, referencing an image file in the `public/img/` directory, available at the static URL `/img/nuxt.png`: ```vue [app/app.vue] ``` ## Assets Directory Nuxt uses [Vite](https://vite.dev/guide/assets) (default) or [webpack](https://webpack.js.org/guides/asset-management/) to build and bundle your application. The main function of these build tools is to process JavaScript files, but they can be extended through [plugins](https://vite.dev/plugins/) (for Vite) or [loaders](https://webpack.js.org/loaders/) (for webpack) to process other kinds of assets, like stylesheets, fonts or SVGs. This step transforms the original file, mainly for performance or caching purposes (such as stylesheet minification or browser cache invalidation). By convention, Nuxt uses the [`app/assets/`](https://nuxt.com/docs/4.x/directory-structure/app/assets) directory to store these files but there is no auto-scan functionality for this directory, and you can use any other name for it. In your application's code, you can reference a file located in the [`app/assets/`](https://nuxt.com/docs/4.x/directory-structure/app/assets) directory by using the `~/assets/` path. ### Example For example, referencing an image file that will be processed if a build tool is configured to handle this file extension: ```vue [app/app.vue] ``` ::note Nuxt won't serve files in the [`app/assets/`](https://nuxt.com/docs/4.x/directory-structure/app/assets) directory at a static URL like `/assets/my-file.png`. If you need a static URL, use the [`public/`](https://nuxt.com/docs/4.x/getting-started/assets#public-directory) directory. :: ### Static vs. Dynamic `src` When an `src` is a static string literal in your template, the build tool rewrites it into a runtime helper that resolves the final URL. A public path such as `/img/nuxt.png` is wrapped so that your [`app.baseURL`](https://nuxt.com/docs/4.x/api/nuxt-config#app-baseurl) is applied when the page renders, and a bundled path such as `~/assets/img/nuxt.png` additionally becomes an import that resolves to the hashed output file. ```vue ``` Because `app.baseURL` is applied at runtime, a static public path works even when the base URL is only known at deploy time (for example set via `NUXT_APP_BASE_URL`), and it works whether or not the file is processed by the build. This resolution only happens for literal paths the build tool can see. A bound `:src` whose value is assembled at runtime is opaque to the build tool, so none of that rewriting happens. The string is used exactly as written: ```vue ``` A runtime-built public path like `/img/${name}.png` is therefore **not** prefixed with [`app.baseURL`](https://nuxt.com/docs/4.x/api/nuxt-config#app-baseurl). If your application is deployed below the origin root, prefix it yourself with [`useRuntimeConfig().app.baseURL`](https://nuxt.com/docs/4.x/api/composables/use-runtime-config) (for example via [`joinURL`](https://github.com/unjs/ufo#joinurl)). The sections below cover how to handle each case when the path is only known at runtime. #### Public Assets If the files do not need to be processed or hashed, put them in the [`public/`](https://nuxt.com/docs/4.x/directory-structure/public) directory and reference them by URL: ```vue [app/app.vue] ``` Files in `public/` keep their original filenames. #### Bundled Assets with Vite The approaches below are specific to Vite, Nuxt's default builder. When the possible files are known, list their imports explicitly: ```vue [app/app.vue] ``` Each import has a literal path, so Vite can find both files at build time while loading only the selected module at runtime. When many files share a directory and extension, use a [variable dynamic import](https://vite.dev/guide/features.html#dynamic-import) instead of listing every file: ```ts async function getImageUrl (name: string) { const image = await import(`./assets/img/${name}.png?url`) return image.default } ``` Only the filename can be dynamic in this example. Keeping the directory and extension in the import lets Vite find the possible files at build time. For a broader pattern or an explicit map of available files, use [`import.meta.glob`](https://vite.dev/guide/features.html#glob-import): ```ts const images = import.meta.glob('./assets/img/*.{png,jpg,svg}', { query: '?url', import: 'default', }) async function getImageUrl (name: string) { const load = images[`./assets/img/${name}.png`] if (!load) { throw new Error(`Unknown image: ${name}`) } return await load() } ``` Glob imports are lazy by default. Add `eager: true` if the URLs must be available synchronously: ```ts const images = import.meta.glob('./assets/img/*.{png,jpg,svg}', { query: '?url', import: 'default', eager: true, }) ``` Every matching asset is still included in the build output. Lazy imports load each match on demand, while an eager glob loads all matches up front and can increase the initial JavaScript size or inline small assets. ::warning Await a lazy import before using its URL in server-rendered markup. Vite's [`new URL(..., import.meta.url)` pattern](https://vite.dev/guide/assets.html#new-url-url-import-meta-url) does not work with SSR. :: # Styling > Learn how to style your Nuxt application. Nuxt is highly flexible when it comes to styling. Write your own styles, or reference local and external stylesheets. You can use CSS preprocessors, CSS frameworks, UI libraries and Nuxt modules to style your application. ## Local Stylesheets If you're writing local stylesheets, the natural place to put them is the [`app/assets/` directory](https://nuxt.com/docs/4.x/directory-structure/app/assets). ### Importing Within Components You can import stylesheets in your pages, layouts and components directly. You can use a JavaScript import, or a CSS [`@import` statement](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@import). ```vue [app/pages/index.vue] ``` ::tip The stylesheets will be inlined in the HTML rendered by Nuxt. :: ### The CSS Property You can also use the `css` property in the Nuxt configuration. The natural place for your stylesheets is the [`app/assets/` directory](https://nuxt.com/docs/4.x/directory-structure/app/assets). You can then reference its path and Nuxt will include it to all the pages of your application. ```ts [nuxt.config.ts] export default defineNuxtConfig({ css: ['~/assets/css/main.css'], }) ``` ::tip The stylesheets will be inlined in the HTML rendered by Nuxt, injected globally and present in all pages. :: ### Working With Fonts Place your local fonts files in your `public/` directory, for example in `public/fonts`. You can then reference them in your stylesheets using `url()`. ```css [assets/css/main.css] @font-face { font-family: 'FarAwayGalaxy'; src: url('/fonts/FarAwayGalaxy.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap; } ``` Then reference your fonts by name in your stylesheets, pages or components: ```vue ``` ### Stylesheets Distributed Through NPM You can also reference stylesheets that are distributed through npm. Let's use the popular `animate.css` library as an example. ::code-group{sync="pm"} ```bash [npm] npm install animate.css ``` ```bash [yarn] yarn add animate.css ``` ```bash [pnpm] pnpm install animate.css ``` ```bash [bun] bun install animate.css ``` ```bash [deno] deno install npm:animate.css ``` :: Then you can reference it directly in your pages, layouts and components: ```vue [app/app.vue] ``` The package can also be referenced as a string in the css property of your Nuxt configuration. ```ts [nuxt.config.ts] export default defineNuxtConfig({ css: ['animate.css'], }) ``` ## External Stylesheets You can include external stylesheets in your application by adding a link element in the head section of your nuxt.config file. You can achieve this result using different methods. Note that local stylesheets can also be included this way. You can manipulate the head with the [`app.head`](https://nuxt.com/docs/4.x/api/nuxt-config#head) property of your Nuxt configuration: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }], }, }, }) ``` ### Dynamically Adding Stylesheets You can use the useHead composable to dynamically set a value in your head in your code. ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-head"} :: ```ts twoslash useHead({ link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }], }) ``` Nuxt uses `unhead` under the hood, and you can refer to [its full documentation](https://unhead.unjs.io). ### Modifying The Rendered Head With A Nitro Plugin If you need more advanced control, you can intercept the rendered html with a hook and modify the head programmatically. Create a plugin in `~~/server/plugins/my-plugin.ts` like this: ```ts [server/plugins/my-plugin.ts] export default defineNitroPlugin((nitro) => { nitro.hooks.hook('render:html', (html) => { html.head.push('') }) }) ``` External stylesheets are render-blocking resources: they must be loaded and processed before the browser renders the page. Web pages that contain unnecessarily large styles take longer to render. You can read more about it on [web.dev](https://web.dev/articles/defer-non-critical-css). ## Using Preprocessors To use a preprocessor like SCSS, Sass, Less or Stylus, install it first. ::code-group ```bash [Sass & SCSS] npm install -D sass ``` ```bash [Less] npm install -D less ``` ```bash [Stylus] npm install -D stylus ``` :: The natural place to write your stylesheets is the `app/assets` directory. You can then import your source files in your `app.vue` (or layouts files) using your preprocessor's syntax. ```vue [app/pages/app.vue] ``` Alternatively, you can use the `css` property of your Nuxt configuration. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ css: ['~/assets/scss/main.scss'], }) ``` ::tip In both cases, the compiled stylesheets will be inlined in the HTML rendered by Nuxt. :: If you need to inject code in pre-processed files, like a [Sass partial](https://sass-lang.com/documentation/at-rules/use/#partials) with color variables, you can do so with the Vite [preprocessors options](https://vite.dev/config/shared-options#css-preprocessoroptions). Create some partials in your `app/assets` directory: ::code-group{sync="preprocessor"} ```scss [assets/_colors.scss] $primary: #49240F; $secondary: #E4A79D; ``` ```sass [assets/_colors.sass] $primary: #49240F $secondary: #E4A79D ``` :: Then in your `nuxt.config` : ::code-group ```ts twoslash [SCSS] export default defineNuxtConfig({ vite: { css: { preprocessorOptions: { scss: { additionalData: '@use "~/assets/_colors.scss" as *;', }, }, }, }, }) ``` ```ts twoslash [SASS] export default defineNuxtConfig({ vite: { css: { preprocessorOptions: { sass: { additionalData: '@use "~/assets/_colors.sass" as *\n', }, }, }, }, }) ``` :: Nuxt uses Vite by default. If you wish to use webpack instead, refer to each preprocessor loader [documentation](https://webpack.js.org/loaders/sass-loader/). ### Preprocessor Workers (Experimental) Vite has made available an [experimental option](https://vite.dev/config/shared-options#css-preprocessormaxworkers) which can speed up using preprocessors. You can enable this in your `nuxt.config`: ```ts export default defineNuxtConfig({ vite: { css: { preprocessorMaxWorkers: true, // number of CPUs minus 1 }, }, }) ``` ::note This is an experimental option and you should refer to the Vite documentation and [provide feedback](https://github.com/vitejs/vite/discussions/15835). :: ## Single File Components (SFC) Styling One of the best things about Vue and SFC is how great it is at naturally dealing with styling. You can directly write CSS or preprocessor code in the style block of your components file, therefore you will have a fantastic developer experience without having to use something like CSS-in-JS. However if you wish to use CSS-in-JS, you can find 3rd party libraries and modules that support it, such as [pinceau](https://github.com/Tahul/pinceau). You can refer to the [Vue docs](https://vuejs.org/api/sfc-css-features) for a comprehensive reference about styling components in SFC. ### Class And Style Bindings You can leverage Vue SFC features to style your components with class and style attributes. ::code-group ```vue [Ref and Reactive] ``` ```vue [Computed] ``` ```vue [Array] ``` ```vue [Style] ``` :: Refer to the [Vue docs](https://vuejs.org/guide/essentials/class-and-style) for more information. ### Dynamic Styles With `v-bind` You can reference JavaScript variables and expressions within your style blocks with the v-bind function. The binding will be dynamic, meaning that if the variable value changes, the style will be updated. ```vue ``` ### Scoped Styles The scoped attribute allows you to style components in isolation. The styles declared with this attribute will only apply to this component. ```vue ``` ### CSS Modules You can use [CSS Modules](https://github.com/css-modules/css-modules) with the module attribute. Access it with the injected `$style` variable. ```vue ``` ### Preprocessors Support SFC style blocks support preprocessor syntax. Vite comes with built-in support for .scss, .sass, .less, .styl and .stylus files without configuration. You just need to install them first, and they will be available directly in SFC with the lang attribute. ::code-group ```vue [SCSS] ``` ```vue [Sass] ``` ```vue [LESS] ``` ```vue [Stylus] ``` :: You can refer to the [Vite CSS docs](https://vite.dev/guide/features#css) and the [@vitejs/plugin-vue docs](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue). For webpack users, refer to the [vue loader docs](https://vue-loader.vuejs.org). ## Using PostCSS Nuxt comes with postcss built-in. You can configure it in your `nuxt.config` file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ postcss: { plugins: { 'postcss-nested': {}, 'postcss-custom-media': {}, }, }, }) ``` For proper syntax highlighting in SFC, you can use the postcss lang attribute. ```vue ``` By default, Nuxt comes with the following plugins already pre-configured: - [postcss-import](https://github.com/postcss/postcss-import): Improves the `@import` rule - [postcss-url](https://github.com/postcss/postcss-url): Transforms `url()` statements - [autoprefixer](https://github.com/postcss/autoprefixer): Automatically adds vendor prefixes - [cssnano](https://cssnano.github.io/cssnano/): Minification and purge ## Leveraging Layouts For Multiple Styles If you need to style different parts of your application completely differently, you can use layouts. Use different styles for different layouts. ```vue ``` ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/layouts"} :: ## Third Party Libraries And Modules Nuxt isn't opinionated when it comes to styling and provides you with a wide variety of options. You can use any styling tool that you want, such as popular libraries like [UnoCSS](https://unocss.dev) or [Tailwind CSS](https://tailwindcss.com). The community and the Nuxt team have developed plenty of Nuxt modules to make the integration easier. You can discover them on the [modules section](https://nuxt.com/modules) of the website. Here are a few modules to help you get started: - [UnoCSS](https://nuxt.com/modules/unocss): Instant on-demand atomic CSS engine - [Tailwind CSS](https://nuxt.com/modules/tailwindcss): Utility-first CSS framework - [Fontaine](https://github.com/nuxt-modules/fontaine): Font metric fallback - [Pinceau](https://github.com/Tahul/pinceau): Adaptable styling framework - [Nuxt UI](https://ui.nuxt.com): A UI Library for Modern Web Apps - [Panda CSS](https://panda-css.com/docs/installation/nuxt): CSS-in-JS engine that generates atomic CSS at build time Nuxt modules provide you with a good developer experience out of the box, but remember that if your favorite tool doesn't have a module, it doesn't mean that you can't use it with Nuxt! You can configure it yourself for your own project. Depending on the tool, you might need to use a [Nuxt plugin](https://nuxt.com/docs/4.x/directory-structure/app/plugins) and/or [make your own module](https://nuxt.com/docs/4.x/guide/modules). Share them with the [community](https://nuxt.com/modules) if you do! ### Easily Load Webfonts You can use [the Nuxt Google Fonts module](https://github.com/nuxt-modules/google-fonts) to load Google Fonts. If you are using [UnoCSS](https://unocss.dev/integrations/nuxt), note that it comes with a [web fonts presets](https://unocss.dev/presets/web-fonts) to conveniently load fonts from common providers, including Google Fonts and more. ## Advanced ### Transitions Nuxt comes with the same `` element that Vue has, and also has support for the experimental [View Transitions API](https://nuxt.com/docs/4.x/getting-started/transitions#view-transitions-api-experimental). ::read-more{to="https://nuxt.com/docs/4.x/getting-started/transitions"} :: ### Font Advanced Optimization We would recommend using [Fontaine](https://github.com/nuxt-modules/fontaine) to reduce your [CLS](https://web.dev/articles/cls). If you need something more advanced, consider creating a Nuxt module to extend the build process or the Nuxt runtime. ::tip Always remember to take advantage of the various tools and techniques available in the Web ecosystem at large to make styling your application easier and more efficient. Whether you're using native CSS, a preprocessor, postcss, a UI library or a module, Nuxt has got you covered. Happy styling! :: ### LCP Advanced Optimizations You can do the following to speed-up the download of your global CSS files: - Use a CDN so the files are physically closer to your users - Compress your assets, ideally using Brotli - Use HTTP2/HTTP3 for delivery - Host your assets on the same domain (do not use a different subdomain) Most of these things should be done for you automatically if you're using modern platforms like Cloudflare, Netlify or Vercel. You can find an LCP optimization guide on [web.dev](https://web.dev/articles/optimize-lcp). If all of your CSS is inlined by Nuxt, you can (experimentally) completely stop external CSS files from being referenced in your rendered HTML. You can achieve that with a hook, that you can place in a module, or in your Nuxt configuration file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ hooks: { 'build:manifest': (manifest) => { // find the app entry, css list const css = Object.values(manifest).find(options => options.isEntry)?.css if (css) { // start from the end of the array and go to the beginning for (let i = css.length - 1; i >= 0; i--) { // if it starts with 'entry', remove it from the list if (css[i].startsWith('entry')) { css.splice(i, 1) } } } }, }, }) ``` # Routing > Nuxt file-system routing creates a route for every file in the pages/ directory. One core feature of Nuxt is the file system router. Every Vue file inside the [`app/pages/`](https://nuxt.com/docs/4.x/directory-structure/app/pages) directory creates a corresponding URL (or route) that displays the contents of the file. By using dynamic imports for each page, Nuxt leverages code-splitting to ship the minimum amount of JavaScript for the requested route. ::note Code-splitting is enabled by default and is recommended for most apps. If you have a specific reason to ship a single bundle instead, you can disable it in your [`nuxt.config`](https://nuxt.com/docs/4.x/api/nuxt-config): ```ts [nuxt.config.ts] export default defineNuxtConfig({ vite: { $client: { build: { rolldownOptions: { output: { codeSplitting: false, }, }, }, }, }, }) ``` This ships all JavaScript in a single file. It is rarely beneficial — it usually increases the initial download, even on slow connections — so only disable code-splitting if you have measured that it helps your case. :: ## Pages Nuxt routing is based on [vue-router](https://router.vuejs.org) and generates the routes from every component created in the [`app/pages/` directory](https://nuxt.com/docs/4.x/directory-structure/app/pages), based on their filename. This file system routing uses naming conventions to create dynamic and nested routes: ::code-group ```bash [Directory Structure] -| pages/ ---| about.vue ---| index.vue ---| posts/ -----| [id].vue ``` ```json [Generated Router File] { "routes": [ { "path": "/about", "component": "pages/about.vue" }, { "path": "/", "component": "pages/index.vue" }, { "path": "/posts/:id", "component": "pages/posts/[id].vue" } ] } ``` :: ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/pages"} :: ## Navigation The [``](https://nuxt.com/docs/4.x/api/components/nuxt-link) component links pages between them. It renders an `` tag with the `href` attribute set to the route of the page. Once the application is hydrated, page transitions are performed in JavaScript by updating the browser URL. This prevents full-page refreshes and allows for animated transitions. When a [``](https://nuxt.com/docs/4.x/api/components/nuxt-link) enters the viewport on the client side, Nuxt will automatically prefetch components and payload (generated pages) of the linked pages ahead of time, resulting in faster navigation. ```vue [app/pages/index.vue] ``` ::read-more{to="https://nuxt.com/docs/4.x/api/components/nuxt-link"} :: ## Route Parameters The [`useRoute()`](https://nuxt.com/docs/4.x/api/composables/use-route) composable can be used in a ` ``` ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-route"} :: ## Route Middleware Nuxt provides a customizable route middleware framework you can use throughout your application, ideal for extracting code that you want to run before navigating to a particular route. ::note Route middleware runs within the Vue part of your Nuxt app. Despite the similar name, they are completely different from server middleware, which are run in the Nitro server part of your app. :: ::important Route middleware does **not** run for server routes (e.g. `/api/*`) or other server requests. To apply middleware to these requests, use [server middleware](https://nuxt.com/docs/4.x/directory-structure/server#server-middleware) instead. :: There are three kinds of route middleware: 1. Anonymous (or inline) route middleware, which are defined directly in the pages where they are used. 2. Named route middleware, which are placed in the [`app/middleware/`](https://nuxt.com/docs/4.x/directory-structure/app/middleware) directory and will be automatically loaded via asynchronous import when used on a page. (**Note**: The route middleware name is normalized to kebab-case, so `someMiddleware` becomes `some-middleware`.) 3. Global route middleware, which are placed in the [`app/middleware/`](https://nuxt.com/docs/4.x/directory-structure/app/middleware) directory (with a `.global` suffix) and will be automatically run on every route change. Example of an `auth` middleware protecting the `/dashboard` page: ::code-group ```ts twoslash [middleware/auth.ts] function isAuthenticated (): boolean { return false } // ---cut--- export default defineNuxtRouteMiddleware((to, from) => { // isAuthenticated() is an example method verifying if a user is authenticated if (isAuthenticated() === false) { return navigateTo('/login') } }) ``` ```vue twoslash [pages/dashboard.vue] ``` :: ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/middleware"} :: ## Route Validation Nuxt offers route validation via the `validate` property in [`definePageMeta()`](https://nuxt.com/docs/4.x/api/utils/define-page-meta) in each page you wish to validate. The `validate` property accepts the `route` as an argument. You can return a boolean value to determine whether or not this is a valid route to be rendered with this page. If you return `false`, this will cause a 404 error. You can also directly return an object with `status`/`statusText` to customize the error returned. If you have a more complex use case, then you can use anonymous route middleware instead. ```vue twoslash [pages/posts/[id\\].vue] ``` ::read-more{to="https://nuxt.com/docs/4.x/api/utils/define-page-meta"} :: # SEO and Meta > Improve your Nuxt app's SEO with powerful head config, composables and components. Nuxt head tag management is powered by [Unhead](https://unhead.unjs.io). It provides sensible defaults, several powerful composables and numerous configuration options to manage your app's head and SEO meta tags. ## Nuxt Config Providing an [`app.head`](https://nuxt.com/docs/4.x/api/nuxt-config#head) property in your [`nuxt.config.ts`](https://nuxt.com/docs/4.x/directory-structure/nuxt-config) allows you to statically customize the head for your entire app. ::important This method does not allow you to provide reactive data. We recommend using `useHead()` in `app.vue`. :: It's good practice to set tags here that won't change such as your site title default, language and favicon. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { title: 'Nuxt', // default fallback title htmlAttrs: { lang: 'en', }, link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, ], }, }, }) ``` ::note When you set [`app.cdnURL`](https://nuxt.com/docs/4.x/api/nuxt-config#cdnurl), assets in your [`public/`](https://nuxt.com/docs/4.x/directory-structure/public) directory (including `favicon.ico`) are served from that CDN. Nuxt resolves public assets against `cdnURL`, falling back to `app.baseURL`. However, a static `app.head` link such as `href: '/favicon.ico'` above is a literal path and is **not** resolved against `cdnURL`. To point the favicon at the resolved location, build the `href` from runtime config with [`useHead()`](https://nuxt.com/docs/4.x/api/composables/use-head) in `app.vue`: ```vue [app/app.vue] ``` :: You can also provide any of the keys listed below in [Types](https://nuxt.com/docs/4.x/getting-started/seo-meta#types). ### Default Tags Some tags are provided by Nuxt by default to ensure your website works well out of the box. - `viewport`: `width=device-width, initial-scale=1` - `charset`: `utf-8` While most sites won't need to override these defaults, you can update them using the keyed shortcuts. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { // update Nuxt defaults charset: 'utf-16', viewport: 'width=device-width, initial-scale=1, maximum-scale=1', }, }, }) ``` ## `useHead` The [`useHead`](https://nuxt.com/docs/4.x/api/composables/use-head) composable function supports reactive input, allowing you to manage your head tags programmatically. ```vue twoslash [app/app.vue] ``` We recommend taking a look at the [`useHead`](https://nuxt.com/docs/4.x/api/composables/use-head) and [`useHeadSafe`](https://nuxt.com/docs/4.x/api/composables/use-head-safe) composables. ## `useSeoMeta` The [`useSeoMeta`](https://nuxt.com/docs/4.x/api/composables/use-seo-meta) composable lets you define your site's SEO meta tags as an object with full type safety. This helps you avoid typos and common mistakes, such as using `name` instead of `property`. ```vue twoslash [app/app.vue] ``` ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-seo-meta"} :: ## Components While using [`useHead`](https://nuxt.com/docs/4.x/api/composables/use-head) is recommended in all cases, you may have a personal preference for defining your head tags in your template using components. Nuxt provides the following components for this purpose: ``, `<Base>`, `<NoScript>`, `<Style>`, `<Meta>`, `<Link>`, `<Body>`, `<Html>` and `<Head>`. Note the capitalization of these components ensuring we don't use invalid native HTML tags. `<Head>` and `<Body>` can accept nested meta tags (for aesthetic reasons) but this does not affect *where* the nested meta tags are rendered in the final HTML. <!-- @case-police-ignore html --> ```vue [app/app.vue] <script setup lang="ts"> const title = ref('Hello World') </script> <template> <div> <Head> <Title>{{ title }}

{{ title }}

``` It's suggested to wrap your components in either a `` or `` component as tags will be deduped more intuitively. ::warning If you need to duplicate tags across client-server boundaries, apply a `key` attribute on the `` component. :: ## Types Below are the non-reactive types used for [`useHead`](https://nuxt.com/docs/4.x/api/composables/use-head), [`app.head`](https://nuxt.com/docs/4.x/api/nuxt-config#head) and components. ```ts [Types] interface MetaObject { title?: string titleTemplate?: string | ((title?: string) => string) templateParams?: Record> base?: Base link?: Link[] meta?: Meta[] style?: Style[] script?: Script[] noscript?: Noscript[] htmlAttrs?: HtmlAttributes bodyAttrs?: BodyAttributes } ``` See [@unhead/vue](https://github.com/unjs/unhead/blob/main/packages/vue/src/types/schema.ts) for more detailed types. ## Features ### Reactivity Reactivity is supported on all properties, by providing a computed value, a getter, or a reactive object. ::code-group ```vue twoslash [useHead] ``` ```vue twoslash [useSeoMeta] ``` ```vue [app/Components] ``` :: ### Title Template You can use the `titleTemplate` option to provide a dynamic template for customizing the title of your site. For example, you could add the name of your site to the title of every page. The `titleTemplate` can either be a string, where `%s` is replaced with the title, or a function. If you want to use a function (for full control), then this cannot be set in your `nuxt.config`. It is recommended instead to set it within your `app.vue` file where it will apply to all pages on your site: ::code-group ```vue twoslash [useHead] ``` :: Now, if you set the title to `My Page` with [`useHead`](https://nuxt.com/docs/4.x/api/composables/use-head) on another page of your site, the title would appear as 'My Page - Site Title' in the browser tab. You could also pass `null` to default to 'Site Title'. ### Template Parameters You can use `templateParams` to provide additional placeholders in your `titleTemplate` besides the default `%s`. This allows for more dynamic title generation. ::code-group ```vue twoslash [useHead] ``` :: ### Body Tags You can use the `tagPosition: 'bodyClose'` option on applicable tags to append them to the end of the `` tag. For example: ```vue twoslash ``` ## Example ### With `definePageMeta` Within your [`app/pages/` directory](https://nuxt.com/docs/4.x/directory-structure/app/pages), you can use `definePageMeta` along with [`useHead`](https://nuxt.com/docs/4.x/api/composables/use-head) to set metadata based on the current route. For example, you can first set the current page title (this is extracted at build time via a macro, so it can't be set dynamically): ```vue twoslash [pages/some-page.vue] ``` And then in your layout file, you might use the route's metadata you have previously set: ```vue twoslash [layouts/default.vue] ``` ::link-example{to="https://nuxt.com/docs/examples/features/meta-tags"} :: ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/pages/#page-metadata"} :: ### Dynamic Title In the example below, `titleTemplate` is set either as a string with the `%s` placeholder or as a `function`, which allows greater flexibility in setting the page title dynamically for each route of your Nuxt app: ```vue twoslash [app/app.vue] ``` ```vue twoslash [app/app.vue] ``` `nuxt.config` is also used as an alternative way of setting the page title. However, `nuxt.config` does not allow the page title to be dynamic. Therefore, it is recommended to use `titleTemplate` in the `app.vue` file to add a dynamic title, which is then applied to all routes of your Nuxt app. ### External CSS The example below shows how you might enable Google Fonts using either the `link` property of the [`useHead`](https://nuxt.com/docs/4.x/api/composables/use-head) composable or using the `` component: ::code-group ```vue twoslash [useHead] ``` ```vue [app/Components] ``` :: # Transitions > Apply transitions between pages and layouts with Vue or native browser View Transitions. ::note Nuxt leverages Vue's [``](https://vuejs.org/guide/built-ins/transition#the-transition-component) component to apply transitions between pages and layouts. :: ::important Because Nuxt uses Vue's `` component, a page or layout you want to animate must have a **single root element**. A page or layout with multiple root elements (a fragment) cannot be animated, so the transition will not run and navigating between routes may error. Nuxt warns about this in development. Wrap the template in a single root element (for example a `
`). :: ## Page Transitions You can enable page transitions to apply an automatic transition for all your [pages](https://nuxt.com/docs/4.x/directory-structure/app/pages). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: { name: 'page', mode: 'out-in' }, }, }) ``` ::note If you are changing layouts as well as pages, the page transition you set here will not run. Instead, you should set a [layout transition](https://nuxt.com/docs/4.x/getting-started/transitions#layout-transitions). :: To start adding transition between your pages, add the following CSS to your [`app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app): ::code-group ```vue [app/app.vue] ``` ```vue [app/pages/index.vue] ``` ```vue [app/pages/about.vue] ``` :: This produces the following result when navigating between pages: To set a different transition for a page, set the `pageTransition` key in [`definePageMeta`](https://nuxt.com/docs/4.x/api/utils/define-page-meta) of the page: ::code-group ```vue twoslash [pages/about.vue] ``` ```vue [app/app.vue] ``` :: Moving to the about page will add the 3d rotation effect: ## Layout Transitions You can enable layout transitions to apply an automatic transition for all your [layouts](https://nuxt.com/docs/4.x/directory-structure/app/layouts). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { layoutTransition: { name: 'layout', mode: 'out-in' }, }, }) ``` To start adding transition between your pages and layouts, add the following CSS to your [`app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app): ::code-group ```vue [app/app.vue] ``` ```vue [app/layouts/default.vue] ``` ```vue [app/layouts/orange.vue] ``` ```vue [app/pages/index.vue] ``` ```vue [app/pages/about.vue] ``` :: This produces the following result when navigating between pages: Similar to `pageTransition`, you can apply a custom `layoutTransition` to the page component using `definePageMeta`: ```vue twoslash [pages/about.vue] ``` ## Global Settings You can customize these default transition names globally using `nuxt.config`. Both `pageTransition` and `layoutTransition` keys accept [`TransitionProps`](https://vuejs.org/api/built-in-components#transition) as JSON serializable values where you can pass the `name`, `mode` and other valid transition-props of the custom CSS transition. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: { name: 'fade', mode: 'out-in', // default }, layoutTransition: { name: 'slide', mode: 'out-in', // default }, }, }) ``` ::warning If you change the `name` property, you also have to rename the CSS classes accordingly. :: To override the global transition property, use the `definePageMeta` to define page or layout transitions for a single Nuxt page and override any page or layout transitions that are defined globally in `nuxt.config` file. ```vue twoslash [pages/some-page.vue] ``` ## Disable Transitions `pageTransition` and `layoutTransition` can be disabled for a specific route: ```vue twoslash [pages/some-page.vue] ``` Or globally in the `nuxt.config`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: false, layoutTransition: false, }, }) ``` ## JavaScript Hooks For advanced use-cases, you can use JavaScript hooks to create highly dynamic and custom transitions for your Nuxt pages. This way presents perfect use-cases for JavaScript animation libraries such as [GSAP](https://gsap.com). ```vue twoslash [pages/some-page.vue] ``` ::tip Learn more about additional [JavaScript hooks](https://vuejs.org/guide/built-ins/transition#javascript-hooks) available in the `Transition` component. :: ## Dynamic Transitions To apply dynamic transitions using conditional logic, you can leverage inline [middleware](https://nuxt.com/docs/4.x/directory-structure/app/middleware) to assign a different transition name to `to.meta.pageTransition`. ::code-group ```vue twoslash [pages/[id\\].vue] ``` ```vue [app/layouts/default.vue] ``` :: The page now applies the `slide-left` transition when going to the next id and `slide-right` for the previous one: ## Transition with NuxtPage When `` is used in `app.vue`, transitions can be configured with the `transition` prop to activate transitions globally. ```vue [app/app.vue] ``` ::note Remember, this page transition cannot be overridden with `definePageMeta` on individual pages. :: ## View Transitions API (Experimental) Nuxt ships with an experimental implementation of the [**View Transitions API**](https://developer.chrome.com/docs/web-platform/view-transitions) (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API)). This is an exciting new way to implement native browser transitions which (among other things) have the ability to transition between unrelated elements on different pages. You can check a demo [on StackBlitz](https://stackblitz.com/edit/nuxt-view-transitions). The Nuxt integration can be enabled with the `experimental.viewTransition` option in your configuration file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { viewTransition: true, }, }) ``` The possible values are: `false`, `true`, or `'always'`. If set to true, Nuxt will not apply transitions if the user's browser matches `prefers-reduced-motion: reduce` (recommended). If set to `always`, Nuxt will always apply the transition and it is up to you to respect the user's preference. By default, view transitions are enabled for all [pages](https://nuxt.com/docs/4.x/directory-structure/app/pages), but you can set a different global default. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { // Disable view transitions globally, and opt-in on a per page basis viewTransition: false, }, }) ``` It is possible to override the default `viewTransition` value for a page by setting the `viewTransition` key in [`definePageMeta`](https://nuxt.com/docs/4.x/api/utils/define-page-meta) of the page: ```vue twoslash [pages/about.vue] ``` ::warning Overriding view transitions on a per-page basis will only have an effect if you have enabled the `experimental.viewTransition` option. :: ### View Transition Types :badge[v4.4]{color="info" size="xs" .align-middle} [View transition types](https://developer.chrome.com/blog/view-transitions-update-io24#view-transition-types) allow you to apply different CSS animations depending on the type of navigation. This is useful for creating asymmetric transitions (e.g., a different animation when navigating forward vs. backward). Types are set on the [`ViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) and can be targeted in CSS using the [`:active-view-transition-type()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:active-view-transition-type) pseudo-class selector. You can set default types globally in your `nuxt.config.ts`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { viewTransition: { enabled: true, types: ['slide'], }, }, }) ``` Or configure types per-page using `definePageMeta`. Per-page types support both static arrays and functions for dynamic behavior: ```vue twoslash [pages/detail.vue] ``` You can also use functions for `types`, `toTypes`, and `fromTypes` in `definePageMeta` to determine types dynamically based on the route: ```vue twoslash [pages/[id].vue] ``` Then target these types in your CSS: ```css /* Default crossfade */ ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.3s; } /* Slide left animation */ html:active-view-transition-type(slide-left) { &::view-transition-old(root) { animation: slide-out-left 0.3s ease-in-out; } &::view-transition-new(root) { animation: slide-in-right 0.3s ease-in-out; } } /* Slide right animation */ html:active-view-transition-type(slide-right) { &::view-transition-old(root) { animation: slide-out-right 0.3s ease-in-out; } &::view-transition-new(root) { animation: slide-in-left 0.3s ease-in-out; } } ``` ::note Function values for `types`, `toTypes`, and `fromTypes` only work in `definePageMeta`, not in `nuxt.config.ts` (where only static `string[]` is supported). :: The `page:view-transition:start` hook provides access to the [`ViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) object, which includes a [`types`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition/types) property (`ViewTransitionTypeSet`) that can be read or modified at runtime: ```ts [plugins/view-transition.client.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('page:view-transition:start', (transition) => { // Read or modify types at runtime console.log([...transition.types]) }) }) ``` If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to *disable* Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents: ```ts export default defineNuxtRouteMiddleware((to) => { if (import.meta.server || !document.startViewTransition) { return } // Disable built-in Vue transitions to.meta.pageTransition = false to.meta.layoutTransition = false }) ``` ### Known Issues - If you perform data fetching within your page setup functions, you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates while they are taking place.) We're looking at restricting the View Transition to the final moments before `` resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you. # Data Fetching > Nuxt provides composables to handle data fetching within your application. Nuxt comes with two composables and a built-in library to perform data-fetching in browser or server environments: `useFetch`, [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) and `$fetch`. In a nutshell: - [`$fetch`](https://nuxt.com/docs/4.x/api/utils/dollarfetch) is the simplest way to make a network request. - [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) is a wrapper around `$fetch` that fetches data only once in [universal rendering](https://nuxt.com/docs/4.x/guide/concepts/rendering#universal-rendering). - [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) is similar to `useFetch` but offers more fine-grained control. Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections. ## The Need for `useFetch` and `useAsyncData` Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](https://nuxt.com/docs/4.x/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This can cause hydration issues, increase the time to interactivity and cause unpredictable behavior. The [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) and [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) composables solve this problem by ensuring that if an API call is made on the server, the data is forwarded to the client in the payload. The payload is a JavaScript object accessible through [`useNuxtApp().payload`](https://nuxt.com/docs/4.x/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser [during hydration](https://nuxt.com/docs/4.x/guide/concepts/rendering#universal-rendering). ::tip Use the [Nuxt DevTools](https://devtools.nuxt.com) to inspect this data in the **Payload tab**. :: ```vue [app/app.vue] ``` In the example above, `useFetch` would make sure that the request would occur on the server and is properly forwarded to the browser. `$fetch` has no such mechanism and is a better option to use when the request is solely made from the browser. ### Suspense Nuxt uses Vue's [``](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-call basis. ::note You can add the [``](https://nuxt.com/docs/4.x/api/components/nuxt-loading-indicator) to add a progress bar between page navigations. :: ### A note on `await` Examples in this documentation usually `await` calls to `useFetch` and `useAsyncData`, but this is not always required. The `await` does **not** change the server-rendered HTML. During server rendering, Nuxt waits for the request to resolve before serializing the page either way (``, and `onServerPrefetch` under the hood), so the fully populated result is always sent to the browser. What the `await` *does* change is what happens next in your own ` ``` ::warning Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](https://nuxt.com/docs/4.x/getting-started/data-fetching#the-need-for-usefetch-and-useasyncdata). It is recommended to use `$fetch` for client-side interactions (event-based) or combined with [`useAsyncData`](https://nuxt.com/docs/4.x/getting-started/data-fetching#useasyncdata) when fetching the initial component data. :: ::read-more{to="https://nuxt.com/docs/4.x/api/utils/dollarfetch"} Read more about `$fetch`. :: ### Typing a Value That Holds `$fetch` To type a value that holds `$fetch` itself, such as a `$fetch` instance passed into a plugin or provided through `provide`/`inject`, use the `$Fetch` type (an alias of `TypedFetch`): ```ts import type { $Fetch } from '#app' function createClient (fetcher: $Fetch) { return { todos: () => fetcher('/api/todos'), } } ``` ### Pass Client Headers to the API When calling `useFetch` on the server, Nuxt will use [`useRequestFetch`](https://nuxt.com/docs/4.x/api/composables/use-request-fetch) to proxy client headers and cookies (with the exception of headers not meant to be forwarded, like `host`). ```vue ``` ```ts // /api/echo.ts export default defineEventHandler(event => parseCookies(event)) ``` Alternatively, the example below shows how to use [`useRequestHeaders`](https://nuxt.com/docs/4.x/api/composables/use-request-headers) to access and send cookies to the API from a server-side request (originating on the client). Using an isomorphic `$fetch` call, we ensure that the API endpoint has access to the same `cookie` header originally sent by the user's browser. This is only necessary if you aren't using `useFetch`. ```vue ``` ::tip You can also use [`useRequestFetch`](https://nuxt.com/docs/4.x/api/composables/use-request-fetch) to proxy headers to the call automatically. :: ::caution Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied: - `host`, `accept` - `content-length`, `content-md5`, `content-type` - `x-forwarded-host`, `x-forwarded-port`, `x-forwarded-proto` - `cf-connecting-ip`, `cf-ray` :: ## `useFetch` The [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) composable uses `$fetch` under-the-hood to make SSR-safe network calls in the setup function. ```vue twoslash [app/app.vue] ``` This composable is a wrapper around the [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) composable and `$fetch` utility. ::video-accordion{title="Watch a video from Alexander Lichter to avoid using useFetch the wrong way" videoId="njsGVmcWviY"} :: ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-fetch"} :: ::link-example{to="https://nuxt.com/docs/examples/features/data-fetching"} :: ## `useAsyncData` The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved. ::tip `useFetch(url)` is nearly equivalent to `useAsyncData(url, () => event.$fetch(url))`. It's developer experience sugar for the most common use case. (You can find out more about `event.fetch` at [`useRequestFetch`](https://nuxt.com/docs/4.x/api/composables/use-request-fetch).) :: ::video-accordion{title="Watch a video from Alexander Lichter to dig deeper into the difference between useFetch and useAsyncData" videoId="0X-aOpSGabA"} :: There are some cases when using the [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) composable is not appropriate, for example when a CMS or a third-party provides their own query layer. In this case, you can use [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) to wrap your calls and still keep the benefits provided by the composable. ```vue [app/pages/users.vue] ``` ::note The first argument of [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) is a unique key used to cache the response of the second argument, the querying function. This key can be ignored by directly passing the querying function, the key will be auto-generated. Since the autogenerated key only takes into account the location where `useAsyncData` is invoked, it is recommended to always create your own key to avoid unwanted behavior, like when you are creating your own custom composable wrapping `useAsyncData`. Setting a key can be useful to share the same data between components using [`useNuxtData`](https://nuxt.com/docs/4.x/api/composables/use-nuxt-data) or to [refresh specific data](https://nuxt.com/docs/4.x/api/utils/refresh-nuxt-data#refresh-specific-data). :: ```vue [app/pages/users/[id\\].vue] ``` The `useAsyncData` composable is a great way to wrap and wait for multiple `$fetch` requests to be completed, and then process the results. ```vue ``` ::note `useAsyncData` is for fetching and caching data, not triggering side effects like calling Pinia actions, as this can cause unintended behavior such as repeated executions with nullish values. If you need to trigger side effects, use the [`callOnce`](https://nuxt.com/docs/4.x/api/utils/call-once) utility to do so. ```vue ``` :: ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-async-data"} Read more about `useAsyncData`. :: ## Return Values `useFetch` and `useAsyncData` have the same return values listed below. - `data`: the result of the asynchronous function that is passed in. - `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function. - `clear`: a function that can be used to set `data` to `undefined` (or the value of `options.default()` if provided), set `error` to `undefined`, set `status` to `idle`, and mark any currently pending requests as cancelled. - `error`: an error object if the data fetching failed. - `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`). ::note `data`, `error` and `status` are Vue refs accessible with `.value` in ` ``` You can alternatively use [`useLazyFetch`](https://nuxt.com/docs/4.x/api/composables/use-lazy-fetch) and `useLazyAsyncData` as convenient methods to perform the same. ```vue twoslash ``` ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-lazy-fetch"} Read more about `useLazyFetch`. :: ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-lazy-async-data"} Read more about `useLazyAsyncData`. :: ::video-accordion{title="Watch a video from Vue School on blocking vs. non-blocking (lazy) requests" videoId="1022000555" platform="vimeo"} :: ### Client-only fetching By default, data fetching composables will perform their asynchronous function on both client and server environments. Set the `server` option to `false` to only perform the call on the client-side. On initial load, the data will not be fetched before hydration is complete so you have to handle a pending state, though on subsequent client-side navigation the data will be awaited before loading the page. Combined with the `lazy` option, this can be useful for data that is not needed on the first render (for example, non-SEO sensitive data). ```ts twoslash /* This call is performed before hydration */ const articles = await useFetch('/api/article') /* This call will only be performed on the client */ const { status, data: comments } = useFetch('/api/comments', { lazy: true, server: false, }) ``` The `useFetch` composable is meant to be invoked in setup method or called directly at the top level of a function in lifecycle hooks, otherwise you should use [`$fetch` method](https://nuxt.com/docs/4.x/getting-started/data-fetching#fetch). ### Minimize payload size The `pick` option helps you to minimize the payload size stored in your HTML document by only selecting the fields that you want returned from the composables. ```vue ``` If you need more control or map over several objects, you can use the `transform` function to alter the result of the query. ```ts const { data: mountains } = await useFetch('/api/mountains', { transform: (mountains) => { return mountains.map(mountain => ({ title: mountain.title, description: mountain.description })) }, }) ``` ::note Both `pick` and `transform` don't prevent the unwanted data from being fetched initially. But they will prevent unwanted data from being added to the payload transferred from server to client. :: ::video-accordion{title="Watch a video from Vue School on minimizing payload size" videoId="1026410430" platform="vimeo"} :: ### Caching and refetching #### Keys [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) and [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) use keys to prevent refetching the same data. - [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) generates a key from the URL, the fetch options and the location of the call in your source code. This means two `useFetch` calls with the same URL in different components have **different** keys and will each perform their own request. To share the same data between multiple components, provide the same explicit `key` in the `options` object passed as a last argument. - [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) uses its first argument as a key if it is a string. If the first argument is the handler function that performs the query, then a key that is unique to the location of the `useAsyncData` call in your source code will be generated for you. ::tip To get the cached data by key, you can use [`useNuxtData`](https://nuxt.com/docs/4.x/api/composables/use-nuxt-data) :: ::video-accordion{title="Watch a video from Vue School on caching data with the key option" videoId="1026410044" platform="vimeo"} :: #### Shared State and Option Consistency When multiple components use the same key with `useAsyncData` or `useFetch`, they will share the same `data`, `error` and `status` refs. This ensures consistency across components but requires some options to be consistent. The following options **must be consistent** across all calls with the same key: - `handler` function - `deep` option - `transform` function - `pick` array - `getCachedData` function - `default` value ```ts // ❌ This will trigger a development warning const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: false }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: true }) ``` The following options **can safely differ** without triggering warnings: - `server` - `lazy` - `immediate` - `dedupe` - `watch` ```ts // ✅ This is allowed const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: true }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: false }) ``` If you need independent instances, use different keys: ```ts // These are completely independent instances const { data: users1 } = useAsyncData('users-1', (_nuxtApp, { signal }) => $fetch('/api/users', { signal })) const { data: users2 } = useAsyncData('users-2', (_nuxtApp, { signal }) => $fetch('/api/users', { signal })) ``` #### Reactive Keys You can use computed refs, plain refs or getter functions as keys, allowing for dynamic data fetching that automatically updates when dependencies change: ```ts // Using a computed property as a key const userId = ref('123') const { data: user } = useAsyncData( computed(() => `user-${userId.value}`), () => fetchUser(userId.value), ) // When userId changes, the data will be automatically refetched // and the old data will be cleaned up if no other components use it userId.value = '456' ``` #### Refresh and execute If you want to fetch or refresh data manually, use the `execute` or `refresh` function provided by the composables. ```vue twoslash ``` The `execute` function is an alias for `refresh` that works in exactly the same way but is more semantic for cases when the fetch is [not immediate](https://nuxt.com/docs/4.x/getting-started/data-fetching#not-immediate). ::tip To globally refetch or invalidate cached data, see [`clearNuxtData`](https://nuxt.com/docs/4.x/api/utils/clear-nuxt-data) and [`refreshNuxtData`](https://nuxt.com/docs/4.x/api/utils/refresh-nuxt-data). :: #### Clear If you want to clear the data provided, for whatever reason, without needing to know the specific key to pass to `clearNuxtData`, you can use the `clear` function provided by the composables. ```vue twoslash ``` #### Watch To re-run your fetching function each time other reactive values in your application change, use the `watch` option. You can use it for one or multiple *watchable* elements. ```vue twoslash ``` Note that **watching a reactive value won't change the URL fetched**. For example, this will keep fetching the same initial ID of the user because the URL is constructed at the moment the function is invoked. ```vue ``` If you need to change the URL based on a reactive value, you may want to use a [computed URL](https://nuxt.com/docs/4.x/getting-started/data-fetching#computed-url) instead. When reactive fetch options are provided, they'll be automatically watched and trigger refetches. In some cases, it can be useful to opt-out of this behavior by specifying `watch: false`. ```ts const id = ref(1) // Won't automatically refetch when id changes const { data, execute } = await useFetch('/api/users', { query: { id }, // id is watched by default watch: false, // disables automatic watching of id }) // doesn't trigger refetch id.value = 2 ``` #### Computed URL Sometimes you may need to compute a URL from reactive values, and refresh the data each time these change. Instead of juggling your way around, you can attach each param as a reactive value. Nuxt will automatically use the reactive value and re-fetch each time it changes. ```vue ``` In the case of more complex URL construction, you may use a callback as a [computed getter](https://vuejs.org/guide/essentials/computed) that returns the URL string. Every time a dependency changes, the data will be fetched using the newly constructed URL. Combine this with [not-immediate](https://nuxt.com/docs/4.x/getting-started/data-fetching#not-immediate), and you can wait until the reactive element changes before fetching. ```vue ``` If you need to force a refresh when other reactive values change, you can also [watch other values](https://nuxt.com/docs/4.x/getting-started/data-fetching#watch). ### Not immediate The `useFetch` composable will start fetching data the moment it is invoked. You may prevent this by setting `immediate: false`, for example, to wait for user interaction. With that, you will need both the `status` to handle the fetch lifecycle, and `execute` to start the data fetch. ```vue ``` For finer control, the `status` variable can be: - `idle` when the fetch hasn't started - `pending` when a fetch has started but not yet completed - `error` when the fetch fails - `success` when the fetch is completed successfully ## Passing Headers and Cookies When we call `$fetch` in the browser, user headers like `cookie` will be directly sent to the API. Normally, during server-side-rendering, due to security considerations, the `$fetch` wouldn't include the user's browser cookies, nor pass on cookies from the fetch response. However, when calling `useFetch` with a relative URL on the server, Nuxt will use [`useRequestFetch`](https://nuxt.com/docs/4.x/api/composables/use-request-fetch) to proxy headers and cookies (with the exception of headers not meant to be forwarded, like `host`). ### Pass Cookies From Server-side API Calls on SSR Response If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself. ```ts [app/composables/fetch.ts] import { appendResponseHeader } from 'h3' import type { H3Event } from 'h3' export const fetchWithCookie = async (event: H3Event, url: string) => { /* Get the response from the server endpoint */ const res = await $fetch.raw(url) /* Get the cookies from the response */ const cookies = res.headers.getSetCookie() /* Attach each cookie to our incoming Request */ for (const cookie of cookies) { appendResponseHeader(event, 'set-cookie', cookie) } /* Return the data of the response */ return res._data } ``` ```vue ``` ## Options API Support Nuxt provides a way to perform `asyncData` fetching within the Options API. You must wrap your component definition within `defineNuxtComponent` for this to work. ```vue ``` ::note Using ` ``` ### Custom serializer function To customize the serialization behavior, you can define a `toJSON` function on your returned object. If you define a `toJSON` method, Nuxt will respect the return type of the function and will not try to convert the types. ```ts [server/api/bar.ts] export default defineEventHandler(() => { const data = { createdAt: new Date(), toJSON () { return { createdAt: { year: this.createdAt.getFullYear(), month: this.createdAt.getMonth(), day: this.createdAt.getDate(), }, } }, } return data }) ``` ```vue [app/app.vue] ``` ### Using an alternative serializer Nuxt does not currently support an alternative serializer to `JSON.stringify`. However, you can return your payload as a normal string and utilize the `toJSON` method to maintain type safety. In the example below, we use [superjson](https://github.com/flightcontrolhq/superjson) as our serializer. ```ts [server/api/superjson.ts] import superjson from 'superjson' export default defineEventHandler(() => { const data = { createdAt: new Date(), // Workaround the type conversion toJSON () { return this }, } // Serialize the output to string, using superjson return superjson.stringify(data) as unknown as typeof data }) ``` ```vue [app/app.vue] ``` ## Recipes ### Consuming SSE (Server-Sent Events) via POST request ::tip If you're consuming SSE via GET request, you can use [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) or VueUse composable [`useEventSource`](https://vueuse.org/core/useeventsource/). :: When consuming SSE via POST request, you need to handle the connection manually. Here's how you can do it: ```ts // Make a POST request to the SSE endpoint const response = await $fetch('/chats/ask-ai', { method: 'POST', body: { query: 'Hello AI, how are you?', }, responseType: 'stream', }) // Create a new ReadableStream from the response with TextDecoderStream to get the data as text const reader = response.pipeThrough(new TextDecoderStream()).getReader() // Read the chunk of data as we get it while (true) { const { value, done } = await reader.read() if (done) { break } console.log('Received:', value) } ``` ### Making parallel requests When requests don't rely on each other, you can make them in parallel with `Promise.all()` to boost performance. ```ts const { data } = await useAsyncData((_nuxtApp, { signal }) => { return Promise.all([ $fetch('/api/comments/', { signal }), $fetch('/api/author/12', { signal }), ]) }) const comments = computed(() => data.value?.[0]) const author = computed(() => data.value?.[1]) ``` ::video-accordion{title="Watch a video from Vue School on parallel data fetching" videoId="1024262536" platform="vimeo"} :: # State Management > Nuxt provides powerful state management libraries and the useState composable to create a reactive and SSR-friendly shared state. Nuxt provides the [`useState`](https://nuxt.com/docs/4.x/api/composables/use-state) composable to create a reactive and SSR-friendly shared state across components. [`useState`](https://nuxt.com/docs/4.x/api/composables/use-state) is an SSR-friendly [`ref`](https://vuejs.org/api/reactivity-core#ref) replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key. ::video-accordion{title="Watch a video from Alexander Lichter about why and when to use useState" videoId="mv0WcBABcIk"} :: ::important Because the data inside [`useState`](https://nuxt.com/docs/4.x/api/composables/use-state) will be serialized to JSON, it is important that it does not contain anything that cannot be serialized, such as classes, functions or symbols. :: ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-state"} Read more about `useState` composable. :: ## Best Practices ::warning Never define `const state = ref()` outside of ` ``` ::link-example{to="https://nuxt.com/docs/examples/features/state-management"} :: ::note To globally invalidate cached state, see [`clearNuxtState`](https://nuxt.com/docs/4.x/api/utils/clear-nuxt-state) util. :: ### Initializing State Most of the time, you will want to initialize your state with data that resolves asynchronously. You can use the [`app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app) component with the [`callOnce`](https://nuxt.com/docs/4.x/api/utils/call-once) util to do so. ```vue twoslash [app/app.vue] ``` ::tip This is similar to the [`nuxtServerInit` action](https://nuxt.com/docs/2.x/directory-structure/store#the-nuxtserverinit-action) in Nuxt 2, which allows filling the initial state of your store server-side before rendering the page. :: ::read-more{to="https://nuxt.com/docs/4.x/api/utils/call-once"} :: ### Usage with Pinia In this example, we leverage the [Pinia module](https://nuxt.com/modules/pinia) to create a global store and use it across the app. ::important Make sure to install the Pinia module with `npx nuxt module add pinia` or follow the [module's installation steps](https://pinia.vuejs.org/ssr/nuxt.html#Installation). :: ::code-group ```ts [app/stores/website.ts] export const useWebsiteStore = defineStore('websiteStore', { state: () => ({ name: '', description: '', }), actions: { async fetch () { const infos = await $fetch('https://api.nuxt.com/modules/pinia') this.name = infos.name this.description = infos.description }, }, }) ``` ```vue [app/app.vue] ``` :: ## Advanced Usage ::code-group ```ts [app/composables/locale.ts] import type { Ref } from 'vue' export const useLocale = () => { return useState('locale', () => useDefaultLocale().value) } export const useDefaultLocale = (fallback = 'en-US') => { const locale = ref(fallback) if (import.meta.server) { const reqLocale = useRequestHeaders()['accept-language']?.split(',')[0] if (reqLocale) { locale.value = reqLocale } } else if (import.meta.client) { const navLang = navigator.language if (navLang) { locale.value = navLang } } return locale } export const useLocales = () => { const locale = useLocale() const locales = ref([ 'en-US', 'en-GB', // ..., 'ja-JP-u-ca-japanese', ]) if (!locales.value.includes(locale.value)) { locales.value.unshift(locale.value) } return locales } export const useLocaleDate = (date: Ref | Date, locale = useLocale()) => { return computed(() => new Intl.DateTimeFormat(locale.value, { dateStyle: 'full' }).format(unref(date))) } ``` ```vue [app/app.vue] ``` :: ::link-example{to="https://nuxt.com/docs/examples/advanced/locale"} :: ## Shared State By using [auto-imported composables](https://nuxt.com/docs/4.x/directory-structure/app/composables) we can define global type-safe states and import them across the app. ```ts twoslash [composables/states.ts] export const useColor = () => useState('color', () => 'pink') ``` ```vue [app/app.vue] ``` ::video-accordion{title="Watch a video from Daniel Roe on how to deal with global state and SSR in Nuxt" videoId="dZSNW07sO-A"} :: ## Using Third-Party Libraries Nuxt **used to rely** on the Vuex library to provide global state management. If you are migrating from Nuxt 2, please head to [the migration guide](https://nuxt.com/docs/4.x/migration/configuration#vuex). Nuxt is not opinionated about state management, so feel free to choose the right solution for your needs. There are multiple integrations with the most popular state management libraries, including: - [Pinia](https://nuxt.com/modules/pinia) - the official Vue recommendation - [Harlem](https://nuxt.com/modules/harlem) - immutable global state management - [XState](https://nuxt.com/modules/xstate) - state machine approach with tools for visualizing and testing your state logic # Error Handling > Learn how to catch and handle errors in Nuxt. Nuxt is a full-stack framework, which means there are several sources of unpreventable user runtime errors that can happen in different contexts: - Errors during the Vue rendering lifecycle (SSR & CSR) - Server and client startup errors (SSR + CSR) - Errors during Nitro server lifecycle ([`server/`](https://nuxt.com/docs/4.x/directory-structure/server) directory) - Errors downloading JS chunks ::tip **SSR** stands for **Server-Side Rendering** and **CSR** for **Client-Side Rendering**. :: ## Vue Errors You can hook into Vue errors using [`onErrorCaptured`](https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured). In addition, Nuxt provides a [`vue:error`](https://nuxt.com/docs/4.x/api/advanced/hooks#app-hooks-runtime) hook that will be called if any errors propagate up to the top level. If you are using an error reporting framework, you can provide a global handler through [`vueApp.config.errorHandler`](https://vuejs.org/api/application#app-config-errorhandler). It will receive all Vue errors, even if they are handled. ```ts twoslash [plugins/error-handler.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.config.errorHandler = (error, instance, info) => { // handle error, e.g. report to a service } // Also possible nuxtApp.hook('vue:error', (error, instance, info) => { // handle error, e.g. report to a service }) }) ``` ::note Note that the `vue:error` hook is based on [`onErrorCaptured`](https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured) lifecycle hook. :: ## Startup Errors Nuxt will call the `app:error` hook if there are any errors in starting your Nuxt application. This includes: - running [Nuxt plugins](https://nuxt.com/docs/4.x/directory-structure/app/plugins) - processing `app:created` and `app:beforeMount` hooks - rendering your Vue app to HTML (during SSR) - mounting the app (on client-side), though you should handle this case with `onErrorCaptured` or with `vue:error` - processing the `app:mounted` hook ## Nitro Server Errors You cannot currently define a server-side handler for these errors, but can render an error page, see the [Render an Error Page](https://nuxt.com/docs/4.x/getting-started/error-handling#error-page) section. ## Errors with JS Chunks You might encounter chunk loading errors due to a network connectivity failure or a new deployment (which invalidates your old, hashed JS chunk URLs). Nuxt provides built-in support for handling chunk loading errors by performing a hard reload when a chunk fails to load during route navigation. You can change this behavior by setting `experimental.emitRouteChunkError` to `false` (to disable hooking into these errors at all) or to `manual` if you want to handle them yourself. If you want to handle chunk loading errors manually, you can check out the [automatic implementation](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/plugins/chunk-reload.client.ts) for ideas. ## Error Page ::note When Nuxt encounters a fatal error (any unhandled error on the server, or an error created with `fatal: true` on the client) it will either render a JSON response (if requested with `Accept: application/json` header) or trigger a full-screen error page. With `future.compatibilityVersion: 5`, a page error always triggers the error page, and an error thrown in a server route or server middleware returns a JSON response. :: An error may occur during the server lifecycle when: - processing your Nuxt plugins - rendering your Vue app into HTML - a server API route throws an error It can also occur on the client side when: - processing your Nuxt plugins - before mounting the application (`app:beforeMount` hook) - mounting your app if the error was not handled with `onErrorCaptured` or `vue:error` hook - the Vue app is initialized and mounted in browser (`app:mounted`). ::read-more{to="https://nuxt.com/docs/4.x/api/advanced/hooks"} Discover all the Nuxt lifecycle hooks. :: Customize the default error page by adding `~/error.vue` in the source directory of your application, alongside `app.vue`. ```vue [error.vue] ``` ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/error"} Read more about `error.vue` and its uses. :: For custom errors we highly recommend using `onErrorCaptured` composable that can be called in a page/component setup function or `vue:error` runtime nuxt hook that can be configured in a nuxt plugin. ```ts twoslash [plugins/error-handler.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('vue:error', (err) => { // }) }) ``` When you are ready to remove the error page, you can call the [`clearError`](https://nuxt.com/docs/4.x/api/utils/clear-error) helper function, which takes an optional path to redirect to (for example, if you want to navigate to a 'safe' page). ::important Make sure to check before using anything dependent on Nuxt plugins, such as `$route` or `useRouter`, as if a plugin threw an error, then it won't be re-run until you clear the error. :: ::note Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](https://nuxt.com/docs/4.x/getting-started/error-handling#useerror) in middleware to check if an error is being handled. :: ::note If you are running on Node 16 and you set any cookies when rendering your error page, they will [overwrite cookies previously set](https://github.com/nuxt/nuxt/pull/20585). We recommend using a newer version of Node as Node 16 reached end-of-life in September 2023. :: ## Error Utils ### `useError` ```ts [TS Signature] function useError (): Ref ``` This function will return the global Nuxt error that is being handled. ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-error"} Read more about `useError` composable. :: ### `createError` ```ts [TS Signature] function createError (err: string | { cause, data, message, name, stack, status, statusText, fatal }): Error ``` Create an error object with additional metadata. You can pass a string to be set as the error `message` or an object containing error properties. It is usable in both the Vue and Server portions of your app, and is meant to be thrown. If you throw an error created with `createError`: - on server-side, it will trigger a full-screen error page which you can clear with [`clearError`](https://nuxt.com/docs/4.x/getting-started/error-handling#clearerror). - on client-side, it will throw a non-fatal error for you to handle. If you need to trigger a full-screen error page, then you can do this by setting `fatal: true`. In development, an error `cause` is preserved and exposed to your error page so you can trace the original error; in production, causes are never included in error responses or the error page payload. ```vue twoslash [pages/movies/[slug\\].vue] ``` ::tip The `statusText` property is intended for short, HTTP-compliant status texts (e.g., "Not Found"). It should only contain horizontal tabs, spaces, and visible ASCII characters (`[\t\u0020-\u007E]`). For any detailed descriptions, multi-line messages, or content with non-ASCII characters, you should always use the `message` property instead. :: ::read-more{to="https://nuxt.com/docs/4.x/api/utils/create-error"} Read more about `createError` util. :: ### `showError` ```ts [TS Signature] function showError (err: string | Error | { status, statusText }): Error ``` You can call this function at any point on client-side, or (on server side) directly within middleware, plugins or `setup()` functions. It will trigger a full-screen error page which you can clear with [`clearError`](https://nuxt.com/docs/4.x/getting-started/error-handling#clearerror). It is recommended instead to use `throw createError()`. ::read-more{to="https://nuxt.com/docs/4.x/api/utils/show-error"} Read more about `showError` util. :: ### `clearError` ```ts [TS Signature] function clearError (options?: { redirect?: string }): Promise ``` This function will clear the currently handled Nuxt error. It also takes an optional path to redirect to (for example, if you want to navigate to a 'safe' page). ::read-more{to="https://nuxt.com/docs/4.x/api/utils/clear-error"} Read more about `clearError` util. :: ## Render Error in Component Nuxt also provides a [``](https://nuxt.com/docs/4.x/api/components/nuxt-error-boundary) component that allows you to handle client-side errors within your app, without replacing your entire site with an error page. This component is responsible for handling errors that occur within its default slot. On client-side, it will prevent the error from bubbling up to the top level, and will render the `#error` slot instead. The `#error` slot will receive `error` as a prop. (If you set `error = null` it will trigger re-rendering the default slot; you'll need to ensure that the error is fully resolved first or the error slot will just be rendered a second time.) ::tip If you navigate to another route, the error will be cleared automatically. :: ```vue [app/pages/index.vue] ``` ::link-example{to="https://nuxt.com/docs/examples/advanced/error-handling"} :: # Server > Build full-stack applications with Nuxt's server framework. You can fetch data from your database or another server, create APIs, or even generate static server-side content like a sitemap or a RSS feed - all from a single codebase. ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/server"} :: ## Powered by Nitro ![Server engine](https://nuxt.com/assets/docs/getting-started/server.svg) Nuxt's server is [Nitro](https://github.com/nitrojs/nitro). It was originally created for Nuxt but is now part of [UnJS](https://unjs.io) and open for other frameworks - and can even be used on its own. Using Nitro gives Nuxt superpowers: - Full control of the server-side part of your app - Universal deployment on any provider (many zero-config) - Hybrid rendering Nitro is internally using [h3](https://github.com/h3js/h3), a minimal HTTP framework built for high performance and portability. ::video-accordion{title="Watch a video from Alexander Lichter to understand the responsibilities of Nuxt and Nitro in your application" videoId="DkvgJa-X31k"} :: ## Server Endpoints & Middleware You can easily manage the server-only part of your Nuxt app, from API endpoints to middleware. Both endpoints and middleware can be defined like this: ```ts twoslash [server/api/test.ts] export default defineEventHandler(async (event) => { // ... Do whatever you want here }) ``` And you can directly return `text`, `json`, `html` or even a `stream`. Out-of-the-box, it supports **hot module replacement** and **auto-import** like the other parts of your Nuxt application. ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/server"} :: ## Universal Deployment Nitro offers the ability to deploy your Nuxt app anywhere, from a bare metal server to the edge network, with a start time of just a few milliseconds. That's fast! ::read-more{to="https://nuxt.com/blog/nuxt-on-the-edge"} :: There are more than 15 presets to build your Nuxt app for different cloud providers and servers, including: - [Cloudflare Workers](https://workers.cloudflare.com) - [Netlify Functions](https://www.netlify.com/platform/core/functions/) - [Vercel Cloud](https://vercel.com/home) Or for other runtimes: ::card-group :::card ```yaml [props] icon: i-logos-deno title: Deno to: https://deno.com target: _blank ``` ::: :::card ```yaml [props] icon: i-logos-bun title: Bun to: https://bun.com target: _blank ``` ::: :: ::read-more{to="https://nuxt.com/docs/4.x/getting-started/deployment"} :: ## Hybrid Rendering Nuxt has a powerful feature called `routeRules` which allows you to define a set of rules to customize how each route of your Nuxt app is rendered (and more). Nuxt compiles the rules, and the default server builder (Nitro) applies the server-side ones. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Generated at build time for SEO purpose '/': { prerender: true }, // Cached for 1 hour '/api/*': { cache: { maxAge: 60 * 60 } }, // Redirection to avoid 404 '/old-page': { redirect: { to: '/new-page', statusCode: 302 }, }, // ... }, }) ``` ::read-more{to="https://nuxt.com/docs/4.x/guide/concepts/rendering#hybrid-rendering"} Learn about all the route rules available to customize the rendering mode of your routes. :: In addition, there are some route rules (for example, `ssr`, `appMiddleware`, and `noScripts`) that are Nuxt specific to change the behavior when rendering your pages to HTML. Some route rules (`appMiddleware`, `redirect` and `prerender`) also affect client-side behavior. Nitro is used to build the app for server side rendering, as well as pre-rendering. ::read-more{to="https://nuxt.com/docs/4.x/guide/concepts/rendering"} :: # Layers > Nuxt provides a powerful system that allows you to extend the default files, configs, and much more. One of the core features of Nuxt is the layers and extending support. You can extend a default Nuxt application to reuse components, utils, and configuration. The layers structure is almost identical to a standard Nuxt application which makes them easy to author and maintain. ## Use Cases - Share reusable configuration presets across projects using `nuxt.config` and `app.config` - Create a component library using [`app/components/`](https://nuxt.com/docs/4.x/directory-structure/app/components) directory - Create utility and composable library using [`app/composables/`](https://nuxt.com/docs/4.x/directory-structure/app/composables) and [`app/utils/`](https://nuxt.com/docs/4.x/directory-structure/app/utils) directories - Create Nuxt module presets - Share standard setup across projects - Create Nuxt themes - Enhance code organization by implementing a modular architecture and support Domain-Driven Design (DDD) pattern in large scale projects. ## Usage By default, any layers within your project in the `~~/layers` directory will be automatically registered as layers in your project. ::note Layer auto-registration was introduced in Nuxt v3.12.0. :: In addition, named layer aliases to the `srcDir` of each of these layers will automatically be created. For example, you will be able to access the `~~/layers/test` layer via `#layers/test`. ::note Named layer aliases were introduced in Nuxt v3.16.0. :: In addition, you can extend from a layer by adding the [extends](https://nuxt.com/docs/4.x/api/nuxt-config#extends) property to your [`nuxt.config`](https://nuxt.com/docs/4.x/directory-structure/nuxt-config) file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ // Extend from a local layer '../base', // Extend from an installed npm package '@my-themes/awesome', // Extend from a git repository 'github:my-themes/awesome#v1', ], }) ``` You can also pass an authentication token if you are extending from a private GitHub repository: ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ // per layer configuration ['github:my-themes/private-awesome', { auth: process.env.GITHUB_TOKEN }], ], }) ``` ::note If a branch is not specified, this will clone `main`. :: ::tip You can override a layer's alias by specifying it in the options next to the layer source. ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ [ 'github:my-themes/awesome', { meta: { name: 'my-awesome-theme', }, }, ], ], }) ``` :: Nuxt uses [unjs/c12](https://github.com/unjs/c12) and [unjs/giget](https://github.com/unjs/giget) for extending remote layers. Check the documentation for more information and all available options. ## Layer Priority When using multiple layers, it's important to understand the override order. Layers with **higher priority** override layers with lower priority when they define the same files or components. ### Priority Order From highest to lowest priority: 1. **Your project files** - always have the highest priority 2. **Auto-scanned layers** from `~~/layers` directory - sorted alphabetically (Z has higher priority than A) 3. **Layers in `extends`** config - first entry has higher priority than second ### Practical Example Consider multiple layers defining the same component: ```bash [Directory structure] layers/ 1.base/ app/components/Button.vue # Base button style 2.theme/ app/components/Button.vue # Themed button (overrides base) app/ components/Button.vue # Project button (overrides all layers) ``` In this case: - If only layers exist, `2.theme/Button.vue` is used (higher alphabetically) - If `app/components/Button.vue` exists in your project, it overrides all layers ### Controlling Priority You can prefix layer directories with numbers to control the order: ```bash [Directory structure] layers/ 1.base/ # Lowest priority 2.features/ # Medium priority 3.admin/ # Highest priority (among layers) ``` ::tip This pattern is useful for creating base layers with defaults that can be progressively overridden by more specific layers. :: Alternatively, you can control the order of your `~~/layers` directories from `nuxt.config` by referencing them in [`extends`](https://nuxt.com/docs/4.x/api/nuxt-config#extends), without renaming your directories. The entries are ordered like any other `extends` layer, with the **first entry having the highest priority**: ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ '~~/layers/admin', // highest priority '~~/layers/features', '~~/layers/base', // lowest priority (among the listed layers) ], }) ``` Both `~~/...` (recommended) and `~/...` alias forms as well as relative paths (`./layers/admin`) are supported. Any layer in `~~/layers` that you do not list keeps its alphabetical auto-scan order, ranked below the layers you list in `extends`. ### When to Use Each - **`~~/layers` directory** - Use for local layers that are part of your project - **`extends`** - Use for external dependencies (npm packages, remote repositories) or layers outside your project directory ### Full Example with `extends` ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ '../base', // Local layer outside project '@my-themes/awesome', // NPM package 'github:my-themes/awesome#v1', // Remote repository ], }) ``` If you also have `~~/layers/custom`, the priority order is: - Your project files (highest) - `~~/layers/custom` - `../base` - `@my-themes/awesome` - `github:my-themes/awesome#v1` (lowest) ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/layers"} Learn about the **layers/ directory** to organize and share reusable code, components, composables, and configurations across your Nuxt application. :: ::read-more{to="https://nuxt.com/docs/4.x/guide/going-further/layers"} Read more about layers in the **Layer Author Guide**. :: ::video-accordion{title="Watch a video from Learn Vue about Nuxt Layers" videoId="lnFCM7c9f7I"} :: ::video-accordion{title="Watch a video from Alexander Lichter about Nuxt Layers" videoId="fr5yo3aVkfA"} :: ## Example ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: Content Wind to: https://github.com/Atinux/content-wind target: _blank ``` A lightweight Nuxt theme to build a Markdown driven website. Powered by Nuxt Content, TailwindCSS and Iconify. ::: :: # Prerendering > Nuxt allows pages to be statically rendered at build time to improve certain performance or SEO metrics Nuxt allows for select pages from your application to be rendered at build time. Nuxt will serve the prebuilt pages when requested instead of generating them on the fly. ::read-more{title="Nuxt rendering modes" to="https://nuxt.com/docs/4.x/guide/concepts/rendering"} :: ## Crawl-based Pre-rendering Use the [`nuxt generate` command](https://nuxt.com/docs/4.x/api/commands/generate) to build and pre-render your application using the [Nitro](https://nuxt.com/docs/4.x/guide/concepts/server-engine) crawler. This command is similar to `nuxt build` with the `nitro.static` option set to `true`, or running `nuxt build --prerender`. This will build your site, stand up a nuxt instance, and, by default, prerender the root page `/` along with any of your site's pages it links to, any of your site's pages they link to, and so on. ::code-group{sync="pm"} ```bash [npm] npx nuxt generate ``` ```bash [yarn] yarn nuxt generate ``` ```bash [pnpm] pnpm nuxt generate ``` ```bash [bun] bun x nuxt generate ``` ```bash [deno] deno x nuxt generate ``` :: You can now deploy the `.output/public` directory to any static hosting service or preview it locally with `npx serve .output/public`. Static and prerender builds also emit `200.html` and `404.html` SPA fallbacks. See [What are 200.html and 404.html?](https://nuxt.com/docs/4.x/guide/concepts/rendering#what-are-200html-and-404html). How the Nitro crawler works: 1. Load the HTML of your application's root route (`/`), any non-dynamic pages in your `~/pages` directory, and any other routes in the `prerender.routes` array. 2. Save the HTML and `_payload.json` to the `~/.output/public/` directory to be served statically. 3. Find all anchor tags (``) in the HTML to navigate to other routes. 4. Repeat steps 1-3 for each anchor tag found until there are no more anchor tags to crawl. This is important to understand since pages that are not linked to a discoverable page can't be pre-rendered automatically. ::read-more{to="https://nuxt.com/docs/4.x/api/commands/generate"} Read more about the `nuxt generate` command. :: ### Selective Pre-rendering You can manually specify routes that Nuxt will fetch and pre-render during the build or ignore routes that you don't want to pre-render like `/dynamic` in the `nuxt.config` file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ prerender: { routes: ['/user/1', '/user/2'], ignore: ['/dynamic'], }, }) ``` You can combine this with the `crawlLinks` option to pre-render a set of routes that the crawler can't discover like your `/sitemap.xml` or `/robots.txt`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ prerender: { crawlLinks: true, routes: ['/sitemap.xml', '/robots.txt'], }, }) ``` The top-level `prerender` option applies whichever `server.builder` you use. Builder-specific options, such as Nitro's `concurrency` or `failOnError`, can be set in `nitro.prerender`. ::read-more{to="https://nitro.build/config#prerender"} Read more about pre-rendering in the Nitro documentation. :: Lastly, you can manually configure this using routeRules. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Set prerender to true to configure it to be prerendered '/rss.xml': { prerender: true }, // Set it to false to configure it to be skipped for prerendering '/this-DOES-NOT-get-prerendered': { prerender: false }, // Everything under /blog gets prerendered as long as it // is linked to from another page '/blog/**': { prerender: true }, }, }) ``` ::read-more{to="https://nitro.build/config#routerules"} Read more about Nitro's `routeRules` configuration. :: As a shorthand, you can also configure this in a page file using [`defineRouteRules`](https://nuxt.com/docs/4.x/api/utils/define-route-rules). ::read-more{to="https://nuxt.com/docs/4.x/guide/going-further/experimental-features#inlinerouterules" icon="i-lucide-star"} This feature is experimental and in order to use it you must enable the `experimental.inlineRouteRules` option in your `nuxt.config`. :: ```vue [app/pages/index.vue] ``` This will be translated to: ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/': { prerender: true }, }, }) ``` ## Payload Extraction When Nuxt renders a page on the server, it serializes the results of your data fetching ([`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch)) and app state ([`useState`](https://nuxt.com/docs/4.x/api/composables/use-state)) into a payload so the client can hydrate without re-fetching. With payload extraction enabled, Nuxt also writes this payload to a `_payload.json` file alongside the route's HTML: - Prerendered routes generate their payload file at build time. - Routes using [ISR or SWR caching](https://nuxt.com/docs/4.x/guide/concepts/rendering#hybrid-rendering) generate their payload file when the route is first rendered, even on a hybrid (non-static) site. During client-side navigation, Nuxt fetches the `_payload.json` file for the destination route and reuses the extracted data instead of running the data fetching again in the browser. You can control this behavior with the [`experimental.payloadExtraction`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#payloadextraction) option: - `'client'` - The payload is inlined in the HTML for the initial render and extracted to `_payload.json` files for client-side navigation. There is no extra network request on first load. - `true` - The payload is extracted to a separate `_payload.json` file for both the initial render and client-side navigation. The HTML is smaller and the payload file can be cached by a CDN, at the cost of one extra request on first load. - `false` - Payload extraction is disabled. The payload is always inlined in the HTML and no `_payload.json` files are generated. The default is `true`, or `'client'` when `compatibilityVersion: 5` is set. It is forced to `false` when `ssr: false` is set. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { payloadExtraction: 'client', }, }) ``` A few practical consequences to be aware of: - On a fully static site, client-side navigation reuses the data captured at build time, so data can be stale until the next rebuild. - For ISR/SWR routes, CDNs can cache payload files alongside the HTML, improving client-side navigation performance for cached routes. Dynamic routes like `pages/[...slug].vue` can opt in with glob patterns such as `'/**': { isr: true }`. - Payloads are serialized with [devalue](https://github.com/Rich-Harris/devalue), so custom types (such as class instances) need payload plugins with custom reducers and revivers to survive the round trip. ::read-more{title="Payload reducers and revivers" to="https://nuxt.com/docs/4.x/api/composables/use-nuxt-app#payload"} :: ## Runtime Prerender Configuration ### `prerenderRoutes` You can use this at runtime within a [Nuxt context](https://nuxt.com/docs/4.x/guide/going-further/nuxt-app#the-nuxt-context) to add more routes for Nitro to prerender. ```vue [app/pages/index.vue] ``` ::read-more{title="prerenderRoutes" to="https://nuxt.com/docs/4.x/api/utils/prerender-routes"} :: ### `prerender:routes` Nuxt hook This is called before prerendering for additional routes to be registered. ```ts [nuxt.config.ts] export default defineNuxtConfig({ hooks: { async 'prerender:routes' (ctx) { const { pages } = await fetch('https://api.some-cms.com/pages').then( res => res.json(), ) for (const page of pages) { ctx.routes.add(`/${page.name}`) } }, }, }) ``` ### `prerender:generate` Nitro hook This is called for each route during prerendering. You can use this for fine-grained handling of each route that gets prerendered. ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { hooks: { 'prerender:generate' (route) { if (route.route?.includes('private')) { route.skip = true } }, }, }, }) ``` # Deployment > Learn how to deploy your Nuxt application to any hosting provider. A Nuxt application can be deployed on a Node.js server, pre-rendered for static hosting, or deployed to serverless or edge (CDN) environments. ::tip If you are looking for a list of cloud providers that support Nuxt, see the [Hosting providers](https://nuxt.com/deploy) section. :: ## Node.js Server Discover the Node.js server preset with Nitro to deploy on any Node hosting. - **Default output format** if none is specified or auto-detected
- Loads only the required chunks to render the request for optimal cold start timing
- Useful for deploying Nuxt apps to any Node.js hosting ### Entry Point When running `nuxt build` with the Node server preset, the result will be an entry point that launches a ready-to-run Node server. ```bash [Terminal] NODE_ENV=production node .output/server/index.mjs ``` This will launch your production Nuxt server that listens on port 3000 by default. ::important Set `NODE_ENV=production` when running the server. Some dependencies (notably Vue Router) only strip development-only warnings when this is set, so leaving it unset can flood your logs with messages like `[Vue Router warn]: No match found for location with path …` on unmatched routes. :: It respects the following runtime environment variables: - `NITRO_PORT` or `PORT` (defaults to `3000`) - `NITRO_HOST` or `HOST` (defaults to `'0.0.0.0'`) - `NITRO_SSL_CERT` and `NITRO_SSL_KEY` - if both are present, this will launch the server in HTTPS mode. In the vast majority of cases, this should not be used other than for testing, and the Nitro server should be run behind a reverse proxy like nginx or Cloudflare which terminates SSL. ### Serving the Same Build at Multiple Paths For a normal subpath deployment, set [`app.baseURL`](https://nuxt.com/docs/4.x/api/nuxt-config#app-baseurl) or the `NUXT_APP_BASE_URL` environment variable. If a reverse proxy deliberately exposes the same rendered page at multiple public paths, Nuxt may replace the browser URL with the path used for server rendering during hydration. You can keep the browser URL by removing the rendered path from the payload in a server plugin: ```ts [app/plugins/preserve-proxy-url.server.ts] export default defineNuxtPlugin((nuxtApp) => { delete nuxtApp.payload.path }) ``` Use this only when the proxy already handles assets and routing for every public path. Without the rendered path, Nuxt cannot correct a genuine mismatch between the requested URL and the server-rendered route. ### PM2 [PM2](https://pm2.keymetrics.io/) (Process Manager 2) is a fast and easy solution for hosting your Nuxt application on your server or VM. To use `pm2`, use an `ecosystem.config.cjs`: ```ts [ecosystem.config.cjs] module.exports = { apps: [ { name: 'NuxtAppName', port: '3000', exec_mode: 'cluster', instances: 'max', script: './.output/server/index.mjs', env: { NODE_ENV: 'production', }, }, ], } ``` ### Cluster Mode You can use `NITRO_PRESET=node_cluster` in order to leverage multi-process performance using Node.js [cluster](https://nodejs.org/dist/latest/docs/api/cluster.html) module. By default, the workload gets distributed to the workers with the round robin strategy. ### Learn More ::read-more{to="https://nitro.build/deploy/runtimes/node" title="the Nitro documentation for node-server preset"} :: ::video-accordion{title="Watch Daniel Roe's short video on the topic" videoId="0x1H6K5yOfs"} :: ## Static Hosting There are two ways to deploy a Nuxt application to any static hosting services: - Static site generation (SSG) with `ssr: true` pre-renders routes of your application at build time. (This is the default behavior when running `nuxt generate`.) It will also generate `/200.html` and `/404.html` single-page app fallback pages, which can render dynamic routes or 404 errors on the client (though you may need to configure this on your static host). See [What are 200.html and 404.html?](https://nuxt.com/docs/4.x/guide/concepts/rendering#client-side-rendering-deploying-a-static-client-rendered-app-what-are-200html-and-404html). - Alternatively, you can prerender your site with `ssr: false` (static single-page app). This will produce HTML pages with an empty `
` where your Vue app would normally be rendered. You will lose many SEO benefits of prerendering your site, so it is suggested instead to use [``](https://nuxt.com/docs/4.x/api/components/client-only) to wrap the portions of your site that cannot be server rendered (if any). Prerendered routes also emit `_payload.json` files with the data captured at build time, which Nuxt reuses during client-side navigation. Read more about [payload extraction](https://nuxt.com/docs/4.x/getting-started/prerendering#payload-extraction). ### Static Fallback Pages Nuxt can generate two fallback pages for static hosts: - `200.html` is the single-page app fallback. Configure your host to serve it for unmatched routes when you want client-side routing to handle the URL. - `404.html` is the not-found fallback. Configure your host to serve it for routes that should keep a 404 status. `nuxt generate` and `nuxt build --prerender` generate these files automatically. If you use `nuxt build` with route rules to prerender selected routes, add the fallback page explicitly: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/200.html': { prerender: true }, }, }) ``` By default both fallbacks are empty shells. Set [`experimental.prerenderErrorPages`](https://nuxt.com/docs/4.x/guide/concepts/rendering#client-side-rendering-deploying-a-static-client-rendered-app-server-rendering-the-error-page) to server-render your `error.vue` into `404.html` at build time. Some providers use `200.html`, some use `404.html`, and some let you configure both. Check your hosting provider's static fallback or rewrite settings after deployment. ::read-more{title="Nuxt prerendering" to="https://nuxt.com/docs/4.x/getting-started/prerendering"} :: ### Client-side Only Rendering If you don't want to pre-render your routes, another way of using static hosting is to set the `ssr` property to `false` in the `nuxt.config` file. The `nuxt generate` command will then output an `.output/public/index.html` entrypoint and JavaScript bundles like a classic client-side Vue.js application. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ ssr: false, }) ``` ## Hosting Providers Nuxt can be deployed to several cloud providers with a minimal amount of configuration: ::read-more{to="https://nuxt.com/deploy"} :: ## Presets In addition to Node.js servers and static hosting services, a Nuxt project can be deployed with several well-tested presets and a minimal amount of configuration. You can explicitly set the desired preset in the [`nuxt.config.ts`](https://nuxt.com/docs/4.x/directory-structure/nuxt-config) file: ```ts twoslash [nuxt.config.ts] // @errors: 2353 export default defineNuxtConfig({ nitro: { preset: 'node-server', }, }) ``` ... or use the `NITRO_PRESET` environment variable when running `nuxt build`: ```bash [Terminal] NITRO_PRESET=node-server nuxt build ``` 🔎 Check [the Nitro deployment](https://nitro.build/deploy) for all possible deployment presets and providers. ## CDN Proxy In most cases, Nuxt can work with third-party content that is not generated or created by Nuxt itself. But sometimes such content can cause problems, especially Cloudflare's "Minification and Security Options". Accordingly, you should make sure that the following options are unchecked / disabled in Cloudflare. Otherwise, unnecessary re-rendering or hydration errors could impact your production application. 1. Speed > Settings > Content Optimization > Disable "Rocket Loader™" 2. Security > Settings > Disable "Email Address Obfuscation" With these settings, you can be sure that Cloudflare won't inject scripts into your Nuxt application that may cause unwanted side effects. ::tip Their location on the Cloudflare dashboard sometimes changes so don't hesitate to look around. :: # Testing > How to test your Nuxt application. ::tip If you are a module author, you can find more specific information in the [Module Author's guide](https://nuxt.com/docs/4.x/guide/modules/testing). :: Nuxt offers first-class support for end-to-end and unit testing of your Nuxt application via `@nuxt/test-utils`, a library of test utilities and configuration that currently powers the [tests we use on Nuxt itself](https://github.com/nuxt/nuxt/tree/main/test) and tests throughout the module ecosystem. ::video-accordion{title="Watch a video from Alexander Lichter about getting started with @nuxt/test-utils" videoId="yGzwk9xi9gU"} :: ## Installation In order to allow you to manage your other testing dependencies, `@nuxt/test-utils` ships with various optional peer dependencies. For example: - you can choose between `happy-dom` and `jsdom` for a runtime Nuxt environment - you can choose between `vitest`, `cucumber`, `jest` and `playwright` for end-to-end test runners - `playwright-core` is only required if you wish to use the built-in browser testing utilities (and are not using `@playwright/test` as your test runner) ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [yarn] yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [pnpm] pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [bun] bun add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` :: ## Test Environments `@nuxt/test-utils` provides helpers for two different test environments: - **Nuxt environment**: tests run inside a Nuxt app in a [`happy-dom`](https://github.com/capricorn86/happy-dom) or [`jsdom`](https://github.com/jsdom/jsdom) environment, with access to auto-imports, plugins and injections. Helpers from `@nuxt/test-utils/runtime` (such as `mountSuspended` and `mockNuxtImport`) require this environment, which is currently only available with Vitest. - **Node environment**: tests run in plain Node. End-to-end tests using `@nuxt/test-utils/e2e` require this environment, as they test your Nuxt app from the outside, over HTTP or through a browser. Unit tests that do not need Nuxt can also run here. A test file can only run in one of these environments, so `@nuxt/test-utils/runtime` and `@nuxt/test-utils/e2e` cannot be used in the same file. With Vitest, you can use [projects](https://vitest.dev/guide/projects.html#test-projects) to make sure that each test file runs in the right environment, as shown in the [setup](#setup) below. ## Unit Testing We currently ship an environment for unit testing code that needs a [Nuxt](https://nuxt.com) runtime environment. It currently *only has support for `vitest`* (although contribution to add other runtimes would be welcome). ### Setup 1. Add `@nuxt/test-utils/module` to your `nuxt.config` file (optional). It adds a Vitest integration to your Nuxt DevTools which supports running your unit tests in development. ```ts twoslash export default defineNuxtConfig({ modules: [ '@nuxt/test-utils/module', ], }) ``` 2. Create a `vitest.config.ts` with the following content: ```ts twoslash import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' export default defineConfig({ test: { projects: [ { test: { name: 'unit', include: ['test/unit/*.{test,spec}.ts'], environment: 'node', }, }, { test: { name: 'e2e', include: ['test/e2e/*.{test,spec}.ts'], environment: 'node', }, }, await defineVitestProject({ test: { name: 'nuxt', include: ['test/nuxt/*.{test,spec}.ts'], environment: 'nuxt', }, }), ], }, }) ``` ::note `defineVitestProject` is only for Nuxt-environment tests. End-to-end tests should be configured as a regular `test.environment: 'node'` project. :: 3. If your Nuxt-environment tests live outside `test/nuxt/`, see [TypeScript Support in Tests](#typescript-support-in-tests) to add them to the TypeScript context. ::tip When importing `@nuxt/test-utils` in your vitest config, it is necessary to have `"type": "module"` specified in your `package.json` or rename your vitest config file appropriately. > i.e., `vitest.config.m{ts,js}`. :: ::tip It is possible to set environment variables for testing by using the `.env.test` file. :: ### Nuxt Environment Options You can pass Nuxt-specific options to a `defineVitestProject` project with `test.environmentOptions.nuxt`: ```ts twoslash import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' export default defineConfig({ test: { projects: [ await defineVitestProject({ test: { name: 'nuxt', include: ['test/nuxt/*.{test,spec}.ts'], environmentOptions: { nuxt: { rootDir: fileURLToPath(new URL('./playground', import.meta.url)), domEnvironment: 'happy-dom', // 'happy-dom' (default) or 'jsdom' overrides: { // other Nuxt config you want to pass }, }, }, }, }), ], }, }) ``` ### Organizing Your Tests With the project-based setup, each project's `include` glob determines which environment a test file runs in: - **Unit tests** in `test/unit/` run in the Node environment. - **Nuxt tests** in `test/nuxt/` run in the Nuxt environment. - **End-to-end tests** in `test/e2e/` run in the Node environment. ```bash [Directory structure] test/ ├── e2e/ │ └── ssr.test.ts ├── nuxt/ │ ├── components.test.ts │ └── composables.test.ts ├── unit/ │ └── utils.test.ts ``` If you prefer to keep tests next to your source files, you can use file suffixes such as `*.unit.spec.ts`, `*.nuxt.spec.ts` and `*.e2e.spec.ts` instead, with matching `include` globs (for example, `include: ['**/*.nuxt.{test,spec}.ts']`). See the [co-location example](https://github.com/nuxt/test-utils/tree/main/examples/app-vitest-co-location) for a full configuration. You can of course opt for any test structure, but each test file should match exactly one project. In particular, keeping Nuxt-environment tests separate from end-to-end tests is important for test stability. ::warning Use your projects' `include` globs to choose the environment for a test file, rather than a `// @vitest-environment` comment. The comment changes the environment without adding or removing the Nuxt Vite plugins configured by `defineVitestProject`. :: #### TypeScript Support in Tests By default, test files in `test/nuxt/` and `tests/nuxt/` directories are included in the [Nuxt app TypeScript context](https://nuxt.com/docs/4.x/guide/concepts/typescript#how-nuxt-uses-project-references). That means they will recognize Nuxt aliases (like `~/`, `@/`, `#imports`) and TypeScript will be aware of auto-imports that work in your Nuxt app. ::tip This matches the recommended structure where only tests that need the Nuxt runtime environment are placed in these directories. Unit tests in other directories like `test/unit/` can be added manually if needed. :: ##### Adding other test directories If you have tests in other directories that you will be running in the Nuxt Vitest environment, you can include them in the Nuxt app TypeScript context by adding them to your configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ typescript: { tsConfig: { include: [ // this path is relative to the generated .nuxt/tsconfig.json '../test/other-nuxt-context/**/*', ], }, }, }) ``` ::important Unit tests should not depend on Nuxt runtime features like auto-imports or composables. Only add TypeScript path alias support if your tests import from your source files (e.g., `~/utils/helpers`), not for Nuxt-specific features. :: #### Running Tests With the project setup, you can run different test suites: ```bash # Run all tests npx vitest # Run only unit tests npx vitest --project unit # Run only Nuxt tests npx vitest --project nuxt # Run only end-to-end tests npx vitest --project e2e # Run tests in watch mode npx vitest --watch ``` ::warning When you run your tests within the Nuxt environment, they will be running in a [`happy-dom`](https://github.com/capricorn86/happy-dom) or [`jsdom`](https://github.com/jsdom/jsdom) environment. Before your tests run, a global Nuxt app will be initialized (including, for example, running any plugins or code you've defined in your `app.vue`). This means you should take particular care not to mutate the global state in your tests (or, if you need to, to reset it afterwards). :: ### 🎭 Built-In Mocks `@nuxt/test-utils` provides some built-in mocks for the DOM environment. These are only available in the Nuxt environment. #### `intersectionObserver` Default `true`, creates a dummy class without any functionality for the IntersectionObserver API #### `indexedDB` Default `false`, uses [`fake-indexeddb`](https://github.com/dumbmatter/fakeIndexedDB) to create a functional mock of the IndexedDB API These can be configured in the [Nuxt environment options](#nuxt-environment-options) of your `vitest.config.ts` file: ```ts twoslash import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' export default defineConfig({ test: { projects: [ await defineVitestProject({ test: { name: 'nuxt', include: ['test/nuxt/*.{test,spec}.ts'], environmentOptions: { nuxt: { mock: { intersectionObserver: true, indexedDb: true, }, }, }, }, }), ], }, }) ``` ### 🛠️ Helpers `@nuxt/test-utils` provides a number of helpers to make testing Nuxt apps easier. #### `mountSuspended` `mountSuspended` allows you to mount any Vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins. ::note Under the hood, `mountSuspended` wraps `mount` from `@vue/test-utils`, so you can check out [the Vue Test Utils documentation](https://test-utils.vuejs.org/guide/) for more on the options you can pass, and how to use this utility. :: For example: ```ts twoslash [tests/components/SomeComponents.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- import { expect, it } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' it('can mount some component', async () => { const component = await mountSuspended(SomeComponent) expect(component.text()).toMatchInlineSnapshot( '"This is an auto-imported component"', ) }) ``` ```ts twoslash [tests/App.nuxt.spec.ts] import { expect, it } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' import App from '~/app.vue' it('can also mount an app', async () => { const component = await mountSuspended(App, { route: '/test' }) expect(component.html()).toMatchInlineSnapshot(` "
This is an auto-imported component
I am a global component
/
Test link " `) }) ``` If you need to spy on component setup state, you can set the `spy` option to `true` and access the return value of the setup via `setupState`. ```ts twoslash [tests/components/SomeComponents.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- import { expect, it, vi } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' it('can spy on setup state', async () => { const component = await mountSuspended(SomeComponent, { spy: true }) vi.mocked(component.setupState.someMethod).mockImplementation(() => 'mocked') }) ``` The options object accepts `@vue/test-utils` mount options and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). - `spy`: enable spy component setup state (default `false`). The return object includes the `@vue/test-utils` mount result and the following properties: - `setupState`: the return value of the component setup. #### `renderSuspended` `renderSuspended` allows you to render any Vue component within the Nuxt environment using `@testing-library/vue`, allowing async setup and access to injections from your Nuxt plugins. This should be used together with utilities from Testing Library, e.g. `screen` and `fireEvent`. Install [@testing-library/vue](https://testing-library.com/docs/vue-testing-library/intro/) in your project to use these. Additionally, Testing Library also relies on testing globals for cleanup. You should turn these on in your [Vitest config](https://vitest.dev/config/globals). The passed in component will be rendered inside a `
`. Examples: ```ts twoslash [tests/components/SomeComponents.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- import { expect, it } from 'vitest' import { renderSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' import { screen } from '@testing-library/vue' it('can render some component', async () => { await renderSuspended(SomeComponent) expect(screen.getByText('This is an auto-imported component')).toBeDefined() }) ``` ~~~ts twoslash [tests/App.nuxt.spec.ts] import { expect, it } from 'vitest' import { renderSuspended } from '@nuxt/test-utils/runtime' import App from '~/app.vue' it('can also render an app', async () => { const html = await renderSuspended(App, { route: '/test' }) expect(html).toMatchInlineSnapshot(` "
This is an auto-imported component
I am a global component
Index page
Test link
" `) }) ~~~ The options object accepts `@testing-library/vue` render options and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). - `spy`: enable spy component setup state (default `false`). See the [`mountSuspended`](#mountsuspended) example above. The return object includes the `@testing-library/vue` render result and the following properties: - `setupState`: the return value of the component setup. #### `mockNuxtImport` `mockNuxtImport` allows you to mock Nuxt's auto import functionality. For example, to mock `useState`, you can do so like this: ```ts twoslash import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', () => { return () => { return { value: 'mocked storage' } } }) // your tests here ``` You can explicitly type the mock for type safety, and use the original implementation passed to the factory function when mocking complex functionality. ```ts twoslash [test/nuxt/import.test.ts] import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', (original) => { return (...args) => { return { ...original('some-key'), value: 'mocked state' } } }) // or specify the target to mock mockNuxtImport(useState, (original) => { return (...args) => { return { ...original('some-key'), value: 'mocked state' } } }) // your tests here ``` ::note `mockNuxtImport` can only be used once per mocked import per test file. It is actually a macro that gets transformed to `vi.mock` and `vi.mock` is hoisted, as described [in the Vitest docs](https://vitest.dev/api/vi#vi-mock). :: If you need to mock a Nuxt import and provide different implementations between tests, you can do it by creating and exposing your mocks using [`vi.hoisted`](https://vitest.dev/api/vi#vi-hoisted), and then use those mocks in `mockNuxtImport`. You then have access to the mocked imports, and can change the implementation between tests. Be careful to [restore mocks](https://vitest.dev/api/mock#mockrestore) before or after each test to undo mock state changes between runs. ```ts twoslash import { vi } from 'vitest' import { mockNuxtImport } from '@nuxt/test-utils/runtime' const { useStateMock } = vi.hoisted(() => { return { useStateMock: vi.fn(() => { return { value: 'mocked storage' } }), } }) mockNuxtImport('useState', () => { return useStateMock }) // Then, inside a test useStateMock.mockImplementation(() => { return { value: 'something else' } }) ``` If you need to mock behavior only inside a test, you can also use the following approach. ```ts twoslash import { beforeEach, vi } from 'vitest' import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport(useRoute, original => vi.fn(original)) beforeEach(() => { vi.resetAllMocks() }) // Then, inside a test const useRouteOriginal = vi.mocked(useRoute).getMockImplementation()! vi.mocked(useRoute).mockImplementation( (...args) => ({ ...useRouteOriginal(...args), path: '/mocked' }), ) ``` #### `mockComponent` `mockComponent` allows you to mock Nuxt's component. The first argument can be the component name in PascalCase, or the relative path of the component. The second argument is a factory function that returns the mocked component. For example, to mock `MyComponent`, you can: ```ts twoslash import { mockComponent } from '@nuxt/test-utils/runtime' mockComponent('MyComponent', { props: { value: String, }, setup (props) { // ... }, }) // relative path or alias also works mockComponent('~/components/my-component.vue', () => { // or a factory function return defineComponent({ setup (props) { // ... }, }) }) // or you can use SFC for redirecting to a mock component mockComponent('MyComponent', () => import('./MockComponent.vue')) // your tests here ``` > **Note**: You can't reference local variables in the factory function since they are hoisted. If you need to access Vue APIs or other variables, you need to import them in your factory function. ```ts twoslash import { mockComponent } from '@nuxt/test-utils/runtime' mockComponent('MyComponent', async () => { const { ref, h } = await import('vue') return defineComponent({ setup (props) { const counter = ref(0) return () => h('div', null, counter.value) }, }) }) ``` #### `registerEndpoint` `registerEndpoint` allows you create Nitro endpoint that returns mocked data. It can come in handy if you want to test a component that makes requests to API to display some data. The first argument is the endpoint name (e.g. `/test/`). The second argument is a factory function that returns the mocked data. For example, to mock `/test/` endpoint, you can do: ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' registerEndpoint('/test/', () => ({ test: 'test-field', })) ``` By default, your request will be made using the `GET` method. You may use another method by setting an object as the second argument instead of a function. ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' registerEndpoint('/test/', { method: 'POST', handler: () => ({ test: 'test-field' }), }) ``` This object accepts the following properties: - `handler`: the event handler function - `method`: (optional) HTTP method to match (e.g., 'GET', 'POST') - `once`: (optional) if true, the handler will only be used for the first matching request and then automatically removed > **Note**: If your requests in a component go to an external API, you can use `baseURL` and then make it empty using [Nuxt Environment Override Config](https://nuxt.com/docs/4.x/getting-started/configuration#environment-overrides) (`$test`) so all your requests will go to Nitro server. #### Conflict with End-To-End Testing `@nuxt/test-utils/runtime` and `@nuxt/test-utils/e2e` need to run in different [test environments](#test-environments) and so can't be used in the same file. If you would like to use both the end-to-end and unit testing functionality of `@nuxt/test-utils`, split your tests into separate files and make sure each file matches only one project: Nuxt-environment tests should be matched by a `defineVitestProject` project, and end-to-end tests by a regular `environment: 'node'` project. See [Organizing Your Tests](#organizing-your-tests) for folder- and suffix-based layouts. `app.nuxt.spec.ts` ```ts twoslash import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', () => { return () => { return { value: 'mocked storage' } } }) ``` `app.e2e.spec.ts` ```ts twoslash import { $fetch, setup } from '@nuxt/test-utils/e2e' await setup({ setupTimeout: 10000, }) // ... ``` ### Using `@vue/test-utils` If you prefer to use `@vue/test-utils` on its own for unit testing in Nuxt, and you are only testing components which do not rely on Nuxt composables, auto-imports or context, you can follow these steps to set it up. 1. Install the needed dependencies ::code-group{sync="pm"} ```bash [npm] npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [yarn] yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [pnpm] pnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [bun] bun add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` :: 2. Create a `vitest.config.ts` with the following content: ```ts import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'happy-dom', }, }) ``` 3. Add a new command for test in your `package.json` ```json "scripts": { "build": "nuxt build", "dev": "nuxt dev", ... "test": "vitest" }, ``` 4. Create a simple `` component `app/components/HelloWorld.vue` with the following content: ```vue ``` 5. Create a simple unit test for this newly created component `~/components/HelloWorld.spec.ts` ```ts twoslash import { describe, expect, it } from 'vitest' import { mount } from '@vue/test-utils' import HelloWorld from './HelloWorld.vue' describe('HelloWorld', () => { it('component renders Hello world properly', () => { const wrapper = mount(HelloWorld) expect(wrapper.text()).toContain('Hello world') }) }) ``` 6. Run vitest command ::code-group{sync="pm"} ```bash [npm] npm run test ``` ```bash [yarn] yarn test ``` ```bash [pnpm] pnpm run test ``` ```bash [bun] bun run test ``` :: Congratulations, you're all set to start unit testing with `@vue/test-utils` in Nuxt! Happy testing! ### Vitest Browser Mode `@nuxt/test-utils` provides helpers for testing in [Vitest Browser Mode](https://vitest.dev/guide/browser/) via `@nuxt/test-utils/browser`. #### Setup 1. Install Vitest browser packages and a browser provider: ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @vitest/browser-playwright ``` ```bash [yarn] yarn add --dev @vitest/browser-playwright ``` ```bash [pnpm] pnpm add -D @vitest/browser-playwright ``` ```bash [bun] bun add --dev @vitest/browser-playwright ``` :: 2. Configure browser mode in your `vitest.config.ts`: ```ts twoslash // @errors: 2307 // ---cut--- import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' import { playwright } from '@vitest/browser-playwright' export default defineConfig({ test: { projects: [ await defineVitestProject({ test: { name: 'browser', include: ['test/browser/**/*.{test,spec}.ts'], browser: { enabled: true, provider: playwright(), instances: [{ browser: 'chromium' }], }, // If you want to enable `page.render` and automatic cleanup, add this setup file setupFiles: ['@nuxt/test-utils/browser'], }, }), ], }, }) ``` ::note When adding `@nuxt/test-utils/browser` to `setupFiles`, if TypeScript does not automatically recognize the types for `page.render`, you can add `@nuxt/test-utils/browser` via `typescript.tsConfig.compilerOptions.types` in your `nuxt.config.ts`. :: ::note If your Vitest browser mode tests live outside `test/nuxt/` (such as in `test/browser/`), see [Adding other test directories](#adding-other-test-directories) to add them to the TypeScript context. :: #### Usage You can use `page.render` from `vitest/browser`: ```ts twoslash [test/browser/components/MyCounter.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const MyCounter: Component } // ---cut--- import { expect, it } from 'vitest' import { page } from 'vitest/browser' // If you added the setup file, the following import is not needed import '@nuxt/test-utils/browser' import { MyCounter } from '#components' it('counter button increments the count', async () => { const screen = await page.render(MyCounter) await screen.getByRole('button', { name: 'Increment' }).click() await expect.element(screen.getByText('Count: 1')).toBeVisible() }) ``` You can also import `render` directly from `@nuxt/test-utils/browser`: ```ts twoslash // @noErrors import type { Component } from 'vue' declare module '#components' { export const MyCounter: Component } // ---cut--- import { expect, it } from 'vitest' import { render } from '@nuxt/test-utils/browser' import { MyCounter } from '#components' it('can render using the render helper', async () => { const screen = await render(MyCounter) await screen.getByRole('button', { name: 'Increment' }).click() await expect.element(screen.getByText('Count: 1')).toBeVisible() }) ``` The options object accepts `@vue/test-utils` mount options (use `container` instead of `attachTo`) and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). - `spy`: enable spy component setup state (default `false`). See the [`mountSuspended`](#mountsuspended) example above. - `container`: custom `HTMLElement` container to render into (use this option instead of `@vue/test-utils` `attachTo`). - `baseElement`: custom base `HTMLElement` (default `document.body`). The return object includes the following properties: - `container`: the container `HTMLElement` the component is rendered into. - `baseElement`: the base `HTMLElement` (default `document.body`). - `locator`: root element `Locator`. - `setupState`: the return value of the component setup, mocked when the `spy` option is enabled. - `debug()`: print formatted DOM to console. - `unmount()`: unmount the component. Also records a `nuxt.unmount` trace mark. - `emitted()`: get emitted events. - `rerender(props)`: re-render the component with new props. Also records a `nuxt.rerender` trace mark. ## End-To-End Testing For end-to-end testing, we support [Vitest](https://github.com/vitest-dev/vitest), [Jest](https://jestjs.io), [Cucumber](https://cucumber.io/) and [Playwright](https://playwright.dev/) as test runners. ### Setup In each `describe` block where you are taking advantage of the `@nuxt/test-utils/e2e` helper methods, you will need to set up the test context before beginning. ```ts twoslash [test/my-test.spec.ts] import { describe, test } from 'vitest' import { $fetch, setup } from '@nuxt/test-utils/e2e' describe('My test', async () => { await setup({ // test context options }) test('my test', () => { // ... }) }) ``` Behind the scenes, `setup` performs a number of tasks in `beforeAll`, `beforeEach`, `afterEach` and `afterAll` to set up the Nuxt test environment correctly. Please use the options below for the `setup` method. #### Nuxt Config - `rootDir`: Path to a directory with a Nuxt app to be put under test. - Type: `string` - Default: `'.'` - `configFile`: Name of the configuration file. - Type: `string` - Default: `'nuxt.config'` #### Timings - `setupTimeout`: The amount of time (in milliseconds) to allow for `setupTest` to complete its work (which could include building or generating files for a Nuxt application, depending on the options that are passed). - Type: `number` - Default: `120000` or `240000` on windows - `teardownTimeout`: The amount of time (in milliseconds) to allow tearing down the test environment, such as closing the browser. - Type: `number` - Default: `30000` #### Features - `build`: Whether to run a separate build step. - Type: `boolean` - Default: `true` (`false` if `browser` or `server` is disabled, or if a `host` is provided) - `server`: Whether to launch a server to respond to requests in the test suite. - Type: `boolean` - Default: `true` (`false` if a `host` is provided) - `port`: If provided, set the launched test server port to the value. - Type: `number | undefined` - Default: `undefined` - `host`: If provided, a URL to use as the test target instead of building and running a new server. Useful for running "real" end-to-end tests against a deployed version of your application, or against an already running local server (which may provide a significant reduction in test execution timings). See the [target host end-to-end example below](https://nuxt.com/docs/4.x/getting-started/testing#target-host-end-to-end-example). - Type: `string` - Default: `undefined` - `browser`: Under the hood, Nuxt test utils uses [`playwright`](https://playwright.dev) to carry out browser testing. If this option is set, a browser will be launched and can be controlled in the subsequent test suite. - Type: `boolean` - Default: `false` - `browserOptions` - Type: `object` with the following properties - `type`: The type of browser to launch - either `chromium`, `firefox` or `webkit` - `launch`: `object` of options that will be passed to playwright when launching the browser. See [full API reference](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). - `runner`: Specify the runner for the test suite. Currently, [Vitest](https://vitest.dev) is recommended. - Type: `'vitest' | 'jest' | 'cucumber'` - Default: `'vitest'` - `logLevel`: Overrides the consola log level for the server subprocess. (use NUXT_TEST_LOG_LEVEL environment variable to override) - Type: `number` - Default: `1` - `captureServerLogs`: Whether to capture server process output instead of inheriting stdio. When `true` (default), server stdout/stderr is suppressed from the console and accessible via `getServerLogs()`. Set to `false` to restore the old inherit-stdio behavior (useful when debugging a test locally). - Type: `boolean` - Default: `true` ##### Target `host` end-to-end example A common use-case for end-to-end testing is running the tests against a deployed application running in the same environment typically used for Production. For local development or automated deploy pipelines, testing against a separate local server can be more efficient and is typically faster than allowing the test framework to rebuild between tests. To utilize a separate target host for end-to-end tests, simply provide the `host` property of the `setup` function with the desired URL. ```ts import { createPage, setup } from '@nuxt/test-utils/e2e' import { describe, expect, it } from 'vitest' describe('login page', async () => { await setup({ host: 'http://localhost:8787', }) it('displays the email and password fields', async () => { const page = await createPage('/login') expect(await page.getByTestId('email').isVisible()).toBe(true) expect(await page.getByTestId('password').isVisible()).toBe(true) }) }) ``` ### APIs #### `$fetch(url)` Get the HTML of a server-rendered page. ```ts twoslash import { $fetch } from '@nuxt/test-utils/e2e' const html = await $fetch('/') ``` #### `fetch(url)` Get the response of a server-rendered page. ```ts twoslash import { fetch } from '@nuxt/test-utils/e2e' const res = await fetch('/') const { body, headers } = res ``` #### `url(path)` Get the full URL for a given page (including the port the test server is running on.) ```ts twoslash import { url } from '@nuxt/test-utils/e2e' const pageUrl = url('/page') // 'http://localhost:6840/page' ``` #### `getServerLogs()` Returns the lines captured from the server subprocess's stdout/stderr since the last `startServer()` call (or `clearServerLogs()`). Only populated when `captureServerLogs` is `true` (the default). ```ts twoslash import { expect, it, vi } from 'vitest' import { $fetch, clearServerLogs, getServerLogs } from '@nuxt/test-utils/e2e' it('captures console.log output from a server route', async () => { clearServerLogs() await $fetch('/api/log-test') await vi.waitFor(() => { expect(getServerLogs().some(line => line.includes('[test] server-log-marker'))).toBe(true) }) }) ``` #### `clearServerLogs()` Clears the captured server log lines. Useful between requests when you want to assert only on the logs produced by a specific operation. ### Testing in a Browser We provide built-in support using Playwright within `@nuxt/test-utils`, either programmatically or via the Playwright test runner. #### `createPage(url)` Within `vitest`, `jest` or `cucumber`, you can create a configured Playwright browser instance with `createPage`, and (optionally) point it at a path from the running server. You can find out more about the API methods available in the [Playwright documentation](https://playwright.dev/docs/api/class-page). When you pass a path, `createPage` waits for your Nuxt app to finish hydrating before it resolves. On later navigations, pass `waitUntil: 'hydration'` to `page.goto` to wait for hydration again. ```ts twoslash import { createPage, url } from '@nuxt/test-utils/e2e' const page = await createPage('/page') // you can access all the Playwright APIs from the `page` variable await page.goto(url('/other-page'), { waitUntil: 'hydration' }) ``` ::tip Interacting with a page before it has hydrated (for example, clicking a button before its event handler is attached) can make tests flaky. :: #### Testing with Playwright Test Runner We also provide first-class support for testing Nuxt within [the Playwright test runner](https://playwright.dev/docs/intro). ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @playwright/test @nuxt/test-utils ``` ```bash [yarn] yarn add --dev @playwright/test @nuxt/test-utils ``` ```bash [pnpm] pnpm add -D @playwright/test @nuxt/test-utils ``` ```bash [bun] bun add --dev @playwright/test @nuxt/test-utils ``` ```bash [deno] deno add --dev npm:@playwright/test npm:@nuxt/test-utils ``` :: You can provide global Nuxt configuration, with the same configuration details as the `setup()` function mentioned earlier in this section. ```ts [playwright.config.ts] import { fileURLToPath } from 'node:url' import { defineConfig, devices } from '@playwright/test' import type { ConfigOptions } from '@nuxt/test-utils/playwright' export default defineConfig({ use: { nuxt: { rootDir: fileURLToPath(new URL('.', import.meta.url)), }, }, // ... }) ``` ::read-more{title="See full example config" to="https://github.com/nuxt/test-utils/blob/main/examples/app-playwright/playwright.config.ts" target="_blank"} :: Your test file should then use `expect` and `test` directly from `@nuxt/test-utils/playwright`: ```ts [tests/example.test.ts] import { expect, test } from '@nuxt/test-utils/playwright' test('test', async ({ page, goto }) => { await goto('/', { waitUntil: 'hydration' }) await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') }) ``` You can alternatively configure your Nuxt server directly within your test file: ```ts [tests/example.test.ts] import { expect, test } from '@nuxt/test-utils/playwright' test.use({ nuxt: { rootDir: fileURLToPath(new URL('..', import.meta.url)), }, }) test('test', async ({ page, goto }) => { await goto('/', { waitUntil: 'hydration' }) await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') }) ``` # Upgrade Guide > Learn how to upgrade to the latest Nuxt version. ## Upgrading Nuxt ### Latest release To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxt upgrade` command. ::code-group{sync="pm"} ```bash [npm] npx nuxt upgrade ``` ```bash [yarn] yarn nuxt upgrade ``` ```bash [pnpm] pnpm nuxt upgrade ``` ```bash [bun] bun x nuxt upgrade ``` ```bash [deno] deno x nuxt upgrade ``` :: ### Nightly Release Channel To use the latest Nuxt build and test features before their release, read about the [nightly release channel](https://nuxt.com/docs/4.x/guide/going-further/nightly-release-channel) guide. ## Testing Nuxt 5 Nuxt 5 is **currently in development**. From Nuxt 4.2+ you can test many of its breaking changes by setting `future.compatibilityVersion` to `5`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ future: { compatibilityVersion: 5, }, }) ``` Defaults throughout your Nuxt configuration then change to opt in to Nuxt 5 behavior. On Nuxt 4 this covers: - **Vite Environment API**: [`experimental.viteEnvironmentApi`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#viteenvironmentapi) is enabled, changing how Vite plugins are registered and configured - **Case-sensitive routing**: page routes match URL casing exactly, consistent with Nitro, so `/About` no longer matches `pages/about.vue` - **Normalized page names**: page component names [match their route names](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#normalizepagenames) for consistent `` behaviour - **Serializable page meta**: [`experimental.extractSerializablePageMeta`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#extractserializablepagemeta) extracts statically analysable `definePageMeta` values at build time - **Client-side payload extraction**: [`experimental.payloadExtraction`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#payloadextraction) becomes `'client'`, inlining the payload in the HTML of a prerendered page - **`clearNuxtState` resets to defaults**: `clearNuxtState` resets state to the initial value of [`useState`](https://nuxt.com/docs/4.x/api/composables/use-state) instead of setting it to `undefined` - **Non-async `callHook`**: `callHook` may return `void` instead of always returning a `Promise` - **Early return from `navigateTo`**: [`experimental.navigateToEarlyReturn`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#navigatetoearlyreturn) stops a middleware continuing after it redirects - **Server-only head composables**: `useServerHead`, `useServerHeadSafe` and `useServerSeoMeta` are no longer auto-imported, `unhead.legacy` is ignored, Unhead's legacy plugins are no longer registered and Capo sorting is always on - **PostCSS defaults off**: `autoprefixer` and `cssnano` are no longer configured unless you enable them in `postcss.plugins` - **Keyed composables require `source`**: a [`optimization.keyedComposables`](https://nuxt.com/docs/4.x/api/nuxt-config#keyedcomposables) entry without a string `source` is no longer matched through its auto-import - **Comment node placeholders**: client-only components use [comment nodes instead of `
`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#clientnodeplaceholder) as SSR placeholders - **Typed pages**: [`experimental.typedPages`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#typedpages) is enabled for type-checked routing - **Error response format**: `experimental.inlineErrorRendering` is enabled, so page errors return the error page and server route errors return JSON, [regardless of the request headers](https://nuxt.com/docs/5.x/getting-started/upgrade#error-format-depends-on-where-the-error-is-thrown) - **Typed `$fetch`**: [`experimental.routeTypedFetch`](https://nuxt.com/docs/4.x/guide/going-further/experimental-features#routetypedfetch) types `$fetch` and `useFetch` from the generated route types - **Nitro auto-imports off**: server code [imports its helpers explicitly](https://nuxt.com/docs/5.x/getting-started/upgrade#server-auto-imports-are-now-opt-in), such as from [`nuxt/server`](#moving-to-nuxtserver), rather than relying on the auto-import compatibility layer. Set `experimental.nitroAutoImports: true` to keep them - **Vue Options API disabled**: the Options API is compiled out of the client bundle to reduce its size - **Stricter side-effect imports**: the generated `tsconfig.json` enables `noUncheckedSideEffectImports` - **TypeScript `baseUrl` ignored**: generated TypeScript configurations no longer use `compilerOptions.baseUrl` to resolve Nuxt aliases ::note This list describes what the flag changes on Nuxt 4. Nuxt 5 itself carries further changes that no flag enables here, such as the upgrade to Nitro v3. :: The migration steps for each change, and everything else the upgrade brings, are documented in the [Nuxt 5 upgrade guide](https://nuxt.com/docs/5.x/getting-started/upgrade). ## Moving to `nuxt/server` From Nuxt 4.6, server code can import its handler and helpers from [`nuxt/server`](https://nuxt.com/docs/4.x/guide/going-further/server-imports) rather than from h3, so the same file runs on Nuxt 4 (`nitropack` v2 and h3 v1) and on [Nuxt 5](https://nuxt.com/docs/5.x/getting-started/upgrade) (Nitro v3 and h3 v2). On Nuxt 4.6, import `defineEventHandler` and the helpers from `nuxt/server` explicitly. The auto-imported names of the same spelling are h3 v1's, and h3's `defineEventHandler` passes your handler a different event from the one the `nuxt/server` helpers read. ```diff [server/api/hello.ts] + import { defineEventHandler, getQuery } from 'nuxt/server' + export default defineEventHandler((event) => { const { name } = getQuery<{ name?: string }>(event) return { message: `Hello, ${name ?? 'world'}!` } }) ``` On Nuxt 5 the same names are auto-imported from `nuxt/server`, so explicit imports written on Nuxt 4.6 keep working unchanged. ### Differences From h3 v1 | h3 v1 | `nuxt/server` | | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sendRedirect(event, location)` sends the response. | `sendRedirect(event, location)` returns the response body: `return sendRedirect(event, '/login')`. | | `getRouterParam(event, name)` and `getRouterParams(event)` return decoded values. | They return the values as they appear in the URL. Pass `{ decode: true }` to decode them. | | `handleCors(event, options)` returns `true` when it answered a preflight request. | `handleCors(event, options)` returns the preflight `Response` to return from the handler, or `false`: `const preflight = handleCors(event, options); if (preflight) return preflight`. | | `handleCors` accepts a single origin string at runtime. | `origin` is `'*'`, `'null'`, an array of origins or patterns, or a function: wrap a single origin in an array. `maxAge` is a string of seconds, such as `'86400'`, or `false`. Import the `CorsOptions` type from `nuxt/server`. | | `readValidatedBody` and `getValidatedQuery` take a validation function. | They take a [Standard Schema](https://standardschema.dev) or a function. | | `createError({ statusCode, statusMessage })` | `createError({ status, statusText })` | | `isError(error)` | `isNuxtError(error)` | | h3's `useSession`, `getSession`, `updateSession` and `clearSession` seal an `h3` cookie with the `password` you pass. | They seal a `nuxt-session` cookie, by default with a secret derived from `appSecret`. The two are not interchangeable: a session one issues does not unseal with the other. | | `useRuntimeConfig(event)` | `useRuntimeConfig()`. There is no per-request form. | | Global `$fetch` | Not part of `nuxt/server`. Call a route of the app with `serverFetch(event, path)`, which returns a `Response`. For other URLs, plain `fetch` does not throw on a non-2xx status and does not parse JSON: check `response.ok` and call `response.json()`. | | `event.path` | `getRequestURL(event).pathname`, or `event.url.pathname`. | | `event.node` | Not part of `nuxt/server`. Use `event.req` and `event.res`. | | `getResponseHeader`, `setResponseHeader`, `appendResponseHeader`, `removeResponseHeader` | `event.res.headers.get()`, `.set()`, `.append()`, `.delete()` | | `getMethod(event)`, `isMethod(event, method)`, `assertMethod(event, method)` | Compare `event.req.method`. | | `readMultipartFormData(event)` | `await event.req.formData()` | | `readRawBody(event)`, `getRequestWebStream(event)` | `await event.req.arrayBuffer()` (or `.text()`), `event.req.body` | | `sanitizeStatusCode` | Not provided. | ### Helpers Outside `nuxt/server` For a handler that needs h3 or Nitro helpers that `nuxt/server` does not provide, such as `readMultipartFormData`, `proxyRequest`, `useStorage` or `defineCachedEventHandler`, keep importing `defineEventHandler` and those helpers from `h3` (or `nitropack/runtime`) for that handler. Code that does this is tied to the server runtime and needs revisiting on Nuxt 5. ```ts [server/api/upload.post.ts] import { defineEventHandler, readMultipartFormData } from 'h3' export default defineEventHandler(async (event) => { const parts = await readMultipartFormData(event) return { received: parts?.length ?? 0 } }) ``` ### `NUXT_E8012` A request fails with a `500` whose message starts with `[NUXT_E8012]` when a `nuxt/server` helper such as `readValidatedBody`, `getValidatedQuery`, `handleCors` or `useSession` receives an h3 event. This means the handler was defined with h3's `defineEventHandler` (usually the auto-imported one). Import `defineEventHandler` from `nuxt/server` in the same file as the helpers. ### Application Secret The `nuxt/server` session helpers and `deriveSecret` read `runtimeConfig.appSecret`, which you set with `NUXT_APP_SECRET`. On Nuxt 4, `nitropack` v2 parses environment overrides, so a secret made only of digits arrives as a number; see [the application secret](https://nuxt.com/docs/4.x/guide/going-further/runtime-config#application-secret) for how to avoid this. ## Migrating to Nuxt 4 Nuxt 4 includes significant improvements and changes. This guide will help you migrate your existing Nuxt 3 application to Nuxt 4. First, upgrade to Nuxt 4: ::code-group{sync="pm"} ```bash [npm] npm install nuxt@^4.0.0 ``` ```bash [yarn] yarn add nuxt@^4.0.0 ``` ```bash [pnpm] pnpm add nuxt@^4.0.0 ``` ```bash [bun] bun add nuxt@^4.0.0 ``` ```bash [deno] deno add npm:nuxt@^4.0.0 ``` :: After upgrading, most Nuxt 4 behaviors are now the default. However, some features can still be configured if you need to maintain backward compatibility during your migration. The following sections detail the key changes and migrations needed when upgrading to Nuxt 4. Breaking or significant changes are documented below along with migration steps and available configuration options. ### Migrating Using Codemods To facilitate the upgrade process, we have collaborated with the [Codemod](https://github.com/codemod/codemod) team to automate many migration steps with some open-source codemods. ::note If you encounter any issues, please report them to the Codemod team with `npx codemod feedback` 🙏 :: For a complete list of Nuxt 4 codemods, detailed information on each, their source, and various ways to run them, visit the [Codemod Registry](https://app.codemod.com/registry). You can run all the codemods mentioned in this guide using the following `codemod` recipe: ::code-group ```bash [npm] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 npx codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [yarn] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 yarn dlx codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [pnpm] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 pnpm dlx codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [bun] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 bun x codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [deno] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 deno x codemod@0.18.7 nuxt/4/migration-recipe ``` :: This command will execute all codemods in sequence, with the option to deselect any that you do not wish to run. Each codemod is also listed below alongside its respective change and can be executed independently. ### New Directory Structure 🚦 **Impact Level**: Significant Nuxt now defaults to a new directory structure, with backwards compatibility (so if Nuxt detects you are using the old structure, such as with a top-level `app/pages/` directory, this new structure will not apply). 👉 [See full RFC](https://github.com/nuxt/nuxt/issues/26444) #### What Changed - the new Nuxt default `srcDir` is `app/` by default, and most things are resolved from there. - `serverDir` now defaults to `/server` rather than `/server` - `layers/`, `modules/` and `public/` are resolved relative to `` by default - if using [Nuxt Content v2.13+](https://github.com/nuxt/content/pull/2649), `content/` is resolved relative to `` - a new `dir.app` is added, which is the directory we look for `router.options.ts` and `spa-loading-template.html` - this defaults to `/` - a new [`shared/`](https://nuxt.com/docs/4.x/directory-structure/shared) directory is available for code shared between the Vue app and the Nitro server, with auto-imports for `shared/utils/` and `shared/types/`
An example v4 folder structure. ```sh .output/ .nuxt/ app/ assets/ components/ composables/ layouts/ middleware/ pages/ plugins/ utils/ app.config.ts app.vue router.options.ts content/ layers/ modules/ node_modules/ public/ shared/ types/ utils/ server/ api/ middleware/ plugins/ routes/ utils/ nuxt.config.ts ``` ::note With this new structure, the `~` alias now points to the `app/` directory by default (your `srcDir`). This means `~/components` resolves to `app/components/`, `~/pages` to `app/pages/`, etc. ::
👉 For more details, see the [PR implementing this change](https://github.com/nuxt/nuxt/pull/27029). #### Reasons for Change 1. **Performance** - placing all your code in the root of your repo causes issues with `.git/` and `node_modules/` folders being scanned/included by FS watchers which can significantly delay startup on non-Mac OSes. 2. **IDE type-safety** - `server/` and the rest of your app are running in two entirely different contexts with different global imports available, and making sure `server/` isn't *inside* the same folder as the rest of your app is a big first step to ensuring you get good auto-completes in your IDE. ::video-accordion{title="Watch a video from Vue School on the new directory structure" videoId="1031028378" platform="vimeo"} :: #### Migration Steps 1. Create a new directory called `app/`. 2. Move your `assets/`, `components/`, `composables/`, `app/layouts/`, `app/middleware/`, `app/pages/`, `app/plugins/` and `utils/` folders under it, as well as `app.vue`, `error.vue`, `app.config.ts`. If you have an `app/router-options.ts` or `app/spa-loading-template.html`, these paths remain the same. 3. Make sure your `nuxt.config.ts`, `content/`, `layers/`, `modules/`, `public/`, `shared/` and `server/` folders remain outside the `app/` folder, in the root of your project. 4. Remember to update any third-party configuration files to work with the new directory structure, such as your `tailwindcss` or `eslint` configuration (if required - `@nuxtjs/tailwindcss` should automatically configure `tailwindcss` correctly). ::tip You can automate this migration by running `npx codemod@latest nuxt/4/file-structure` :: However, migration is *not required*. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) The one exception is that if you *already* have a custom `srcDir`. In this case, you should be aware that your `modules/`, `public/`, `shared/` and `server/` folders will be resolved from your `rootDir` rather than from your custom `srcDir`. You can override this by configuring `dir.modules`, `dir.public` and `serverDir` if you need to. You can also force a v3 folder structure with the following configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ // This reverts the new srcDir default from `app` back to your root directory srcDir: '.', // This specifies the directory prefix for `router.options.ts` and `spa-loading-template.html` dir: { app: 'app', }, }) ``` ### Singleton Data Fetching Layer 🚦 **Impact Level**: Moderate #### What Changed Nuxt's data fetching system (`useAsyncData` and `useFetch`) has been significantly reorganized for better performance and consistency: 1. **Shared refs for the same key**: All calls to `useAsyncData` or `useFetch` with the same key now share the same `data`, `error` and `status` refs. This means that it is important that all calls with an explicit key must not have conflicting `deep`, `transform`, `pick`, `getCachedData` or `default` options. 2. **More control over `getCachedData`**: The `getCachedData` function is now called every time data is fetched, even if this is caused by a watcher or calling `refreshNuxtData`. (Previously, new data was always fetched and this function was not called in these cases.) To allow more control over when to use cached data and when to refetch, the function now receives a context object with the cause of the request. 3. **Reactive key support**: You can now use computed refs, plain refs or getter functions as keys, which enables automatic data refetching (and stores data separately). 4. **Data cleanup**: When the last component using data fetched with `useAsyncData` is unmounted, Nuxt will remove that data to avoid ever-growing memory usage. #### Reasons for Change These changes have been made to improve memory usage and increase consistency with loading states across calls of `useAsyncData`. #### Migration Steps 1. **Check for inconsistent options**: Review any components using the same key with different options or fetch functions. ```ts // This will now trigger a warning const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false }) const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true }) ``` It may be beneficial to extract any calls to `useAsyncData` that share an explicit key (and have custom options) into their own composable: ```ts [app/composables/useUserData.ts] export function useUserData (userId: string) { return useAsyncData( `user-${userId}`, () => fetchUser(userId), { deep: true, transform: user => ({ ...user, lastAccessed: new Date() }), }, ) } ``` 2. **Update `getCachedData` implementations**: ```diff useAsyncData('key', fetchFunction, { - getCachedData: (key, nuxtApp) => { - return cachedData[key] - } + getCachedData: (key, nuxtApp, ctx) => { + // ctx.cause - can be 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch' + + // Example: Don't use cache on manual refresh + if (ctx.cause === 'refresh:manual') return undefined + + return cachedData[key] + } }) ``` Alternatively, for now, you can disable this behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { granularCachedData: false, purgeCachedData: false, }, }) ``` ### Corrected Module Loading Order in Layers 🚦 **Impact Level**: Minimal #### What Changed The order in which modules are loaded when using [Nuxt layers](https://nuxt.com/docs/4.x/guide/going-further/layers) has been corrected. Previously, modules from the project root were loaded before modules from extended layers, which was the reverse of the expected behavior. Now modules are loaded in the correct order: 1. **Layer modules first** (in extend order - deeper layers first) 2. **Project modules last** (highest priority) This affects both: - Modules defined in the `modules` array in `nuxt.config.ts` - Auto-discovered modules from the `modules/` directory #### Reasons for Change This change ensures that: - Extended layers have lower priority than the consuming project - Module execution order matches the intuitive layer inheritance pattern - Module configuration and hooks work as expected in multi-layer setups #### Migration Steps **Most projects will not need changes**, as this corrects the loading order to match expected behavior. However, if your project was relying on the previous incorrect order, you may need to: 1. **Review module dependencies**: Check if any modules depend on specific loading order 2. **Adjust module configuration**: If modules were configured to work around the incorrect order 3. **Test thoroughly**: Ensure all functionality works as expected with the corrected order Example of the new correct order: ```ts // Layer: my-layer/nuxt.config.ts export default defineNuxtConfig({ modules: ['layer-module-1', 'layer-module-2'], }) // Project: nuxt.config.ts export default defineNuxtConfig({ extends: ['./my-layer'], modules: ['project-module-1', 'project-module-2'], }) // Loading order (corrected): // 1. layer-module-1 // 2. layer-module-2 // 3. project-module-1 (can override layer modules) // 4. project-module-2 (can override layer modules) ``` If you encounter issues with module order dependencies due to needing to register a hook, consider using the [`modules:done` hook](https://nuxt.com/docs/4.x/guide/modules/recipes-advanced) for modules that need to call a hook. This is run after all other modules have been loaded, which means it is safe to use. 👉 See [PR #31507](https://github.com/nuxt/nuxt/pull/31507) and [issue #25719](https://github.com/nuxt/nuxt/issues/25719) for more details. ### Deduplication of Route Metadata 🚦 **Impact Level**: Minimal #### What Changed It's possible to set some route metadata using `definePageMeta`, such as the `name`, `path`, and so on. Previously these were available both on the route and on route metadata (for example, `route.name` and `route.meta.name`). Now, they are only accessible on the route object. #### Reasons for Change This is a result of enabling `experimental.scanPageMeta` by default, and is a performance optimization. #### Migration Steps The migration should be straightforward: ```diff const route = useRoute() - console.log(route.meta.name) + console.log(route.name) ``` ### Normalized Component Names 🚦 **Impact Level**: Moderate Vue will now generate component names that match the Nuxt pattern for component naming. #### What Changed By default, if you haven't set it manually, Vue will assign a component name that matches the filename of the component. ```bash [Directory structure] ├─ components/ ├─── SomeFolder/ ├───── MyComponent.vue ``` In this case, the component name would be `MyComponent`, as far as Vue is concerned. If you wanted to use `` with it, or identify it in the Vue DevTools, you would need to use this name. But in order to auto-import it, you would need to use `SomeFolderMyComponent`. With this change, these two values will match, and Vue will generate a component name that matches the Nuxt pattern for component naming. #### Migration Steps Ensure that you use the updated name in any tests which use `findComponent` from `@vue/test-utils` and in any `` which depends on the name of your component. Alternatively, for now, you can disable this behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { normalizeComponentNames: false, }, }) ``` ### Unhead v2 🚦 **Impact Level**: Minimal #### What Changed [Unhead](https://unhead.unjs.io/), used to generate `` tags, has been updated to version 2. While mostly compatible it includes several breaking changes for lower-level APIs. - Removed props: `vmid`, `hid`, `children`, `body`. - Promise input no longer supported. - Tags are now sorted using Capo.js by default. #### Migration Steps The above changes should have minimal impact on your app. If you have issues you should verify: - You're not using any of the removed props. ```diff useHead({ meta: [{ name: 'description', // meta tags don't need a vmid, or a key - vmid: 'description' - hid: 'description' }] }) ``` - If you're using [Template Params](https://unhead.unjs.io/docs/head/guides/plugins/template-params) or [Alias Tag Sorting](https://unhead.unjs.io/docs/head/guides/plugins/alias-sorting), you will need to explicitly opt in to these features now. ```ts import { AliasSortingPlugin, TemplateParamsPlugin } from '@unhead/vue/plugins' export default defineNuxtPlugin({ setup () { const unhead = injectHead() unhead.use(TemplateParamsPlugin) unhead.use(AliasSortingPlugin) }, }) ``` While not required it's recommended to update any imports from `@unhead/vue` to `#imports` or `nuxt/app`. ```diff -import { useHead } from '@unhead/vue' +import { useHead } from '#imports' ``` If you still have issues you may revert to the v1 behavior by enabling the `head.legacy` config. ```ts export default defineNuxtConfig({ unhead: { legacy: true, }, }) ``` ### New DOM Location for SPA Loading Screen 🚦 **Impact Level**: Minimal #### What Changed When rendering a client-only page (with `ssr: false`), we optionally render a loading screen (from `~/app/spa-loading-template.html` - note that this has also changed to `~/spa-loading-template.html` in Nuxt 4), within the Nuxt app root: ```html
``` Now, we default to rendering the template alongside the Nuxt app root: ```html
``` #### Reasons for Change This allows the spa loading template to remain in the DOM until the Vue app suspense resolves, preventing a flash of white. #### Migration Steps If you were targeting the spa loading template with CSS or `document.queryElement` you will need to update your selectors. For this purpose you can use the new `app.spaLoaderTag` and `app.spaLoaderAttrs` configuration options. Alternatively, you can revert to the previous behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { spaLoadingTemplateLocation: 'within', }, }) ``` ### Parsed `error.data` 🚦 **Impact Level**: Minimal It was possible to throw an error with a `data` property, but this was not parsed. Now, it is parsed and made available in the `error` object. Although a fix, this is technically a breaking change if you were relying on the previous behavior and parsing it manually. #### Migration Steps Update your custom `error.vue` to remove any additional parsing of `error.data`: ```diff ``` ### More Granular Inline Styles 🚦 **Impact Level**: Moderate Nuxt will now only inline styles for Vue components, not global CSS. #### What Changed Previously, Nuxt would inline all CSS, including global styles, and remove `` elements to separate CSS files. Now, Nuxt will only do this for Vue components (which previously produced separate chunks of CSS). We think this is a better balance of reducing separate network requests (just as before, there will not be separate requests for individual `.css` files per-page or per-component on the initial load), as well as allowing caching of a single global CSS file and reducing the document download size of the initial request. #### Migration Steps This feature is fully configurable and you can revert to the previous behavior by setting `inlineStyles: true` to inline global CSS as well as per-component CSS. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ features: { inlineStyles: true, }, }) ``` ### Scan Page Meta After Resolution 🚦 **Impact Level**: Minimal #### What Changed We now scan page metadata (defined in `definePageMeta`) *after* calling the `pages:extend` hook rather than before. #### Reasons for Change This was to allow scanning metadata for pages that users wanted to add in `pages:extend`. We still offer an opportunity to change or override page metadata in a new `pages:resolved` hook. #### Migration Steps If you want to override page metadata, do that in `pages:resolved` rather than in `pages:extend`. ```diff export default defineNuxtConfig({ hooks: { - 'pages:extend'(pages) { + 'pages:resolved'(pages) { const myPage = pages.find(page => page.path === '/') myPage.meta ||= {} myPage.meta.layout = 'overridden-layout' } } }) ``` Alternatively, you can revert to the previous behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { scanPageMeta: true, }, }) ``` ### Shared Prerender Data 🚦 **Impact Level**: Medium #### What Changed We enabled a previously experimental feature to share data from `useAsyncData` and `useFetch` calls, across different pages. See [original PR](https://github.com/nuxt/nuxt/pull/24894). #### Reasons for Change This feature automatically shares payload *data* between pages that are prerendered. This can result in a significant performance improvement when prerendering sites that use `useAsyncData` or `useFetch` and fetch the same data in different pages. For example, if your site requires a `useFetch` call for every page (for example, to get navigation data for a menu, or site settings from a CMS), this data would only be fetched once when prerendering the first page that uses it, and then cached for use when prerendering other pages. #### Migration Steps Make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` should do this automatically for you.) ```ts [app/pages/test/[slug\\].vue] // This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference // to the data fetched, but Nuxt can't know that because it's not reflected in the key. const route = useRoute() const { data } = await useAsyncData(async () => { return await $fetch(`/api/my-page/${route.params.slug}`) }) // Instead, you should use a key that uniquely identifies the data fetched. const { data } = await useAsyncData(route.params.slug, async () => { return await $fetch(`/api/my-page/${route.params.slug}`) }) ``` Alternatively, you can disable this feature with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { sharedPrerenderData: false, }, }) ``` ### Default `data` and `error` values in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal #### What Changed `data` and `error` objects returned from `useAsyncData` will now default to `undefined`. #### Reasons for Change Previously `data` was initialized to `null` but reset in `clearNuxtData` to `undefined`. `error` was initialized to `null`. This change is to bring greater consistency. #### Migration Steps If you were checking if `data.value` or `error.value` were `null`, you can update these checks to check for `undefined` instead. ::tip You can automate this step by running `npx codemod@latest nuxt/4/default-data-error-value` :: ### Removal of deprecated `boolean` values for `dedupe` option when calling `refresh` in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal #### What Changed Previously it was possible to pass `dedupe: boolean` to `refresh`. These were aliases of `cancel` (`true`) and `defer` (`false`). ```ts twoslash [app/app.vue] // @errors: 2322 const { refresh } = await useAsyncData(() => Promise.resolve({ message: 'Hello, Nuxt!' })) async function refreshData () { await refresh({ dedupe: true }) } ``` #### Reasons for Change These aliases were removed, for greater clarity. The issue came up when adding `dedupe` as an option to `useAsyncData`, and we removed the boolean values as they ended up being *opposites*. `refresh({ dedupe: false })` meant **do not *cancel* existing requests in favour of this new one**. But passing `dedupe: true` within the options of `useAsyncData` means **do not make any new requests if there is an existing pending request.** (See [PR](https://github.com/nuxt/nuxt/pull/24564#pullrequestreview-1764584361).) #### Migration Steps The migration should be straightforward: ```diff const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' })) async function refreshData () { - await refresh({ dedupe: true }) + await refresh({ dedupe: 'cancel' }) - await refresh({ dedupe: false }) + await refresh({ dedupe: 'defer' }) } ``` ::tip You can automate this step by running `npx codemod@latest nuxt/4/deprecated-dedupe-value` :: ### Respect defaults when clearing `data` in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal #### What Changed If you provide a custom `default` value for `useAsyncData`, this will now be used when calling `clear` or `clearNuxtData` and it will be reset to its default value rather than simply unset. #### Reasons for Change Often users set an appropriately empty value, such as an empty array, to avoid the need to check for `null`/`undefined` when iterating over it. This should be respected when resetting/clearing the data. ### Respect defaults when clearing `useState` 🚦 **Impact Level**: Minimal #### What Changed With `compatibilityVersion: 5`, `clearNuxtState` will reset state to its initial value (provided by the `init` function of `useState`) instead of setting it to `undefined`. This aligns `clearNuxtState` behavior with `clearNuxtData`, which already resets to defaults. #### Reasons for Change When `clearNuxtState` sets state to `undefined`, composables that depend on that state can crash because they expect the state to always have a valid shape (e.g., accessing properties on `undefined`). Resetting to the `init` value ensures state always has a usable default. #### Migration Steps If you rely on `clearNuxtState` setting state to `undefined`, you can explicitly pass `{ reset: false }`: ```diff - clearNuxtState('myKey') + clearNuxtState('myKey', { reset: false }) ``` Alternatively, you can revert to the previous behavior with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { useState: { resetOnClear: false, }, }, }, }) ``` You can also opt in to this behavior today without setting `compatibilityVersion: 5`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { useState: { resetOnClear: true, }, }, }, }) ``` ### Alignment of `pending` value in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Medium The `pending` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a computed property that is `true` only when `status` is also pending. #### What Changed Now, when `immediate: false` is passed, `pending` will be `false` until the first request is made. This is a change from the previous behavior, where `pending` was always `true` until the first request was made. #### Reasons for Change This aligns the meaning of `pending` with the `status` property, which is also `pending` when the request is in progress. #### Migration Steps If you rely on the `pending` property, ensure that your logic accounts for the new behavior where `pending` will only be `true` when the status is also pending. ```diff ``` Alternatively, you can temporarily revert to the previous behavior with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { pendingWhenIdle: true, }, }) ``` ### Key Change Behavior in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Medium #### What Changed When using reactive keys in `useAsyncData` or `useFetch`, Nuxt automatically refetches data when the key changes. When `immediate: false` is set, `useAsyncData` will only fetch data when the key changes if the data has already been fetched once. Previously, `useFetch` had slightly different behavior. It would always fetch data when the key changed. Now, `useFetch` and `useAsyncData` behave consistently - by only fetch data when the key changes if the data has already been fetched once. #### Reasons for Change This ensures consistent behavior between `useAsyncData` and `useFetch`, and prevents unexpected fetches. If you have set `immediate: false`, then you must call `refresh` or `execute` or data will never be fetched in `useFetch` or `useAsyncData`. #### Migration Steps This change should generally improve the expected behavior, but if you were expecting changing the key or options of a non-immediate `useFetch`, you now will need to trigger it manually the first time. ```diff const id = ref('123') const { data, execute } = await useFetch('/api/test', { query: { id }, immediate: false ) + watch(id, () => execute(), { once: true }) ``` To opt out of this behavior: ```ts // Or globally in your Nuxt config export default defineNuxtConfig({ experimental: { alwaysRunFetchOnKeyChange: true, }, }) ``` ### Shallow Data Reactivity in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal The `data` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a `shallowRef` rather than a `ref`. #### What Changed When new data is fetched, anything depending on `data` will still be reactive because the entire object is replaced. But if your code changes a property *within* that data structure, this will not trigger any reactivity in your app. #### Reasons for Change This brings a **significant** performance improvement for deeply nested objects and arrays because Vue does not need to watch every single property/array for modification. In most cases, `data` should also be immutable. #### Migration Steps In most cases, no migration steps are required, but if you rely on the reactivity of the data object then you have two options: 1. You can granularly opt in to deep reactivity on a per-composable basis: ```diff - const { data } = useFetch('/api/test') + const { data } = useFetch('/api/test', { deep: true }) ``` 2. You can change the default behavior on a project-wide basis (not recommended): ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { useAsyncData: { deep: true, }, }, }, }) ``` ::tip If you need to, you can automate this step by running `npx codemod@latest nuxt/4/shallow-function-reactivity` :: ### Absolute Watch Paths in `builder:watch` 🚦 **Impact Level**: Minimal #### What Changed The Nuxt `builder:watch` hook now emits a path which is absolute rather than relative to your project `srcDir`. #### Reasons for Change This allows us to support watching paths which are outside your `srcDir`, and offers better support for layers and other more complex patterns. #### Migration Steps We have already proactively migrated the public Nuxt modules which we are aware use this hook. See [issue #25339](https://github.com/nuxt/nuxt/issues/25339). However, if you are a module author using the `builder:watch` hook and wishing to remain backwards/forwards compatible, you can use the following code to ensure that your code works the same in both Nuxt v3 and Nuxt v4: ```diff + import { relative, resolve } from 'node:fs' // ... nuxt.hook('builder:watch', async (event, path) => { + path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path)) // ... }) ``` ::tip You can automate this step by running `npx codemod@latest nuxt/4/absolute-watch-path` :: ### Removal of `window.__NUXT__` object #### What Changed We are removing the global `window.__NUXT__` object after the app finishes hydration. #### Reasons for Change This opens the way to multi-app patterns ([#21635](https://github.com/nuxt/nuxt/issues/21635)) and enables us to focus on a single way to access Nuxt app data - `useNuxtApp()`. #### Migration Steps The data is still available, but can be accessed with `useNuxtApp().payload`: ```diff - console.log(window.__NUXT__) + console.log(useNuxtApp().payload) ``` ### Directory index scanning 🚦 **Impact Level**: Medium #### What Changed Child folders in your `app/middleware/` folder are also scanned for `index` files and these are now also registered as middleware in your project. #### Reasons for Change Nuxt scans a number of folders automatically, including `app/middleware/` and `app/plugins/`. Child folders in your `app/plugins/` folder are scanned for `index` files and we wanted to make this behavior consistent between scanned directories. #### Migration Steps Probably no migration is necessary but if you wish to revert to previous behavior you can add a hook to filter out these middleware: ```ts export default defineNuxtConfig({ hooks: { 'app:resolve' (app) { app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path)) }, }, }) ``` ### Template Compilation Changes 🚦 **Impact Level**: Minimal #### What Changed Previously, Nuxt used `lodash/template` to compile templates located on the file system using the `.ejs` file format/syntax. In addition, we provided some template utilities (`serialize`, `importName`, `importSources`) which could be used for code-generation within these templates, which are now being removed. #### Reasons for Change In Nuxt v3 we moved to a 'virtual' syntax with a `getContents()` function which is much more flexible and performant. In addition, `lodash/template` has had a succession of security issues. These do not really apply to Nuxt projects because it is being used at build-time, not runtime, and by trusted code. However, they still appear in security audits. Moreover, `lodash` is a hefty dependency and is unused by most projects. Finally, providing code serialization functions directly within Nuxt is not ideal. Instead, we maintain projects like [unjs/knitwork](https://github.com/unjs/knitwork) which can be dependencies of your project, and where security issues can be reported/resolved directly without requiring an upgrade of Nuxt itself. #### Migration Steps We have raised PRs to update modules using EJS syntax, but if you need to do this yourself, you have three backwards/forwards-compatible alternatives: - Moving your string interpolation logic directly into `getContents()`. - Using a custom function to handle the replacement, such as in . - Use `es-toolkit/compat` (a drop-in replacement for lodash template), as a dependency of *your* project rather than Nuxt: ```diff + import { readFileSync } from 'node:fs' + import { template } from 'es-toolkit/compat' // ... addTemplate({ fileName: 'appinsights-vue.js' options: { /* some options */ }, - src: resolver.resolve('./runtime/plugin.ejs'), + getContents({ options }) { + const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8') + return template(contents)({ options }) + }, }) ``` Finally, if you are using the template utilities (`serialize`, `importName`, `importSources`), you can replace them as follows with utilities from `knitwork`: ```ts import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork' const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"\{(.+)\}"(?=,?$)/gm, r => JSON.parse(r).replace(/^\{(.*)\}$/, '$1')) const importSources = (sources: string | string[], { lazy = false } = {}) => { return toArray(sources).map((src) => { if (lazy) { return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}` } return genImport(src, genSafeVariableName(src)) }).join('\n') } const importName = genSafeVariableName ``` ::tip You can automate this step by running `npx codemod@latest nuxt/4/template-compilation-changes` :: ### Default TypeScript Configuration Changes 🚦 **Impact Level**: Minimal #### What Changed `compilerOptions.noUncheckedIndexedAccess` is now `true` instead of `false`. #### Reasons for Change This change is a follow up to a prior [3.12 config update](https://github.com/nuxt/nuxt/pull/27485) where we improved our defaults, mostly adhering to [TotalTypeScript's recommendations](https://www.totaltypescript.com/tsconfig-cheat-sheet). #### Migration Steps There are two approaches: 1. Run a typecheck on your app and fix any new errors (recommended). 2. Override the new default in your `nuxt.config.ts`: ```ts export default defineNuxtConfig({ typescript: { tsConfig: { compilerOptions: { noUncheckedIndexedAccess: false, }, }, }, }) ``` ### TypeScript Configuration Splitting 🚦 **Impact Level**: Minimal #### What Changed Nuxt now generates separate TypeScript configurations for different contexts to provide better type-checking experiences: 1. **New TypeScript configuration files**: Nuxt now generates additional TypeScript configurations: - `.nuxt/tsconfig.app.json` - For your app code (Vue components, composables, etc.) - `.nuxt/tsconfig.server.json` - For your server-side code (Nitro/server directory) - `.nuxt/tsconfig.node.json` - For your build-time code (modules, `nuxt.config.ts`, etc.) - `.nuxt/tsconfig.shared.json` - For code shared between app and server contexts (like types and non-environment specific utilities) - `.nuxt/tsconfig.json` - Legacy configuration for backward compatibility 2. **Backward compatibility**: Existing projects that extend `.nuxt/tsconfig.json` will continue to work as before. 3. **Opt-in project references**: New projects or those wanting better type checking can adopt TypeScript's project references feature. 4. **Context-specific type checking**: Each context now has appropriate compiler options and includes/excludes for its specific environment. 5. **New `typescript.nodeTsConfig` option**: You can now customize the TypeScript configuration for Node.js build-time code. #### Reasons for Change This change provides several benefits: 1. **Better type safety**: Each context (app, server, build-time) gets appropriate type checking with context-specific globals and APIs. 2. **Improved IDE experience**: Better IntelliSense and error reporting for different parts of your codebase. 3. **Cleaner separation**: Server code won't incorrectly suggest client-side APIs and vice versa. 4. **Performance**: TypeScript can more efficiently check code with properly scoped configurations. For example, auto-imports are not available in your `nuxt.config.ts` (but previously this was not flagged by TypeScript). And while IDEs recognized the separate context hinted by `tsconfig.json` in your `server/` directory, this was not reflected in type-checking (requiring a separate step). #### Migration Steps **No migration is required** - existing projects will continue to work as before. However, to take advantage of improved type checking, you can opt in to the new project references approach: 1. **Update your root `tsconfig.json`** to use project references: ::note If your `tsconfig.json` currently has an `"extends": "./.nuxt/tsconfig.json"` line, **remove it** before adding the references. Project references and extends are mutually exclusive. :: ```json { // Remove "extends": "./.nuxt/tsconfig.json" if present "files": [], "references": [ { "path": "./.nuxt/tsconfig.app.json" }, { "path": "./.nuxt/tsconfig.server.json" }, { "path": "./.nuxt/tsconfig.shared.json" }, { "path": "./.nuxt/tsconfig.node.json" } ] } ``` 2. **Remove any manual server `tsconfig.json`** files (like `server/tsconfig.json`) that extended `.nuxt/tsconfig.server.json`. 3. **Update your type checking scripts** to use the build flag for project references: ```diff - "typecheck": "nuxt prepare && vue-tsc --noEmit" + "typecheck": "nuxt prepare && vue-tsc -b --noEmit" ``` 4. **Move all type augmentations into their appropriate context**: - If you are augmenting types for the app context, move the files to the `app/` directory. - If you are augmenting types for the server context, move the files to the `server/` directory. - If you are augmenting types that are **shared between the app and server**, move the files to the `shared/` directory. ::warning Augmenting types from outside the `app/`, `server/`, or `shared/` directories will not work with the new project references setup. :: 5. **Configure TypeScript options** if needed: ```ts export default defineNuxtConfig({ typescript: { // customize tsconfig.app.json tsConfig: { // ... }, // customize tsconfig.shared.json sharedTsConfig: { // ... }, // customize tsconfig.node.json nodeTsConfig: { // ... }, }, nitro: { typescript: { // customize tsconfig.server.json tsConfig: { // ... }, }, }, }) ``` 6. **Update any CI/build scripts** that run TypeScript checking to ensure they use the new project references approach. The new configuration provides better type safety and IntelliSense for projects that opt in, while maintaining full backward compatibility for existing setups. ### Removal of Experimental Features 🚦 **Impact Level**: Minimal #### What Changed Four experimental features are no longer configurable in Nuxt 4: - `experimental.treeshakeClientOnly` will be `true` (default since v3.0) - `experimental.configSchema` will be `true` (default since v3.3) - `experimental.polyfillVueUseHead` will be `false` (default since v3.4) - `experimental.respectNoSSRHeader` will be `false` (default since v3.4) - `vite.devBundler` is no longer configurable - it will use `vite-node` by default #### Reasons for Change These options have been set to their current values for some time and we do not have a reason to believe that they need to remain configurable. #### Migration Steps - `polyfillVueUseHead` is implementable in user-land with [this plugin](https://github.com/nuxt/nuxt/blob/f209158352b09d1986aa320e29ff36353b91c358/packages/nuxt/src/head/runtime/plugins/vueuse-head-polyfill.ts#L10-L11) - `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9) ### Removal of Top-Level `generate` Configuration 🚦 **Impact Level**: Minimal #### What Changed The top-level `generate` configuration option is no longer available in Nuxt 4. This includes all of its properties: - `generate.exclude` - for excluding routes from prerendering - `generate.routes` - for specifying routes to prerender #### Reasons for Change The top level `generate` configuration was a holdover from Nuxt 2. We've supported `nitro.prerender` for a while now, and it is the preferred way to configure prerendering in Nuxt 3+. #### Migration Steps Replace `generate` configuration with the corresponding `nitro.prerender` options: ```diff export default defineNuxtConfig({ - generate: { - exclude: ['/admin', '/private'], - routes: ['/sitemap.xml', '/robots.txt'] - } + nitro: { + prerender: { + ignore: ['/admin', '/private'], + routes: ['/sitemap.xml', '/robots.txt'] + } + } }) ``` ::read-more{to="https://nitro.build/config#prerender"} Read more about Nitro's prerender configuration options. :: ### Normalized Page Component Names 🚦 **Impact Level**: Minimal #### What Changed When `future.compatibilityVersion` is set to `5` (or `experimental.normalizePageNames` is enabled), page component names match their route names instead of using the filename. For example, `pages/foo/index.vue` will have the component name `foo` instead of `index`. #### Reasons for Change Previously, Vue assigned component names based on the filename. This meant multiple pages like `pages/foo/index.vue` and `pages/bar/index.vue` would both have the component name `index`. This made `` with `include`/`exclude` filters unreliable and required manually adding `defineOptions({ name: '...' })` to each page. #### Migration Steps If you rely on the current component names (e.g. in `` `include`/`exclude` lists), update them to use route names instead of filenames. ```diff ``` To disable this behavior: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { normalizePageNames: false, }, }) ``` ## Nuxt 2 vs. Nuxt 3+ In the table below, there is a quick comparison between 3 versions of Nuxt: | Feature / Version | Nuxt 2 | Nuxt Bridge | Nuxt 3+ | | ----------------------- | ---------- | ----------- | ---------- | | Vue | 2 | 2 | 3 | | Stability | 😊 Stable | 😊 Stable | 😊 Stable | | Performance | 🏎 Fast | ✈️ Faster | 🚀 Fastest | | Nitro Engine | ❌ | ✅ | ✅ | | ESM support | 🌙 Partial | 👍 Better | ✅ | | TypeScript | ☑️ Opt-in | 🚧 Partial | ✅ | | Composition API | ❌ | 🚧 Partial | ✅ | | Options API | ✅ | ✅ | ✅ | | Components Auto Import | ✅ | ✅ | ✅ | | ` ``` ::important If you are using `resolveComponent` to handle dynamic components, make sure not to insert anything but the name of the component, which must be a literal string and not be or contain a variable. The string is statically analyzed at the compilation step. :: ::video-accordion{title="Watch Daniel Roe's short video about resolveComponent()" videoId="4kq8E5IUM2U"} :: Alternatively, though not recommended, you can register all your components globally, which will create async chunks for all your components and make them available throughout your application. ```diff export default defineNuxtConfig({ components: { + global: true, + dirs: ['~/components'] }, }) ``` You can also selectively register some components globally by placing them in a `~/components/global` directory, or by using a `.global.vue` suffix in the filename. As noted above, each global component is rendered in a separate chunk, so be careful not to overuse this feature. ::note The `global` option can also be set per component directory. :: ## Dynamic Imports To dynamically import a component (also known as lazy-loading a component) all you need to do is add the `Lazy` prefix to the component's name. This is particularly useful if the component is not always needed. By using the `Lazy` prefix you can delay loading the component code until the right moment, which can be helpful for optimizing your JavaScript bundle size. ```vue [app/pages/index.vue] ``` ## Delayed (or Lazy) Hydration Lazy components are great for controlling the chunk sizes in your app, but they don't always enhance runtime performance, as they still load eagerly unless conditionally rendered. In real-world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive as soon as the page is loaded. Having them all load eagerly can negatively impact performance. In order to optimize your app, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks. Nuxt supports this using lazy (or delayed) hydration, allowing you to control when components become interactive. ### Hydration Strategies Nuxt provides a range of built-in hydration strategies. Only one strategy can be used per lazy component. ::note Any prop change on a lazily hydrated component will trigger hydration immediately. (e.g., changing a prop on a component with `hydrate-never` will cause it to hydrate) :: ::warning Currently Nuxt's built-in lazy hydration only works in single-file components (SFCs), and requires you to define the prop in the template (rather than spreading an object of props via `v-bind`). It also does not work with direct imports from `#components`. :: #### `hydrate-on-visible` Hydrates the component when it becomes visible in the viewport. ```vue [app/pages/index.vue] ``` ::read-more{to="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver" title="IntersectionObserver options"} Read more about the options for `hydrate-on-visible`. :: ::note Under the hood, this uses Vue's built-in [`hydrateOnVisible` strategy](https://vuejs.org/guide/components/async#hydrate-on-visible). :: #### `hydrate-on-idle` Hydrates the component when the browser is idle. This is suitable if you need the component to load as soon as possible, but not block the critical rendering path. You can also pass a number which serves as a max timeout. ```vue [app/pages/index.vue] ``` ::note Under the hood, this uses Vue's built-in [`hydrateOnIdle` strategy](https://vuejs.org/guide/components/async#hydrate-on-idle). :: #### `hydrate-on-interaction` Hydrates the component after a specified interaction (e.g., click, mouseover). ```vue [app/pages/index.vue] ``` If you do not pass an event or list of events, it defaults to hydrating on `pointerenter`, `click` and `focus`. ::note Under the hood, this uses Vue's built-in [`hydrateOnInteraction` strategy](https://vuejs.org/guide/components/async#hydrate-on-interaction). :: #### `hydrate-on-media-query` Hydrates the component when the window matches a media query. ```vue [app/pages/index.vue] ``` ::note Under the hood, this uses Vue's built-in [`hydrateOnMediaQuery` strategy](https://vuejs.org/guide/components/async#hydrate-on-media-query). :: #### `hydrate-after` Hydrates the component after a specified delay (in milliseconds). ```vue [app/pages/index.vue] ``` #### `hydrate-when` Hydrates the component based on a boolean condition. ```vue [app/pages/index.vue] ``` #### `hydrate-never` Never hydrates the component. ```vue [app/pages/index.vue] ``` ### Listening to Hydration Events All delayed hydration components emit a `@hydrated` event when they are hydrated. ```vue [app/pages/index.vue] ``` ### Caveats and Best Practices Delayed hydration can offer performance benefits, but it's essential to use it correctly: 1. **Prioritize In-Viewport Content:** Avoid delayed hydration for critical, above-the-fold content. It's best suited for content that isn't immediately needed. 2. **Conditional Rendering:** When using `v-if="false"` on a lazy component, you might not need delayed hydration. You can just use a normal lazy component. 3. **Shared State:** Be mindful of shared state (`v-model`) across multiple components. Updating the model in one component can trigger hydration in all components bound to that model. 4. **Use Each Strategy's Intended Use Case:** Each strategy is optimized for a specific purpose. - `hydrate-when` is best for components that might not always need to be hydrated. - `hydrate-after` is for components that can wait a specific amount of time. - `hydrate-on-idle` is for components that can be hydrated when the browser is idle. 5. **Avoid `hydrate-never` on interactive components:** If a component requires user interaction, it should not be set to never hydrate. ## Direct Imports You can also explicitly import components from `#components` if you want or need to bypass Nuxt's auto-importing functionality. ```vue [app/pages/index.vue] ``` ## Custom Directories By default, only the `~/components` directory is scanned. If you want to add other directories, or change how the components are scanned within a subfolder of this directory, you can add additional directories to the configuration: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: [ // ~/calendar-module/components/event/Update.vue => { path: '~/calendar-module/components' }, // ~/user-module/components/account/UserDeleteDialog.vue => { path: '~/user-module/components', pathPrefix: false }, // ~/components/special-components/Btn.vue => { path: '~/components/special-components', prefix: 'Special' }, // It's important that this comes last if you have overrides you wish to apply // to sub-directories of `~/components`. // // ~/components/Btn.vue => // ~/components/base/Btn.vue => '~/components', ], }) ``` ::note Any nested directories need to be added first as they are scanned in order. :: Each directory entry also accepts `pattern` and `ignore` glob options, which control which files are scanned within `path`. This is useful when your components live in nested folders that don't follow the default layout, such as a domain-driven structure: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: [ // ~/domains/blog/components/PostCard.vue => { path: '~/domains', pattern: '*/components/**', pathPrefix: false, }, ], }) ``` ::note If `pattern` is specified, the `extensions` option has no effect, so make sure your pattern matches the file extensions you want to scan. :: ## npm Packages If you want to auto-import components from an npm package, you can use [`addComponent`](https://nuxt.com/docs/4.x/api/kit/components#addcomponent) in a [local module](https://nuxt.com/docs/4.x/directory-structure/modules) to register them. ::code-group ```ts twoslash [~/modules/register-component.ts] import { addComponent, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { // import { MyComponent as MyAutoImportedComponent } from 'my-npm-package' addComponent({ name: 'MyAutoImportedComponent', export: 'MyComponent', filePath: 'my-npm-package', }) }, }) ``` ```vue [app/app.vue] ``` :: ## Component Extensions By default, any file with an extension specified in the [extensions key of `nuxt.config.ts`](https://nuxt.com/docs/4.x/api/nuxt-config#extensions) is treated as a component. If you need to restrict the file extensions that should be registered as components, you can use the extended form of the components directory declaration and its `extensions` key: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: [ { path: '~/components', extensions: ['.vue'], // [!code ++] }, ], }) ``` ## Client Components If a component is meant to be rendered only client-side, you can add the `.client` suffix to your component. ```bash [Directory Structure] | components/ --| Comments.client.vue ``` ```vue [app/pages/example.vue] ``` ::note This feature only works with Nuxt auto-imports and `#components` imports. Explicitly importing these components from their real paths does not convert them into client-only components. :: ::important `.client` components are rendered only after being mounted. To access the rendered template using `onMounted()`, add `await nextTick()` in the callback of the `onMounted()` hook. :: ::read-more{to="https://nuxt.com/docs/4.x/api/components/client-only"} You can also achieve a similar result with the `` component. :: ## Server Components Server components allow server-rendering individual components within your client-side apps. It's possible to use server components within Nuxt, even if you are generating a static site. That makes it possible to build complex sites that mix dynamic components, server-rendered HTML and even static chunks of markup. Server components can either be used on their own or paired with a [client component](https://nuxt.com/docs/4.x/directory-structure/app/components#paired-with-a-client-component). ::video-accordion{title="Watch Learn Vue video about Nuxt Server Components" videoId="u1yyXe86xJM"} :: ::tip{icon="i-lucide-newspaper" to="https://roe.dev/blog/nuxt-server-components" target="_blank"} Read Daniel Roe's guide to Nuxt Server Components. :: ### Standalone server components Standalone server components will always be rendered on the server, also known as Islands components. When their props update, this will result in a network request that will update the rendered HTML in-place. Register server-only components with the `.server` suffix and use them anywhere in your application automatically. ```bash [Directory Structure] -| components/ ---| HighlightedMarkdown.server.vue ``` ```vue [app/pages/example.vue] ``` Server-only components use [``](https://nuxt.com/docs/4.x/api/components/nuxt-island) under the hood, meaning that `lazy` prop and `#fallback` slot are both passed down to it. ::warning Server components (and islands) must have a single root element. (HTML comments are considered elements as well.) :: ::read-more{to="https://nuxt.com/docs/4.x/guide/concepts/server-components"} Read more about how islands are rendered, the isolated island context, selective hydration with `nuxt-client`, slots, caching and current limitations in the dedicated server components guide. :: ### Paired with a Client component In this case, the `.server` + `.client` components are two 'halves' of a component and can be used in advanced use cases for separate implementations of a component on server and client side. ```bash [Directory Structure] -| components/ ---| Comments.client.vue ---| Comments.server.vue ``` ```vue [app/pages/example.vue] ``` ## Built-In Nuxt Components There are a number of components that Nuxt provides, including `` and ``. You can read more about them in the API documentation. ::read-more{to="https://nuxt.com/docs/4.x/api"} :: ## Library Authors Making Vue component libraries with automatic tree-shaking and component registration is super easy. ✨ You can use the [`addComponentsDir`](https://nuxt.com/docs/4.x/api/kit/components#addcomponentsdir) method provided from the `@nuxt/kit` to register your components directory in your Nuxt module. Imagine a directory structure like this: ```bash [Directory Structure] -| node_modules/ ---| awesome-ui/ -----| components/ -------| Alert.vue -------| Button.vue -----| nuxt.ts -| pages/ ---| index.vue -| nuxt.config.ts ``` Then in `awesome-ui/nuxt.ts` you can use the `addComponentsDir` hook: ```ts twoslash import { addComponentsDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const resolver = createResolver(import.meta.url) // Add ./components dir to the list addComponentsDir({ path: resolver.resolve('./components'), prefix: 'awesome', }) }, }) ``` That's it! Now in your project, you can import your UI library as a Nuxt module in your `nuxt.config` file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ modules: ['awesome-ui/nuxt'], }) ``` ... and directly use the module components (prefixed with `awesome-`) in our `app/pages/index.vue`: ```vue ``` It will automatically import the components only if used and also support HMR when updating your components in `node_modules/awesome-ui/components/`. ::link-example{to="https://nuxt.com/docs/examples/features/auto-imports"} :: # composables > Use the composables/ directory to auto-import your Vue composables into your application. ## Usage **Method 1:** Using named export ```ts [app/composables/useFoo.ts] export const useFoo = () => { return useState('foo', () => 'bar') } ``` **Method 2:** Using default export ```ts [app/composables/use-foo.ts or composables/useFoo.ts] // It will be available as useFoo() (camelCase of file name without extension) export default function () { return useState('foo', () => 'bar') } ``` **Usage:** You can now use auto imported composable in `.js`, `.ts` and `.vue` files ```vue [app/app.vue] ``` ::note The `app/composables/` directory in Nuxt does not provide any additional reactivity capabilities to your code. Instead, any reactivity within composables is achieved using Vue's Composition API mechanisms, such as ref and reactive. Note that reactive code is also not limited to the boundaries of the `app/composables/` directory. You are free to employ reactivity features wherever they're needed in your application. :: ::read-more{to="https://nuxt.com/docs/4.x/guide/concepts/auto-imports"} :: ::link-example{to="https://nuxt.com/docs/examples/features/auto-imports"} :: ## Types Under the hood, Nuxt auto generates the file `.nuxt/imports.d.ts` to declare the types. Be aware that you have to run [`nuxt prepare`](https://nuxt.com/docs/4.x/api/commands/prepare), [`nuxt dev`](https://nuxt.com/docs/4.x/api/commands/dev) or [`nuxt build`](https://nuxt.com/docs/4.x/api/commands/build) in order to let Nuxt generate the types. ::note If you create a composable without having the dev server running, TypeScript will throw an error, such as `Cannot find name 'useBar'.` :: ## Example ### Nested Composables You can use a composable within another composable using auto imports: ```ts [app/composables/test.ts] export const useFoo = () => { const nuxtApp = useNuxtApp() const bar = useBar() } ``` ### Access Plugin Injections You can access [plugin injections](https://nuxt.com/docs/4.x/directory-structure/app/plugins#providing-helpers) from composables: ```ts [app/composables/test.ts] export const useHello = () => { const nuxtApp = useNuxtApp() return nuxtApp.$hello } ``` ## How Files Are Scanned Nuxt only scans files at the top level of the [`app/composables/` directory](https://nuxt.com/docs/4.x/directory-structure/app/composables), e.g.: ```bash [Directory Structure] -| composables/ ---| index.ts // scanned ---| useFoo.ts // scanned ---| nested/ -----| utils.ts // not scanned ``` Only `app/composables/index.ts` and `app/composables/useFoo.ts` would be searched for imports. To get auto imports working for nested modules, you could either re-export them (recommended) or configure the scanner to include nested directories: **Example:** Re-export the composables you need from the `app/composables/index.ts` file: ```ts [app/composables/index.ts] // Enables auto import for this export export { utils } from './nested/utils.ts' ``` **Example:** Scan nested directories inside the `app/composables/` folder: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ imports: { dirs: [ // Scan top-level composables '~/composables', // ... or scan composables nested one level deep with a specific name and file extension '~/composables/*/index.{ts,js,mjs,mts}', // ... or scan all composables within given directory '~/composables/**', ], }, }) ``` # layouts > Nuxt provides a layouts framework to extract common UI patterns into reusable layouts. ::tip{icon="i-lucide-rocket"} For best performance, components placed in this directory will be automatically loaded via asynchronous import when used. :: ## Enable Layouts Layouts are enabled by adding [``](https://nuxt.com/docs/4.x/api/components/nuxt-layout) to your [`app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app): ```vue [app/app.vue] ``` To use a layout: - Set a `layout` property in your page with [definePageMeta](https://nuxt.com/docs/4.x/api/utils/define-page-meta). - Set the `name` prop of ``. - Set the `appLayout` property in route rules. ::note The layout name is normalized to kebab-case, so `someLayout` becomes `some-layout`. :: ::note If no layout is specified, `app/layouts/default.vue` will be used. :: ::important If you only have a single layout in your application, we recommend using [`app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app) instead. :: ::important Unlike other components, your layouts must have a single root element to allow Nuxt to apply transitions between layout changes - and this root element cannot be a ``. :: ## Default Layout Add a `~/layouts/default.vue`: ```vue [app/layouts/default.vue] ``` In a layout file, the content of the page will be displayed in the `` component. ## Named Layout ```bash [Directory Structure] -| layouts/ ---| default.vue ---| custom.vue ``` Then you can use the `custom` layout in your page: ```vue twoslash [pages/about.vue] ``` ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/pages#page-metadata"} Learn more about `definePageMeta`. :: You can directly override the default layout for all pages using the `name` property of [``](https://nuxt.com/docs/4.x/api/components/nuxt-layout): ```vue [app/app.vue] ``` If you have a layout in nested directories, the layout's name will be based on its own path directory and filename, with duplicate segments being removed. | File | Layout Name | | --------------------------------- | ----------------- | | `~/layouts/desktop/default.vue` | `desktop-default` | | `~/layouts/desktop-base/base.vue` | `desktop-base` | | `~/layouts/desktop/index.vue` | `desktop` | For clarity, we recommend that the layout's filename matches its name: | File | Layout Name | | ---------------------------------------- | ----------------- | | `~/layouts/desktop/DesktopDefault.vue` | `desktop-default` | | `~/layouts/desktop-base/DesktopBase.vue` | `desktop-base` | | `~/layouts/desktop/Desktop.vue` | `desktop` | ::link-example{to="https://nuxt.com/docs/examples/features/layouts"} :: ## Changing the Layout Dynamically You can also use the [`setPageLayout`](https://nuxt.com/docs/4.x/api/utils/set-page-layout) helper to change the layout dynamically: ```vue twoslash [app/pages/index.vue] ``` You can also set layouts for specific routes using the `appLayout` property in route rules :badge[v4.3]{color="info" size="xs" .align-middle}: ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Set layout for specific route '/admin': { appLayout: 'admin' }, // Set layout for multiple routes '/dashboard/**': { appLayout: 'dashboard' }, // Disable layout for a route '/landing': { appLayout: false }, }, }) ``` ::tip This is useful when you want to manage layouts centrally in your configuration rather than in each page file, or when you need to apply layouts to routes that don't have corresponding page components (such as catchall pages which might match many paths). :: ::link-example{to="https://nuxt.com/docs/examples/features/layouts"} :: ## Passing Props to Layouts :badge[+4.4]{color="primary" .align-middle} You can pass props to layouts in several ways. ### Via `definePageMeta` Use the object syntax for the `layout` property to pass props directly from your page: ::code-group ```vue [app/pages/dashboard.vue] ``` ```vue [app/layouts/panel.vue] ``` :: ::tip Props are fully typed based on your layout's `defineProps`. You'll get autocomplete and type-checking in your editor. :: ### Via `setPageLayout` You can also pass props when changing the layout dynamically with [`setPageLayout`](https://nuxt.com/docs/4.x/api/utils/set-page-layout): ```ts setPageLayout('panel', { sidebar: true, title: 'Dashboard' }) ``` ## Overriding a Layout on a Per-page Basis If you are using pages, you can take full control by setting `layout: false` and then using the `` component within the page. ::code-group ```vue [app/pages/index.vue] ``` ```vue [app/layouts/custom.vue] ``` :: ::important If you use `` within your pages, make sure it is not the root element (or [disable layout/page transitions](https://nuxt.com/docs/4.x/getting-started/transitions#disable-transitions)). :: # middleware > Nuxt provides middleware to run code before navigating to a particular route. Nuxt provides a customizable **route middleware** framework you can use throughout your application, ideal for extracting code that you want to run before navigating to a particular route. There are three kinds of route middleware: 1. Anonymous (or inline) route middleware are defined directly within the page. 2. Named route middleware, placed in the `app/middleware/` and automatically loaded via asynchronous import when used on a page. 3. Global route middleware, placed in the `app/middleware/` with a `.global` suffix and is run on every route change. The first two kinds of route middleware can be defined in [`definePageMeta`](https://nuxt.com/docs/4.x/api/utils/define-page-meta). ::note Name of middleware are normalized to kebab-case: `myMiddleware` becomes `my-middleware`. :: ::note Only files at the top level of the directory (or index files within any subdirectories) are registered. An index file takes its name from the folder that contains it: `middleware/auth/index.ts` is registered as `auth`. :: ::note Route middleware run within the Vue part of your Nuxt app. Despite the similar name, they are completely different from [server middleware](https://nuxt.com/docs/4.x/directory-structure/server#server-middleware), which are run in the Nitro server part of your app. :: ::video-accordion{title="Watch a video from Vue School on all 3 kinds of middleware" videoId="761471577" platform="vimeo"} :: ## Usage Route middleware are navigation guards that receive the current route and the next route as arguments. ```ts twoslash [middleware/my-middleware.ts] export default defineNuxtRouteMiddleware((to, from) => { if (to.params.id === '1') { return abortNavigation() } // In a real app you would probably not redirect every route to `/` // however it is important to check `to.path` before redirecting or you // might get an infinite redirect loop if (to.path !== '/') { return navigateTo('/') } }) ``` Nuxt provides two globally available helpers that can be returned directly from the middleware. 1. [`navigateTo`](https://nuxt.com/docs/4.x/api/utils/navigate-to) - Redirects to the given route 2. [`abortNavigation`](https://nuxt.com/docs/4.x/api/utils/abort-navigation) - Aborts the navigation, with an optional error message. Unlike [navigation guards](https://router.vuejs.org/guide/advanced/navigation-guards#Global-Before-Guards) from `vue-router`, a third `next()` argument is not passed, and **redirect or route cancellation is handled by returning a value from the middleware**. Possible return values are: - nothing (a simple `return` or no return at all) - does not block navigation and will move to the next middleware function, if any, or complete the route navigation - `return navigateTo('/')` - redirects to the given path and will set the redirect code to [`302` Found](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/302) if the redirect happens on the server side - `return navigateTo('/', { redirectCode: 301 })` - redirects to the given path and will set the redirect code to [`301` Moved Permanently](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/301) if the redirect happens on the server side - `return abortNavigation()` - stops the current navigation - `return abortNavigation(error)` - rejects the current navigation with an error ::read-more{to="https://nuxt.com/docs/4.x/api/utils/navigate-to"} :: ::read-more{to="https://nuxt.com/docs/4.x/api/utils/abort-navigation"} :: ::important We recommend using the helper functions above for performing redirects or stopping navigation. Other possible return values described in [the vue-router docs](https://router.vuejs.org/guide/advanced/navigation-guards#Global-Before-Guards) may work but there may be breaking changes in future. :: ## Middleware Order Middleware runs in the following order: 1. Global Middleware 2. Page defined middleware order (if there are multiple middleware declared with the array syntax) For example, assuming you have the following middleware and component: ```bash [app/middleware/ directory] -| middleware/ ---| analytics.global.ts ---| setup.global.ts ---| auth.ts ``` ```vue twoslash [pages/profile.vue] ``` You can expect the middleware to be run in the following order: 1. `analytics.global.ts` 2. `setup.global.ts` 3. Custom inline middleware 4. `auth.ts` ### Ordering Global Middleware By default, global middleware is executed alphabetically based on the filename. However, there may be times you want to define a specific order. For example, in the last scenario, `setup.global.ts` may need to run before `analytics.global.ts`. In that case, we recommend prefixing global middleware with 'alphabetical' numbering. ```bash [Directory structure] -| middleware/ ---| 01.setup.global.ts ---| 02.analytics.global.ts ---| auth.ts ``` ::note In case you're new to 'alphabetical' numbering, remember that filenames are sorted as strings, not as numeric values. For example, `10.new.global.ts` would come before `2.new.global.ts`. This is why the example prefixes single digit numbers with `0`. :: ## When Middleware Runs If your site is server-rendered or generated, middleware for the initial page will be executed both when the page is rendered and then again on the client. This might be needed if your middleware needs a browser environment, such as if you have a generated site, aggressively cache responses, or want to read a value from local storage. However, if you want to avoid this behavior you can do so: ```ts twoslash [middleware/example.ts] export default defineNuxtRouteMiddleware((to) => { // skip middleware on server if (import.meta.server) { return } // skip middleware on client side entirely if (import.meta.client) { return } // or only skip middleware on initial client load const nuxtApp = useNuxtApp() if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) { return } }) ``` This is true even if you throw an error in your middleware on the server, and an error page is rendered. The middleware will still run again in the browser. ::note Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](https://nuxt.com/docs/4.x/getting-started/error-handling#useerror) in middleware to check if an error is being handled. :: ## Accessing Route in Middleware Always use the `to` and `from` parameters in your middleware to access the next and previous routes. Avoid using the [`useRoute()`](https://nuxt.com/docs/4.x/api/composables/use-route) composable in this context altogether. There is **no concept of a "current route" in middleware**, as middleware can abort a navigation or redirect to a different route. The `useRoute()` composable will always be inaccurate in this context. ::warning Sometimes, you might call a composable that uses `useRoute()` internally, which can trigger this warning even if there is no direct call in your middleware. This leads to the **same issue as above**, so you should structure your functions to accept the route as an argument instead when they are used in middleware. :: ::code-group ```ts twoslash [middleware/access-route.ts] // @errors: 2304 export default defineNuxtRouteMiddleware((to) => { // passing the route to the function to avoid calling `useRoute()` in middleware doSomethingWithRoute(to) // ❌ this will output a warning and is NOT recommended callsRouteInternally() }) ``` ```ts twoslash [utils/handle-route.ts] // providing the route as an argument so that it can be used in middleware correctly export function doSomethingWithRoute (route = useRoute()) { // ... } ``` ```ts twoslash [utils/dont-do-this.ts] // ❌ this function is not suitable for use in middleware export function callsRouteInternally () { const route = useRoute() // ... } ``` :: ## Adding Middleware Dynamically It is possible to add global or named route middleware manually using the [`addRouteMiddleware()`](https://nuxt.com/docs/4.x/api/utils/add-route-middleware) helper function, such as from within a plugin. ```ts twoslash export default defineNuxtPlugin(() => { addRouteMiddleware('global-test', () => { console.log('this global middleware was added in a plugin and will be run on every route change') }, { global: true }) addRouteMiddleware('named-test', () => { console.log('this named middleware was added in a plugin and would override any existing middleware of the same name') }) }) ``` ## Example ```bash [Directory Structure] -| middleware/ ---| auth.ts ``` In your page file, you can reference this route middleware: ```vue twoslash ``` Now, before navigation to that page can complete, the `auth` route middleware will be run. ::link-example{to="https://nuxt.com/docs/examples/routing/middleware"} :: ## Setting Middleware at Build Time Instead of using `definePageMeta` on each page, you can add named route middleware within the `pages:extend` hook. ```ts twoslash [nuxt.config.ts] import type { NuxtPage } from 'nuxt/schema' export default defineNuxtConfig({ hooks: { 'pages:extend' (pages) { function setMiddleware (pages: NuxtPage[]) { for (const page of pages) { if (/* some condition */ Math.random() > 0.5) { page.meta ||= {} // Note that this will override any middleware set in `definePageMeta` in the page page.meta.middleware = ['named'] } if (page.children) { setMiddleware(page.children) } } } setMiddleware(pages) }, }, }) ``` # pages > Nuxt provides file-based routing to create routes within your web application. ::note To reduce your application's bundle size, this directory is **optional**, meaning that [`vue-router`](https://router.vuejs.org) won't be included if you only use [`app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app). To force the pages system, set `pages: true` in `nuxt.config` or have a [`router.options.ts`](https://nuxt.com/docs/4.x/guide/recipes/custom-routing#using-routeroptions). :: ## Usage Pages are Vue components and can have any [valid extension](https://nuxt.com/docs/4.x/api/nuxt-config#extensions) that Nuxt supports (by default `.vue`, `.js`, `.jsx`, `.mjs`, `.ts` or `.tsx`). Nuxt will automatically create a route for every page in your `~/pages/` directory. ::tip Pages are only automatically registered for prerendering if you have not disabled `prerender.crawlLinks` and you have at least one page in your `prerender.routes` list. See the [prerendering guide](https://nuxt.com/docs/4.x/getting-started/prerendering). :: ::code-group ```vue [app/pages/index.vue] ``` ```ts twoslash [pages/index.ts] // https://vuejs.org/guide/extras/render-function.html export default defineComponent({ render () { return h('h1', 'Index page') }, }) ``` ```tsx twoslash [pages/index.tsx] // /docs/examples/advanced/jsx // https://vuejs.org/guide/extras/render-function.html#jsx-tsx export default defineComponent({ render () { return

Index page

}, }) ``` :: The `app/pages/index.vue` file will be mapped to the `/` route of your application. If you are using [`app.vue`](https://nuxt.com/docs/4.x/directory-structure/app/app), make sure to use the [``](https://nuxt.com/docs/4.x/api/components/nuxt-page) component to display the current page: ```vue [app/app.vue] ``` Pages **must have a single root element** to allow [route transitions](https://nuxt.com/docs/4.x/getting-started/transitions) between pages. HTML comments are considered elements as well. This means that when the route is server-rendered, or statically generated, you will be able to see its contents correctly, but when you navigate towards that route during client-side navigation the transition between routes will fail and you'll see that the route will not be rendered. Here are some examples to illustrate what a page with a single root element looks like: ::code-group ```vue [app/pages/working.vue] ``` ```vue [app/pages/bad-1.vue] ``` ```vue [app/pages/bad-2.vue] ``` :: ## Dynamic Routes If you place anything within square brackets, it will be turned into a [dynamic route](https://router.vuejs.org/guide/essentials/dynamic-matching) parameter. You can mix and match multiple parameters and even non-dynamic text within a file name or directory. If you want a parameter to be *optional*, you must enclose it in double square brackets - for example, `~/pages/[[slug]]/index.vue` or `~/pages/[[slug]].vue` will match both `/` and `/test`. ```bash [Directory Structure] -| pages/ ---| index.vue ---| users-[group]/ -----| [id].vue ``` Given the example above, you can access group/id within your component via the `$route` object: ```vue [app/pages/users-[group\\]/[id\\].vue] ``` Navigating to `/users-admins/123` would render: ```html

admins - 123

``` If you want to access the route using Composition API, there is a global [`useRoute`](https://nuxt.com/docs/4.x/api/composables/use-route) function that will allow you to access the route just like `this.$route` in the Options API. ```vue twoslash ``` ::note Named parent routes will take priority over nested dynamic routes. For the `/foo/hello` route, `~/pages/foo.vue` will take priority over `~/pages/foo/[slug].vue`. Use `~/pages/foo/index.vue` and `~/pages/foo/[slug].vue` to match `/foo` and `/foo/hello` with different pages,. :: ::video-accordion{title="Watch a video from Vue School on dynamic routes" videoId="754465699" platform="vimeo"} :: ## Catch-all Route If you need a catch-all route, you create it by using a file named like `[...slug].vue`. This will match *all* routes under that path. ```vue [app/pages/[...slug\\].vue] ``` Navigating to `/hello/world` would render: ```html

["hello", "world"]

``` ## Nested Routes It is possible to display [nested routes](https://router.vuejs.org/guide/essentials/nested-routes) with ``. Example: ```bash [Directory Structure] -| pages/ ---| parent/ -----| child.vue ---| parent.vue ``` This file tree will generate these routes: ```js [ { path: '/parent', component: '~/pages/parent.vue', name: 'parent', children: [ { path: 'child', component: '~/pages/parent/child.vue', name: 'parent-child', }, ], }, ] ``` To display the `child.vue` component, you have to insert the `` component inside `app/pages/parent.vue`: ```vue {}[pages/parent.vue] ``` ```vue {}[pages/parent/child.vue] ``` ### Child Route Keys If you want more control over when the `` component is re-rendered (for example, for transitions), you can either pass a string or function via the `pageKey` prop, or you can define a `key` value via `definePageMeta`: ```vue {}[pages/parent.vue] ``` Or alternatively: ```vue twoslash {}[pages/parent/child.vue] ``` ::link-example{to="https://nuxt.com/docs/examples/routing/pages"} :: ## Named Views :badge[v4.5]{color="info" size="xs" .align-middle} A single route can render into multiple `` outlets in a parent component by giving each outlet a `name` and providing a sibling page file for each name. Use the `name@view.vue` filename convention to declare a named view alongside the default route file: ```bash [Directory Structure] -| pages/ ---| parent/ -----| child.vue -----| child@sidebar.vue ---| parent.vue ``` Then render each outlet by name from the parent: ```vue [pages/parent.vue] ``` When the user navigates to `/parent/child`, `child.vue` renders into the default `` and `child@sidebar.vue` renders into ``. Outlets without a matching named view are left empty. ::note `definePageMeta` is read from the default route file only. Meta declared inside a `name@view.vue` sibling has no effect on the route. :: ::read-more{title="Named Views" to="https://router.vuejs.org/guide/essentials/named-views.html" target="_blank"} :: ## Route Groups In some cases, you may want to group a set of routes together in a way which doesn't affect file-based routing. For this purpose, you can put files in a folder which is wrapped in parentheses - `(` and `)`. For example: ```bash [Directory structure] -| pages/ ---| index.vue ---| (marketing)/ -----| about.vue -----| contact.vue ``` This will produce `/`, `/about` and `/contact` pages in your app. The `marketing` group is ignored for purposes of your URL structure. ### Accessing Route Groups :badge[v4.3]{color="info" size="xs" .align-middle} Route groups are automatically available in the route metadata as `route.meta.groups`. This allows you to access the group information in your components for conditional logic, styling, or other purposes. ```vue {}[pages/(marketing)/about.vue] ``` ## Page Metadata You might want to define metadata for each route in your app. You can do this using the `definePageMeta` macro, which will work both in ` ``` This data can then be accessed throughout the rest of your app from the `route.meta` object. ```vue twoslash ``` If you are using nested routes, the page metadata from all these routes will be merged into a single object. For more on route meta, see the [vue-router docs](https://router.vuejs.org/guide/advanced/meta). Much like `defineEmits` or `defineProps` (see [Vue docs](https://vuejs.org/api/sfc-script-setup#defineprops-defineemits)), `definePageMeta` is a **compiler macro**. It will be compiled away so you cannot reference it within your component. Instead, the metadata passed to it will be hoisted out of the component. Therefore, the page meta object cannot reference the component. However, it can reference imported bindings, as well as locally defined **pure functions**. ::warning Make sure not to reference any reactive data or functions that cause side effects. This can lead to unexpected behavior. :: ```vue ``` ### Special Metadata Of course, you are welcome to define metadata for your own use throughout your app. But some metadata defined with `definePageMeta` has a particular purpose: #### `alias` You can define page aliases. They allow you to access the same page from different paths. It can be either a string or an array of strings as defined [in the vue-router documentation](https://router.vuejs.org/guide/essentials/redirect-and-alias#Alias). #### `keepalive` Nuxt will automatically wrap your page in [the Vue `` component](https://vuejs.org/guide/built-ins/keep-alive#keepalive) if you set `keepalive: true` in your `definePageMeta`. This might be useful to do, for example, in a parent route that has dynamic child routes, if you want to preserve page state across route changes. When your goal is to preserve state for parent routes use this syntax: ``. You can also set props to be passed to `` (see [a full list](https://vuejs.org/api/built-in-components#keepalive)). You can set a default value for this property [in your `nuxt.config`](https://nuxt.com/docs/4.x/api/nuxt-config#keepalive). #### `key` [See above](https://nuxt.com/docs/4.x/directory-structure/app/pages#child-route-keys). #### `layout` You can define the layout used to render the route. This can be either false (to disable any layout), a string or a ref/computed, if you want to make it reactive in some way. [More about layouts](https://nuxt.com/docs/4.x/directory-structure/app/layouts). #### `layoutTransition` and `pageTransition` You can define transition properties for the `` component that wraps your pages and layouts, or pass `false` to disable the `` wrapper for that route. You can see [a list of options that can be passed](https://vuejs.org/api/built-in-components#transition) or read [more about how transitions work](https://vuejs.org/guide/built-ins/transition#transition). You can set default values for these properties [in your `nuxt.config`](https://nuxt.com/docs/4.x/api/nuxt-config#layouttransition). #### `middleware` You can define middleware to apply before loading this page. It will be merged with all the other middleware used in any matching parent/child routes. It can be a string, a function (an anonymous/inlined middleware function following [the global before guard pattern](https://router.vuejs.org/guide/advanced/navigation-guards#Global-Before-Guards)), or an array of strings/functions. [More about named middleware](https://nuxt.com/docs/4.x/directory-structure/app/middleware). #### `name` You may define a name for this page's route. #### `path` You may define a path matcher, if you have a more complex pattern than can be expressed with the file name. See [the `vue-router` docs](https://router.vuejs.org/guide/essentials/route-matching-syntax#Custom-regex-in-params) for more information. #### `props` Allows accessing the route `params` as props passed to the page component. See [the `vue-router` docs](https://router.vuejs.org/guide/essentials/passing-props) for more information. ### Typing Custom Metadata If you add custom metadata for your pages, you may wish to do so in a type-safe way. It is possible to augment the type of the object accepted by `definePageMeta`: ```ts [index.d.ts] declare module '#app' { interface PageMeta { pageType?: string } } // It is always important to ensure you import/export something when augmenting a type export {} ``` ## Navigation To navigate between pages of your app, you should use the [``](https://nuxt.com/docs/4.x/api/components/nuxt-link) component. This component is included with Nuxt and therefore you don't have to import it as you do with other components. A simple link to the `index.vue` page in your `app/pages` folder: ```vue ``` ::read-more{to="https://nuxt.com/docs/4.x/api/components/nuxt-link"} Learn more about `` usage. :: ## Programmatic Navigation Nuxt allows programmatic navigation through the `navigateTo()` utility method. Using this utility method, you will be able to programmatically navigate the user in your app. This is great for taking input from the user and navigating them dynamically throughout your application. In this example, we have a simple method called `navigate()` that gets called when the user submits a search form. ::note Make sure to always `await` on `navigateTo` or chain its result by returning from functions. :: ```vue twoslash ``` ## Client-Only Pages You can define a page as [client only](https://nuxt.com/docs/4.x/directory-structure/app/components#client-components) by giving it a `.client.vue` suffix. None of the content of this page will be rendered on the server. ## Server-Only Pages You can define a page as [server only](https://nuxt.com/docs/4.x/directory-structure/app/components#server-components) by giving it a `.server.vue` suffix. While you will be able to navigate to the page using client-side navigation, controlled by `vue-router`, it will be rendered with a server component automatically, meaning the code required to render the page will not be in your client-side bundle. ::warning Server-only pages must have a single root element. (HTML comments are considered elements as well.) :: Navigating to a server-only page needs a request to render it, so when a `` to one is prefetched, Nuxt prefetches that response as well. ::read-more{to="https://nuxt.com/docs/4.x/guide/concepts/server-components#the-client-navigation-round-trip"} :: ## Custom Routing As your app gets bigger and more complex, your routing might require more flexibility. For this reason, Nuxt directly exposes the router, routes and router options for customization in different ways. ::read-more{to="https://nuxt.com/docs/4.x/guide/recipes/custom-routing"} :: ## Multiple Pages Directories By default, all your pages should be in one `app/pages` directory at the root of your project. However, you can use [Nuxt Layers](https://nuxt.com/docs/4.x/getting-started/layers) to create groupings of your app's pages: ```bash [Directory Structure] -| some-app/ ---| nuxt.config.ts ---| pages/ -----| app-page.vue -| nuxt.config.ts ``` ```ts twoslash [some-app/nuxt.config.ts] // some-app/nuxt.config.ts export default defineNuxtConfig({ }) ``` ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ extends: ['./some-app'], }) ``` ::read-more{to="https://nuxt.com/docs/4.x/guide/going-further/layers"} :: # plugins > Nuxt has a plugins system to use Vue plugins and more at the creation of your Vue application. Nuxt automatically reads the files in the `app/plugins/` directory and loads them at the creation of the Vue application. ::note All plugins inside are auto-registered, you don't need to add them to your `nuxt.config` separately. :: ::note You can use `.server` or `.client` suffix in the file name to load a plugin only on the server or client side. :: ## Registered Plugins Only files at the top level of the directory (or index files within any subdirectories) will be auto-registered as plugins. ```bash [Directory structure] -| plugins/ ---| foo.ts // scanned ---| bar/ -----| baz.ts // not scanned -----| foz.vue // not scanned -----| index.ts // currently scanned but deprecated ``` Only `foo.ts` and `bar/index.ts` would be registered. To add plugins in subdirectories, you can use the [`app/plugins`](https://nuxt.com/docs/4.x/api/nuxt-config#plugins-1) option in `nuxt.config.ts`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ plugins: [ '~/plugins/bar/baz', '~/plugins/bar/foz', ], }) ``` ## Creating Plugins The only argument passed to a plugin is [`nuxtApp`](https://nuxt.com/docs/4.x/api/composables/use-nuxt-app). ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin((nuxtApp) => { // Doing something with nuxtApp }) ``` ### Object Syntax Plugins It is also possible to define a plugin using an object syntax, for more advanced use cases. For example: ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin({ name: 'my-plugin', enforce: 'pre', // or 'post' async setup (nuxtApp) { // this is the equivalent of a normal functional plugin }, hooks: { // You can directly register Nuxt app runtime hooks here 'app:created' () { const nuxtApp = useNuxtApp() // do something in the hook }, }, env: { // Set this value to `false` if you don't want the plugin to run when rendering server-only or island components. islands: true, }, }) ``` ::video-accordion{title="Watch a video from Alexander Lichter about the Object Syntax for Nuxt plugins" videoId="2aXZyXB1QGQ"} :: ::note If you are using the object-syntax, the properties are statically analyzed to produce a more optimized build. So you should not define them at runtime. For example, setting `enforce: import.meta.server ? 'pre' : 'post'` would defeat any future optimization Nuxt is able to do for your plugins. Nuxt does statically pre-load any hook listeners when using object-syntax, allowing you to define hooks without needing to worry about order of plugin registration. :: ## Registration Order You can control the order in which plugins are registered by prefixing with 'alphabetical' numbering to the file names. ```bash [Directory structure] plugins/ | - 01.myPlugin.ts | - 02.myOtherPlugin.ts ``` In this example, `02.myOtherPlugin.ts` will be able to access anything that was injected by `01.myPlugin.ts`. This is useful in situations where you have a plugin that depends on another plugin. ::note In case you're new to 'alphabetical' numbering, remember that filenames are sorted as strings, not as numeric values. For example, `10.myPlugin.ts` would come before `2.myOtherPlugin.ts`. This is why the example prefixes single digit numbers with `0`. :: ## Loading Strategy ### Parallel Plugins By default, Nuxt loads plugins sequentially. You can define a plugin as `parallel` so Nuxt won't wait until the end of the plugin's execution before loading the next plugin. ```ts twoslash [plugins/my-plugin.ts] export default defineNuxtPlugin({ name: 'my-plugin', parallel: true, async setup (nuxtApp) { // the next plugin will be executed immediately }, }) ``` ### Plugins With Dependencies If a plugin needs to wait for another plugin before it runs, you can add the plugin's name to the `dependsOn` array. ```ts twoslash [plugins/depending-on-my-plugin.ts] export default defineNuxtPlugin({ name: 'depends-on-my-plugin', dependsOn: ['my-plugin'], async setup (nuxtApp) { // this plugin will wait for the end of `my-plugin`'s execution before it runs }, }) ``` ## Using Composables You can use [composables](https://nuxt.com/docs/4.x/directory-structure/app/composables) as well as [utils](https://nuxt.com/docs/4.x/directory-structure/app/utils) within Nuxt plugins: ```ts [app/plugins/hello.ts] export default defineNuxtPlugin((nuxtApp) => { const foo = useFoo() }) ``` However, keep in mind there are some limitations and differences: ::important **If a composable depends on another plugin registered later, it might not work.** Plugins are called in order sequentially and before everything else. You might use a composable that depends on another plugin which has not been called yet. :: ::important **If a composable depends on the Vue.js lifecycle, it won't work.** Normally, Vue.js composables are bound to the current component instance while plugins are only bound to [`nuxtApp`](https://nuxt.com/docs/4.x/api/composables/use-nuxt-app) instance. :: ## Providing Helpers If you would like to provide a helper on the [`NuxtApp`](https://nuxt.com/docs/4.x/api/composables/use-nuxt-app) instance, return it from the plugin under a `provide` key. ::code-group ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin(() => { return { provide: { hello: (msg: string) => `Hello ${msg}!`, }, } }) ``` ```ts twoslash [plugins/hello-object-syntax.ts] export default defineNuxtPlugin({ name: 'hello', setup () { return { provide: { hello: (msg: string) => `Hello ${msg}!`, }, } }, }) ``` :: You can then use the helper in your components: ```vue [app/components/Hello.vue] ``` ::important Note that we highly recommend using [`composables`](https://nuxt.com/docs/4.x/directory-structure/app/composables) instead of providing helpers to avoid polluting the global namespace and keep your main bundle entry small. :: ::warning **If your plugin provides a `ref` or `computed`, it will not be unwrapped in a component ` ``` :: ::important 1. Make sure you use capital letters for these component names to distinguish them from native HTML elements (`` rather than `<title>`). 2. You can place these components anywhere in your template for your page. :: ### Options API ```vue [Nuxt 3 (Options API)] <script> // if using options API `head` method you must use `defineNuxtComponent` export default defineNuxtComponent({ head (nuxtApp) { // `head` receives the nuxt app but cannot access the component instance return { meta: [{ name: 'description', content: 'This is my page description.', }], } }, }) </script> ``` # Plugins and Middleware > Learn how to migrate from Nuxt 2 to Nuxt 3 plugins and middleware. ## Plugins Plugins now have a different format, and take only one argument (`nuxtApp`). ::code-group ```ts [Nuxt 2] export default (ctx, inject) => { inject('injected', () => 'my injected function') } ``` ```ts [Nuxt 3] export default defineNuxtPlugin((nuxtApp) => { // now available on `nuxtApp.$injected` nuxtApp.provide('injected', () => 'my injected function') // You can alternatively use this format, which comes with automatic type support return { provide: { injected: () => 'my injected function', }, } }) ``` :: ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/plugins"} :: ::read-more{to="https://nuxt.com/docs/4.x/api/composables/use-nuxt-app"} Read more about the format of `nuxtApp`. :: ### Migration 1. Migrate your plugins to use the `defineNuxtPlugin` helper function. 2. Remove any entries in your `nuxt.config` plugins array that are located in your `app/plugins/` folder. All files in this directory at the top level (and any index files in any subdirectories) will be automatically registered. Instead of setting `mode` to `client` or `server`, you can indicate this in the file name. For example, `~/plugins/my-plugin.client.ts` will only be loaded on client-side. ## Route Middleware Route middleware has a different format. ::code-group ```js [Nuxt 2] export default function ({ store, redirect }) { // If the user is not authenticated if (!store.state.authenticated) { return redirect('/login') } } ``` ```ts [Nuxt 3] export default defineNuxtRouteMiddleware((to, from) => { const auth = useState('auth') if (!auth.value.authenticated) { return navigateTo('/login') } }) ``` :: Much like Nuxt 2, route middleware placed in your `~/middleware` folder is automatically registered. You can then specify it by name in a component. However, this is done with `definePageMeta` rather than as a component option. `navigateTo` is one of a number of route helper functions. ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/middleware"} :: ### Migration 1. Migrate your route middleware to use the `defineNuxtRouteMiddleware` helper function. 2. Any global middleware (such as in your `nuxt.config`) can be placed in your `~/middleware` folder with a `.global` extension, for example `~/middleware/auth.global.ts`. # Pages and Layouts > Learn how to migrate from Nuxt 2 to Nuxt 3 pages and layouts. ## `app.vue` Nuxt 3 provides a central entry point to your app via `~/app.vue`. ::note If you don't have an `app.vue` file in your source directory, Nuxt will use its own default version. :: This file is a great place to put any custom code that needs to be run once when your app starts up, as well as any components that are present on every page of your app. For example, if you only have one layout, you can move this to `app.vue` instead. ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/app"} :: ::link-example{to="https://nuxt.com/docs/examples/hello-world"} :: ### Migration Consider creating an `app.vue` file and including any logic that needs to run once at the top-level of your app. You can check out [an example here](https://nuxt.com/docs/4.x/directory-structure/app/app). ## Layouts If you are using layouts in your app for multiple pages, there is only a slight change required. In Nuxt 2, the `<Nuxt>` component is used within a layout to render the current page. In Nuxt 3, layouts use slots instead, so you will have to replace that component with a `<slot />`. This also allows advanced use cases with named and scoped slots. [Read more about layouts](https://nuxt.com/docs/4.x/directory-structure/app/layouts). You will also need to change how you define the layout used by a page using the `definePageMeta` compiler macro. Layouts will be kebab-cased. So `app/layouts/customLayout.vue` becomes `custom-layout` when referenced in your page. ### Migration 1. Replace `<Nuxt />` with `<slot />` ```diff [app/layouts/custom.vue] <template> <div id="app-layout"> <main> - <Nuxt /> + <slot /> </main> </div> </template> ``` 2. Use [`definePageMeta`](https://nuxt.com/docs/4.x/api/utils/define-page-meta) to select the layout used by your page. ```diff [app/pages/index.vue] + <script setup> + definePageMeta({ + layout: 'custom' + }) - <script> - export default { - layout: 'custom' - } </script> ``` 3. Move `~/layouts/_error.vue` to `~/error.vue`. See [the error handling docs](https://nuxt.com/docs/4.x/getting-started/error-handling). If you want to ensure that this page uses a layout, you can use [`<NuxtLayout>`](https://nuxt.com/docs/4.x/directory-structure/app/layouts) directly within `error.vue`: ```vue [error.vue] <template> <div> <NuxtLayout name="default"> <!-- --> </NuxtLayout> </div> </template> ``` ## Pages Nuxt 3 ships with an optional `vue-router` integration triggered by the existence of a [`app/pages/`](https://nuxt.com/docs/4.x/directory-structure/app/pages) directory in your source directory. If you only have a single page, you may consider instead moving it to `app.vue` for a lighter build. ### Dynamic Routes The format for defining dynamic routes in Nuxt 3 is slightly different from Nuxt 2, so you may need to rename some of the files within `app/pages/`. 1. Where you previously used `_id` to define a dynamic route parameter you now use `[id]`. 2. Where you previously used `_.vue` to define a catch-all route, you now use `[...slug].vue`. ### Nested Routes In Nuxt 2, you will have defined any nested routes (with parent and child components) using `<Nuxt>` and `<NuxtChild>`. In Nuxt 3, these have been replaced with a single `<NuxtPage>` component. ### Page Keys and Keep-alive Props If you were passing a custom page key or keep-alive props to `<Nuxt>`, you will now use `definePageMeta` to set these options. ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/pages#special-metadata"} :: ### Page and Layout Transitions If you have been defining transitions for your page or layout directly in your component options, you will now need to use `definePageMeta` to set the transition. Since Vue 3, [-enter and -leave CSS classes have been renamed](https://v3-migration.vuejs.org/breaking-changes/transition.html). The `style` prop from `<Nuxt>` no longer applies to transition when used on `<slot>`, so move the styles to your `-active` class. ::read-more{to="https://nuxt.com/docs/4.x/getting-started/transitions"} :: ### Migration 1. Rename any pages with dynamic parameters to match the new format. 2. Update `<Nuxt>` and `<NuxtChild>` to be `<NuxtPage>`. 3. If you're using the Composition API, you can also migrate `this.$route` and `this.$router` to use [`useRoute`](https://nuxt.com/docs/4.x/api/composables/use-route) and [`useRouter`](https://nuxt.com/docs/4.x/api/composables/use-router) composables. #### Example: Dynamic Routes ::code-group ``` [Nuxt 2] - URL: /users - Page: /pages/users/index.vue - URL: /users/some-user-name - Page: /pages/users/_user.vue - Usage: params.user - URL: /users/some-user-name/edit - Page: /pages/users/_user/edit.vue - Usage: params.user - URL: /users/anything-else - Page: /pages/users/_.vue - Usage: params.pathMatch ``` ``` [Nuxt 3] - URL: /users - Page: /pages/users/index.vue - URL: /users/some-user-name - Page: /pages/users/[user].vue - Usage: params.user - URL: /users/some-user-name/edit - Page: /pages/users/[user]/edit.vue - Usage: params.user - URL: /users/anything-else - Page: /pages/users/[...slug].vue - Usage: params.slug ``` :: #### Example: Nested Routes and `definePageMeta` ::code-group ```vue [Nuxt 2] <template> <div> <NuxtChild keep-alive :keep-alive-props="{ exclude: ['modal'] }" :nuxt-child-key="$route.slug" /> </div> </template> <script> export default { transition: 'page', // or { name: 'page' } } </script> ``` ```vue [Nuxt 3] <template> <div> <NuxtPage /> </div> </template> <script setup lang="ts"> // This compiler macro works in both <script> and <script setup> definePageMeta({ // you can also pass a string or a computed property key: route => route.slug, transition: { name: 'page', }, keepalive: { exclude: ['modal'], }, }) </script> ``` :: ## `<NuxtLink>` Component Most of the syntax and functionality are the same for the global [NuxtLink](https://nuxt.com/docs/4.x/api/components/nuxt-link) component. If you have been using the shortcut `<NLink>` format, you should update this to use `<NuxtLink>`. `<NuxtLink>` is now a drop-in replacement for *all* links, even external ones. You can read more about it, and how to extend it to provide your own link component. ::read-more{to="https://nuxt.com/docs/4.x/api/components/nuxt-link"} :: ## Programmatic Navigation When migrating from Nuxt 2 to Nuxt 3, you will have to update how you programmatically navigate your users. In Nuxt 2, you had access to the underlying Vue Router with `this.$router`. In Nuxt 3, you can use the `navigateTo()` utility method which allows you to pass a route and parameters to Vue Router. ::warning Make sure to always `await` on [`navigateTo`](https://nuxt.com/docs/4.x/api/utils/navigate-to) or chain its result by returning from functions. :: ::code-group ```vue [Nuxt 2] <script> export default { methods: { navigate () { this.$router.push({ path: '/search', query: { name: 'first name', type: '1', }, }) }, }, } </script> ``` ```vue [Nuxt 3] <script setup lang="ts"> function navigate () { return navigateTo({ path: '/search', query: { name: 'first name', type: '1', }, }) } </script> ``` :: # Component Options > Learn how to migrate from Nuxt 2 components options to Nuxt 3 composables. ## `asyncData` and `fetch` Nuxt 3 provides new options for [fetching data from an API](https://nuxt.com/docs/4.x/getting-started/data-fetching). <!-- TODO: Intro to <script setup> --> <!-- TODO: Mention about options compatibility with asyncData --> ### Isomorphic Fetch In Nuxt 2 you might use `@nuxtjs/axios` or `@nuxt/http` to fetch your data - or just the polyfilled global `fetch`. In Nuxt 3 you can use a globally available `fetch` method that has the same API as [the Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) or [`$fetch`](https://nuxt.com/docs/4.x/api/utils/dollarfetch) method which is using [unjs/ofetch](https://github.com/unjs/ofetch). It has a number of benefits, including: 1. It will handle 'smartly' making [direct API calls](https://nuxt.com/docs/4.x/guide/concepts/server-engine#direct-api-calls) if it's running on the server, or making a client-side call to your API if it's running on the client. (It can also handle calling third-party APIs.) 2. Plus, it comes with convenience features including automatically parsing responses and stringifying data. You can read more [about direct API calls](https://nuxt.com/docs/4.x/guide/concepts/server-engine#direct-api-calls) or [fetching data](https://nuxt.com/docs/4.x/getting-started/data-fetching). ### Composables Nuxt 3 provides new composables for fetching data: [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) and `useFetch`. They each have 'lazy' variants (`useLazyAsyncData` and `useLazyFetch`), which do not block client-side navigation. In Nuxt 2, you'd fetch your data in your component using a syntax similar to: ```ts export default { async asyncData ({ params, $http }) { const post = await $http.$get(`https://api.nuxtjs.dev/posts/${params.id}`) return { post } }, // or alternatively fetch () { this.post = await $http.$get(`https://api.nuxtjs.dev/posts/${params.id}`) }, } ``` Within your methods and templates, you could use the `post` variable similar how you'd use any other piece of data provided by your component. With Nuxt 3, you can perform this data fetching using composables in your `setup()` method or `<script setup>` tag: ```vue <script setup lang="ts"> // Define params wherever, through `defineProps()`, `useRoute()`, etc. const { data: post, refresh } = await useAsyncData('post', () => $fetch(`https://api.nuxtjs.dev/posts/${params.id}`)) // Or instead - useFetch is a convenience wrapper around useAsyncData when you're just performing a simple fetch const { data: post, refresh } = await useFetch(`https://api.nuxtjs.dev/posts/${params.id}`) </script> ``` You can now use `post` inside of your Nuxt 3 template, or call `refresh` to update the data. ::note Despite the names, [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) is not a direct replacement of the `fetch()` hook. Rather, [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) replaces both hooks and is more customizable; it can do more than simply fetching data from an endpoint. [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) is a convenience wrapper around [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) for simply fetching data from an endpoint. :: ### Migration 1. Replace the `asyncData` hook with [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) or [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) in your page/component. 2. Replace the `fetch` hook with [`useAsyncData`](https://nuxt.com/docs/4.x/api/composables/use-async-data) or [`useFetch`](https://nuxt.com/docs/4.x/api/composables/use-fetch) in your component. ## `head` ::read-more{to="https://nuxt.com/docs/4.x/migration/meta"} :: ## `key` You can now define a key within the [`definePageMeta`](https://nuxt.com/docs/4.x/api/utils/define-page-meta) compiler macro. ```diff [app/pages/index.vue] - <script> - export default { - key: 'index' - // or a method - // key: route => route.fullPath - } + <script setup> + definePageMeta({ + key: 'index' + // or a method + // key: route => route.fullPath + }) </script> ``` ## `layout` ::read-more{to="https://nuxt.com/docs/4.x/migration/pages-and-layouts"} :: ## `loading` This feature is not yet supported in Nuxt 3. ## `middleware` ::read-more{to="https://nuxt.com/docs/4.x/migration/plugins-and-middleware"} :: ## `scrollToTop` This feature is not yet supported in Nuxt 3. If you want to overwrite the default scroll behavior of `vue-router`, you can do so in an `~/router.options.ts` (see [docs](https://nuxt.com/docs/4.x/guide/recipes/custom-routing#router-options)) for more info. Similar to `key`, specify it within the [`definePageMeta`](https://nuxt.com/docs/4.x/api/utils/define-page-meta) compiler macro. ```diff [app/pages/index.vue] - <script> - export default { - scrollToTop: false - } + <script setup> + definePageMeta({ + scrollToTop: false + }) </script> ``` ## `transition` ::read-more{to="https://nuxt.com/docs/4.x/getting-started/transitions"} :: ## `validate` The validate hook in Nuxt 3 only accepts a single argument, the `route`. Just as in Nuxt 2, you can return a boolean value. If you return false and another match can't be found, this will mean a 404. You can also directly return an object with `status`/`statusText` to respond immediately with an error (other matches will not be checked). ```diff [app/pages/users/[id\\].vue] - <script> - export default { - async validate({ params }) { - return /^\d+$/.test(params.id) - } - } + <script setup> + definePageMeta({ + validate: async (route) => { + const nuxtApp = useNuxtApp() + return /^\d+$/.test(route.params.id) + } + }) </script> ``` ## `watchQuery` This is not supported in Nuxt 3. Instead, you can directly use a watcher to trigger refetching data. ```vue [app/pages/users/[id\\].vue] <script setup lang="ts"> const route = useRoute() const { data, refresh } = await useFetch('/api/user') watch(() => route.query, () => refresh()) </script> ``` # Runtime Config > Learn how to migrate from Nuxt 2 to Nuxt 3 runtime config. If you wish to reference environment variables within your Nuxt 3 app, you will need to use runtime config. When referencing these variables within your components, you will have to use the [`useRuntimeConfig`](https://nuxt.com/docs/4.x/api/composables/use-runtime-config) composable in your setup method (or Nuxt plugin). In the `server/` portion of your app, you can use [`useRuntimeConfig`](https://nuxt.com/docs/4.x/api/composables/use-runtime-config) without any import. ::read-more{to="https://nuxt.com/docs/4.x/guide/going-further/runtime-config"} :: ## Migration 1. Add any environment variables that you use in your app to the `runtimeConfig` property of the `nuxt.config` file. 2. Migrate `process.env` to [`useRuntimeConfig`](https://nuxt.com/docs/4.x/api/composables/use-runtime-config) throughout the Vue part of your app. ::code-group ```ts [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { // Private config that is only available on the server apiSecret: '123', // Config within public will be also exposed to the client public: { apiBase: '/api', }, }, }) ``` ```vue [app/pages/index.vue] <script setup lang="ts"> const config = useRuntimeConfig() // instead of process.env you will now access config.public.apiBase console.log(config.public.apiBase) </script> ``` ```ts [server/api/hello.ts] export default defineEventhandler((event) => { const config = useRuntimeConfig(event) // In server, you can now access config.apiSecret, in addition to config.public console.log(config.apiSecret) console.log(config.public.apiBase) }) ``` ```ini [.env] # Runtime config values are automatically replaced by matching environment variables at runtime NUXT_API_SECRET=api_secret_token NUXT_PUBLIC_API_BASE=https://nuxtjs.org ``` :: # Build Tooling > Learn how to migrate from Nuxt 2 to Nuxt 3 build tooling. We use the following build tools by default: - [Vite](https://vite.dev) or [webpack](https://webpack.js.org) - [Rollup](https://rollupjs.org) - [PostCSS](https://postcss.org) - [esbuild](https://esbuild.github.io) For this reason, most of your previous `build` configuration in `nuxt.config` will now be ignored, including any custom babel configuration. If you need to configure any of Nuxt's build tools, you can do so in your `nuxt.config`, using the new top-level `vite`, `webpack` and `postcss` keys. In addition, Nuxt ships with TypeScript support. ::read-more{to="https://nuxt.com/docs/4.x/guide/concepts/typescript"} :: ## Steps 1. Remove `@nuxt/typescript-build` and `@nuxt/typescript-runtime` from your dependencies and modules. 2. Remove any unused babel dependencies from your project. 3. Remove any explicit core-js dependencies. 4. Migrate `require` to `import`. <!-- TODO: Enabling webpack builder --> # Server > Learn how to migrate from Nuxt 2 to Nuxt 3 server. In a built Nuxt 3 application, there is no runtime Nuxt dependency. That means your site will be highly performant, and ultra-slim. But it also means you can no longer hook into runtime Nuxt server hooks. ::read-more{to="https://nuxt.com/docs/4.x/guide/concepts/server-engine"} :: ## Steps 1. Remove the `render` key in your `nuxt.config`. 2. Any files in `~~/server/api` and `~~/server/middleware` will be automatically registered; you can remove them from your `serverMiddleware` array. 3. Update any other items in your `serverMiddleware` array to point to files or npm packages directly, rather than using inline functions. ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/server"} :: ::read-more{to="https://nuxt.com/docs/4.x/guide/going-further/hooks#server-hooks-runtime"} :: # Modules > Learn how to migrate from Nuxt 2 to Nuxt 3 modules. ## Module Compatibility Nuxt 3 has a basic backward compatibility layer for Nuxt 2 modules using `@nuxt/kit` auto wrappers. But there are usually steps to follow to make modules compatible with Nuxt 3 and sometimes, using Nuxt Bridge is required for cross-version compatibility. We have prepared a [Dedicated Guide](https://nuxt.com/docs/4.x/guide/modules) for authoring Nuxt 3 ready modules using `@nuxt/kit`. Currently best migration path is to follow it and rewrite your modules. Rest of this guide includes preparation steps if you prefer to avoid a full rewrite yet making modules compatible with Nuxt 3. ::tip{icon="i-lucide-puzzle" to="https://nuxt.com/modules"} Explore Nuxt 3 compatible modules. :: ### Plugin Compatibility Nuxt 3 plugins are **not** fully backward compatible with Nuxt 2. ::read-more{to="https://nuxt.com/docs/4.x/directory-structure/app/plugins"} :: ### Vue Compatibility Plugins or components using the Composition API need exclusive Vue 2 or Vue 3 support. By using [vue-demi](https://github.com/vueuse/vue-demi) they should be compatible with both Nuxt 2 and 3. ## Module Migration When Nuxt 3 users add your module, you will not have access to the module container (`this.*`) so you will need to use utilities from `@nuxt/kit` to access the container functionality. ### Test with `@nuxt/bridge` Migrating to `@nuxt/bridge` is the first and most important step for supporting Nuxt 3. If you have a fixture or example in your module, add `@nuxt/bridge` package to its config (see [example](https://nuxt.com/docs/4.x/bridge/overview#update-nuxtconfig)) ### Migrate from CommonJS to ESM Nuxt 3 natively supports TypeScript and ECMAScript Modules. Please check [Native ES Modules](https://nuxt.com/docs/4.x/guide/concepts/esm) for more info and upgrading. ### Ensure Plugins Default Export If you inject a Nuxt plugin that does not have `export default` (such as global Vue plugins), ensure you add `export default () => { }` to the end of it. ::code-group ```js [Before] // ~/plugins/vuelidate.js import Vue from 'vue' import Vuelidate from 'vuelidate' Vue.use(Vuelidate) ``` ```js [After] // ~/plugins/vuelidate.js import Vue from 'vue' import Vuelidate from 'vuelidate' Vue.use(Vuelidate) export default () => { } ``` :: ### Avoid Runtime Modules With Nuxt 3, Nuxt is now a build-time-only dependency, which means that modules shouldn't attempt to hook into the Nuxt runtime. Your module should work even if it's only added to [`buildModules`](https://nuxt.com/docs/4.x/api/nuxt-config#runtimeconfig) (instead of `modules`). For example: - Avoid updating `process.env` within a Nuxt module and reading by a Nuxt plugin; use [`runtimeConfig`](https://nuxt.com/docs/4.x/api/nuxt-config#runtimeconfig) instead. - (\*) Avoid depending on runtime hooks like `vue-renderer:*` for production - (\*) Avoid adding `serverMiddleware` by importing them inside the module. Instead, add them by referencing a file path so that they are independent of the module's context (\*) Unless it is for `nuxt dev` purpose only and guarded with `if (nuxt.options.dev) { }`. ::tip Continue reading about Nuxt 3 modules in the [Modules Author Guide](https://nuxt.com/docs/4.x/guide/modules). :: ### Use TypeScript (Optional) While it is not essential, most of the Nuxt ecosystem is shifting to use TypeScript, so it is highly recommended to consider migration. ::tip You can start migration by renaming `.js` files, to `.ts`. TypeScript is designed to be progressive! :: ::tip You can use TypeScript syntax for Nuxt 2 and 3 modules and plugins without any extra dependencies. :: # Hello World > A minimal Nuxt application only requires the \`app.vue\` and \`nuxt.config.js\` files. ::code-explorer{path="examples/hello-world" default-value="app.vue"} :: ::browser{src="https://hello-world.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/getting-started/introduction"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/hello-world) # Auto Imports > This example demonstrates the auto-imports feature in Nuxt. ::code-explorer{path="examples/features/auto-imports" default-value="app.vue"} :: ::browser{src="https://auto-imports.example.nuxt.space"} :: Example of the auto-imports feature in Nuxt with: - Vue components in the `components/` directory are auto-imported and can be used directly in your templates. - Vue composables in the `composables/` directory are auto-imported and can be used directly in your templates and JS/TS files. - JS/TS variables and functions in the `utils/` directory are auto-imported and can be used directly in your templates and JS/TS files. ::read-more{to="https://nuxt.com/docs/guide/directory-structure/components"} :: ::read-more{to="https://nuxt.com/docs/guide/directory-structure/composables"} :: ::read-more{to="https://nuxt.com/docs/guide/directory-structure/utils"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/features/auto-imports) # Data Fetching > This example demonstrates data fetching with Nuxt using built-in composables and API routes. ::code-explorer{path="examples/features/data-fetching" default-value="pages/index.vue"} :: ::browser{src="https://data-fetching.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/getting-started/data-fetching"} :: ::read-more{to="https://nuxt.com/docs/guide/directory-structure/server"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/features/data-fetching) # State Management > This example shows how to use the \`useState\` composable to create a reactive and SSR-friendly shared state across components. ::code-explorer{path="examples/features/state-management" default-value="app.vue"} :: ::browser{src="https://state-management.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/getting-started/state-management"} :: ::read-more{to="https://nuxt.com/docs/api/composables/use-state"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/features/state-management) # Meta Tags > This example shows how to use the Nuxt helpers and composables for SEO and meta management. ::code-explorer{path="examples/features/meta-tags" default-value="pages/index.vue"} :: ::browser{src="https://meta-tags.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/getting-started/seo-meta"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/features/meta-tags) # Layouts > This example shows how to define default and custom layouts. ::code-explorer{path="examples/features/layouts" default-value="pages/index.vue"} :: ::browser{src="https://layouts.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/getting-started/views#layouts"} :: ::read-more{to="https://nuxt.com/docs/guide/directory-structure/layouts"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/features/layouts) # Middleware > This example shows how to add route middleware with the middleware/ directory or with a plugin, and how to use them globally or per page. ::code-explorer{path="examples/routing/middleware" default-value="middleware/redirect-me.ts"} :: ::browser{src="https://middleware.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/guide/directory-structure/middleware"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/routing/middleware) # Pages > This example shows how to use the pages/ directory to create application routes. ::code-explorer{path="examples/routing/pages" default-value="pages/index.vue"} :: ::browser{src="https://pages.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/guide/directory-structure/pages"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/routing/pages) # Universal Router > This example demonstrates Nuxt universal routing utilities without depending on \`pages/\` and \`vue-router\`. ::code-explorer{path="examples/routing/universal-router" default-value="plugins/add.ts"} :: ::browser{src="https://universal-router.example.nuxt.space"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/routing/universal-router) # Layers > This example shows how to use the extends key in \`nuxt.config.ts\`. ::code-explorer{path="examples/advanced/config-extends" default-value="nuxt.config.ts"} :: ::browser{src="https://config-extends.example.nuxt.space"} :: This example shows how to use the `extends` key in `nuxt.config.ts` to use the `base/` directory as a base Nuxt application, and use its components, composables or config and override them if necessary. ::read-more{to="https://nuxt.com/docs/getting-started/layers"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/config-extends) # Error Handling > This example shows how to handle errors in different contexts: pages, plugins, components and middleware. ::code-explorer{path="examples/advanced/error-handling" default-value="error.vue"} :: ::browser{src="https://error-handling.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/getting-started/error-handling"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/error-handling) # JSX / TSX > This example shows how to use JSX syntax with typescript in Nuxt pages and components. ::code-explorer{path="examples/advanced/jsx" default-value="app.vue"} :: ::browser{src="https://jsx.example.nuxt.space"} :: ::read-more{icon="i-simple-icons-vuedotjs" to="https://vuejs.org/guide/extras/render-function.html#jsx-tsx" target="_blank"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/jsx) # Locale > This example shows how to define a locale composable to handle the application's locale, both server and client side. ::code-explorer{path="examples/advanced/locale" default-value="composables/locale.ts"} :: ::browser{src="https://locale.example.nuxt.space"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/locale) # Module Extend Pages > This example defines a new \`test\` page using \`extendPages\` within a module. ::code-explorer{path="examples/advanced/module-extend-pages" default-value="modules/pages/index.ts"} :: ::browser{src="https://module-extend-pages.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/guide/modules"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/module-extend-pages) # Teleport > This example shows how to use the \<Teleport> with client-side and server-side rendering. ::code-explorer{path="examples/advanced/teleport" default-value="app.vue"} :: ::browser{src="https://teleport.example.nuxt.space"} :: Vue 3 provides the [`<Teleport>` component](https://vuejs.org/guide/built-ins/teleport.html) which allows content to be rendered elsewhere in the DOM, outside of the Vue application. This example shows how to use the `<Teleport>` with client-side and server-side rendering. ::read-more{to="https://nuxt.com/docs/api/components/teleports"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/teleport) # Testing > This example shows how to test your Nuxt application. ::code-explorer{path="examples/advanced/testing" default-value="tests/basic.test.ts"} :: ::browser{src="https://testing.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/getting-started/testing"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/testing) # useCookie > This example shows how to use the useCookie API to persist small amounts of data that both client and server can use. ::code-explorer{path="examples/advanced/use-cookie" default-value="app.vue"} :: ::browser{src="https://use-cookie.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/api/composables/use-cookie"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/use-cookie) # Use Custom Fetch Composable > This example shows a convenient wrapper for the useFetch composable from nuxt. It allows you to customize the fetch request with default values and user authentication token. ::code-explorer{path="examples/advanced/use-custom-fetch-composable" default-value="plugins/customFetch.ts"} :: ::browser{src="https://use-custom-fetch-composable.example.nuxt.space"} :: ::read-more{to="https://nuxt.com/docs/guide/recipes/custom-usefetch"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/advanced/use-custom-fetch-composable) # WASM > This example demonstrates the server-side support of WebAssembly in Nuxt. ::code-explorer{path="examples/experimental/wasm" default-value="app.vue"} :: ::browser{src="https://wasm.example.nuxt.space"} :: --- - [Source](https://github.com/nuxt/examples/tree/main/examples/experimental/wasm) # Deploy a Nuxt App > Discover the different ways to deploy your Nuxt project on different hosting providers. --- - [Deployment Guide](https://nuxt.com/docs/getting-started/deployment) # AWS Amplify > Deploy your Nuxt Application to AWS Amplify infrastructure. ::tip **Zero Configuration ✨** Integration with AWS Amplify is possible with zero configuration, [learn more](https://nitro.unjs.io/deploy#zero-config-providers). :: ## Setup 1. Login to the [AWS Amplify Hosting Console](https://console.aws.amazon.com/amplify/?trk=01c5a476-5997-4e6a-88b9-fd0a0a5bbe34&sc_channel=el) 2. Click on "Get Started" > Amplify Hosting (Host your web app) 3. Select and authorize access to your Git repository provider and select the main branch 4. Choose a name for your app, make sure build settings are auto-detected and optionally set requirement environment variables under the advanced section 5. Optionally, select Enable SSR logging to enable server-side logging to your Amazon CloudWatch account 6. Confirm configuration and click on "Save and Deploy" ## Learn more ::read-more{to="https://www.youtube.com/watch?v=CAk5_XGkOG4" target="_blank"} Watch an Amplify Hosting tutorial with Nuxt :: ::read-more{to="https://nitro.unjs.io/deploy/providers/aws-amplify" target="_blank"} Head over **Nitro documentation** to learn more about the aws-amplify deployment preset. :: # Azure > Deploy your Nuxt Application to Azure infrastructure. ## Azure Static Web Apps ::tip **Zero Configuration ✨** Integration with Azure Static Web Apps provider is possible with zero configuration, [learn more](https://nitro.unjs.io/deploy#zero-config-providers). :: Azure Static Web Apps are designed to be deployed continuously in a [GitHub Actions workflow](https://docs.microsoft.com/en-us/azure/static-web-apps/github-actions-workflow). By default, Nuxt will detect this deployment environment to enable the `azure` preset. ### Local preview Install [Azure Functions Core Tools](https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local) if you want to test locally. You can invoke a development environment to preview before deploying. ```bash [Terminal] npx nuxi build --preset=azure npx @azure/static-web-apps-cli start .output/public --api-location .output/server ``` ### Configuration Azure Static Web Apps are [configured](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration) using the `staticwebapp.config.json` file. Nuxt automatically generates this configuration file whenever the application is built with the `azure` preset. It adds the following properties based on the following criteria: | Property | Criteria | Default | | ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | | **[platform.apiRuntime](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#platform)** | Set to `node:20` or `node:22` based on the `engines.node` field in your `package.json`. | `node:22` | | **[navigationFallback.rewrite](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#fallback-routes)** | Is always `/api/server` | `/api/server` | | **[routes](https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#routes)** | All prerendered routes are added. Additionally, if you do not have an `index.html` file an empty one is created for you for compatibility purposes and also requests to `/index.html` are redirected to the root directory which is handled by `/api/server`. | `[]` | ### Custom Configuration You can alter the generated configuration using `azure.config` option. For instance, if you wanted to specify a Node runtime for your Azure Functions, edit your `nuxt.config.ts` file to the following: <!-- Supported apiRuntime versions: https://learn.microsoft.com/en-us/azure/static-web-apps/configuration#select-the-api-language-runtime-version --> ```ts [nuxt.config.ts] export default defineNuxtConfig({ // ... nitro: { azure: { config: { // ... platform: { apiRuntime: 'node:22' } } } } }) ``` Custom routes will be added and matched first. In the case of a conflict (determined if an object has the same route property), custom routes will override generated ones. ### Deploy from CI/CD via GitHub Actions When you link your GitHub repository to Azure Static Web Apps, a workflow file is added to the repository. When you are asked to select your framework, select custom and provide the following information: | Input | Value | | ------------------- | ---------------- | | **app_location** | '/' | | **api_location** | '.output/server' | | **output_location** | '.output/public' | If you miss this step, you can always find the build configuration section in your workflow and update the build configuration: ```yaml [.github/workflows/azure-static-web-apps-<RANDOM_NAME>.yml] ###### Repository/Build Configurations ###### app_location: '/' api_location: '.output/server' output_location: '.output/public' ###### End of Repository/Build Configurations ###### ``` ::callout That's it! Now Azure Static Web Apps will automatically deploy your Nitro-powered application on push. :: If you are using `runtimeConfig`, you will likely want to configure the corresponding [environment variables on Azure](https://docs.microsoft.com/en-us/azure/static-web-apps/application-settings). ## More options ::read-more{to="https://nitro.unjs.io/deploy/providers/azure" target="_blank"} Learn about the other Azure deployment presets on Nitro documentation. :: # Cleavr > Deploy your Nuxt Application to Cleavr infrastructure. ::tip **Zero Configuration ✨** Integration with this provider is possible with zero configuration, [learn more](https://nitro.unjs.io/deploy#zero-config-providers). :: ## Setup **In your [Cleavr.io](https://cleavr.io/) panel:** 1. Provision a new server 2. Add a website, selecting **Nuxt 3** as the app type 3. In web app > settings > Code Repo, point to your project's code repository 4. In web app > settings > Environment variables, set `SERVER_PRESET=cleavr` You're now all set to deploy your project! ## Learn more ::read-more{to="https://nitro.unjs.io/deploy/providers/cleavr" target="_blank"} Head over **Nitro documentation** to learn more about the cleavr deployment preset. :: # Clever Cloud > Deploy your Nuxt Application to Clever Cloud infrastructure. Nuxt supports deploying on [Clever Cloud](https://www.clever-cloud.com/) with minimal configuration. ## Deploy Clever Cloud from the Console To deploy your Nuxt project to Clever Cloud, you will need to create a **new application**. The application wizard will walk you through the necessary configuration steps. 1. From the lateral menubar, click **Create > An application** 2. Choose how to deploy: **Create an application from a local repository** or **Create an application from a GitHub repository** 3. Select a **Node.js** application, or a **static one**. 4. Set up the minimal size for your instance and scalability options. Nuxt app must be deployed with a minimum size of **XS** instance for **Node.js** application and **nano** instance for **static one**. The build process, however, will need to be configured later with at least an M instance size to ensure it can handle the resource requirements. Depending on your project’s specifications and dependencies, you may need to adjust further as you monitor the metrics from the **Overview** page. 5. Select a **region** to deploy your instance. 6. Skip connecting **Add-ons** to your Clever application unless you’re using a database. 7. Inject **environment variables**: - For **Node.js** ::code-group{sync="pm"} ```ini [npm] CC_POST_BUILD_HOOK="npm run build" CC_RUN_COMMAND="node .output/server/index.mjs" ``` ```ini [yarn] CC_POST_BUILD_HOOK="yarn build" CC_RUN_COMMAND="node .output/server/index.mjs" ``` ```ini [pnpm] CC_POST_BUILD_HOOK="pnpm build" CC_RUN_COMMAND="node .output/server/index.mjs" ``` ```ini [bun] CC_POST_BUILD_HOOK="bun build" CC_RUN_COMMAND="node .output/server/index.mjs" ``` :: - For a **static application** ::note If [`ssr: false` is set in `nuxt.config.ts`](https://nuxt.com/docs/4.x/getting-started/deployment#static-hosting) **or** if your project contains dynamic routes that cannot be pre-rendered, you should : 1. Use a **Static Apache** application 2. Create a [`.htaccess`](https://www.clever.cloud/developers/doc/applications/static-apache/#serving-indexhtml-for-spa-single-page-application-routers) file that redirects all routes to `index.html` to ensure proper routing for your SPA. Otherwise, you can use the default **Static HTML** application. :: ::code-group{sync="pm"} ```ini [npm] CC_WEBROOT=/.output/public CC_OVERRIDE_BUILDCACHE=/.output/public CC_PRE_BUILD_HOOK=npm install CC_POST_BUILD_HOOK=npm run generate ``` ```ini [yarn] CC_WEBROOT=/.output/public CC_OVERRIDE_BUILDCACHE=/.output/public CC_PRE_BUILD_HOOK=yarn install CC_POST_BUILD_HOOK=yarn generate ``` ```ini [pnpm] CC_WEBROOT=/.output/public CC_OVERRIDE_BUILDCACHE=/.output/public CC_PRE_BUILD_HOOK=pnpm install CC_POST_BUILD_HOOK=pnpm generate ``` ```ini [bun] CC_WEBROOT=/.output/public CC_OVERRIDE_BUILDCACHE=/.output/public CC_PRE_BUILD_HOOK=bun install CC_POST_BUILD_HOOK=bun generate ``` :: 8. Navigate to the application **Information** menu and enable the **enable dedicated build instance** option on a minimal instance of type **M**. 9. **Deploy!** If you’re deploying from **GitHub**, your deployment should start automatically. If you’re using **Git**, show [this docs](https://www.clever-cloud.com/developers/doc/quickstart/#choose-how-to-deploy). ## Learn more ::read-more{to="https://developers.clever-cloud.com/guides/nuxt" target="_blank"} Clever Cloud documentation for deploying Nuxt :: # Cloudflare > Deploy your Nuxt Application to Cloudflare infrastructure. ## Cloudflare Pages ::tip **Zero Configuration ✨** Integration with Cloudflare Pages is possible with zero configuration, [learn more](https://nitro.unjs.io/deploy#zero-config-providers). :: ::important Use [@nuxthub/core](https://nuxt.com/modules/hub) to add database, KV, blob storage, and cache to your Nuxt application. NuxtHub works on Vercel, Cloudflare, Netlify, and more. Learn more on [hub.nuxt.com](https://hub.nuxt.com). :: ### Git Integration If you use the GitHub/GitLab integration with Cloudflare Pages, **no configuration is required**. Pushing to your repository will automatically build your project and deploy it. ::note Nuxt will detect the environment to set the correct [Server/Nitro preset](https://nitro.unjs.io/deploy/providers/cloudflare). :: To leverage server-side rendering on the edge, set the build command to: `nuxt build` To statically generate your website, set the build command to: `nuxt generate` ### Route matching On CloudFlare Pages, if an HTML file is found with a matching path to the current route requested, it will serve it. It will also redirect HTML pages to their extension-less counterparts: for instance, `/contact.html` will be redirected to `/contact`, and `/about/index.html` will be redirected to `/about/`. To match Cloudflare [route matching](https://developers.cloudflare.com/pages/configuration/serving-pages/#route-matching) rules, set the nitro option `autoSubfolderIndex` to `false`. ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { prerender: { autoSubfolderIndex: false } } }) ``` ### Direct Upload Alternatively, you can use [wrangler](https://github.com/cloudflare/workers-sdk) to upload your project to Cloudflare. In this case, you will have to set the preset manually. 1. Build your project for Cloudflare Pages: ```bash [Terminal] npx nuxi build --preset=cloudflare_pages ``` 2. Deploy, it will ask you to create a project for the first time: ```bash [Terminal] npx wrangler pages deploy dist/ ``` ## Learn more ::read-more{to="https://nitro.unjs.io/deploy/providers/cloudflare" target="_blank"} Head over **Nitro documentation** to learn more about the Cloudflare deployment preset. :: ::read-more{to="https://developers.cloudflare.com/pages/framework-guides/deploy-a-nuxt-site/#use-bindings-in-your-nuxt-application" target="_blank"} Head over **CloudFlare Pages** documentation to learn more about it. :: ## Templates ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: Atidone to: https://github.com/atinux/atidone target: _blank ui.icon.base: text-black dark:text-white ``` A todos application with user authentication, SSR and Cloudflare D1. ::: :::card ```yaml [props] icon: i-simple-icons-github title: Atinotes to: https://github.com/atinux/atinotes target: _blank ui.icon.base: text-black dark:text-white ``` An editable website with universal rendering based on Cloudflare KV. ::: :::card ```yaml [props] icon: i-simple-icons-github title: Atidraw to: https://github.com/atinux/atidraw target: _blank ui.icon.base: text-black dark:text-white ``` Web application that lets you to draw and share your drawings with the world, with Cloudflare R2 & AI. ::: :::card ```yaml [props] icon: i-simple-icons-github title: Nuxt Image Gallery to: https://github.com/flosciante/nuxt-image-gallery target: _blank ui.icon.base: text-black dark:text-white ``` An image gallery to upload, edit and share your images to the world, with Cloudflare R2. ::: :: ## Learn more ::read-more{to="https://v2.nitro.build/deploy/providers/cloudflare" target="_blank"} Head over **Nitro documentation** to learn more about the cloudflare deployment preset. :: # Deno Deploy > Deploy your Nuxt Application to Deno Deploy infrastructure. ::important Deno deploy preset is experimental. :: ## Deploy with the CLI You can use [deployctl](https://deno.com/deploy/docs/deployctl) to deploy your app. Login to [Deno Deploy](https://dash.deno.com/account#access-tokens) to obtain a `DENO_DEPLOY_TOKEN` access token, and set it as an environment variable. ```bash # Build with the deno_deploy preset npm run build --preset=deno_deploy # Make sure to run the deployctl command from the output directory cd .output deployctl deploy --project=my-project server/index.ts --token=<DENO_DEPLOY_TOKEN> ``` ## Deploy within CI/CD using GitHub Actions Link your GitHub repository to your Deno Deploy project and choose the "GitHub Actions" deployment mode. You can do this in your project settings on <https://dash.deno.com>. Create a GitHub action file in your repository: <!-- Supported Node versions: https://github.com/actions/setup-node#supported-version-syntax --> ```yaml [.github/workflows/deno_deploy.yml] name: deno-deploy on: push: branches: - main pull_request: branches: - main jobs: deploy: steps: - uses: actions/checkout@v7 with: persist-credentials: false - run: corepack enable - uses: actions/setup-node@v7 with: node-version: 24 cache: pnpm - run: pnpm install - run: pnpm build env: NITRO_PRESET: deno_deploy - name: Deploy to Deno Deploy uses: denoland/deployctl@v1 with: project: <my-project> entrypoint: server/index.ts root: .output ``` ::important Make sure to rename `<my-project>` with your project name. :: ## Templates ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: Nuxt Deno KV to: https://github.com/Atinux/nuxt-deno-kv target: _blank ui.icon.base: text-black dark:text-white ``` A collaborative todo-list app built with Deno KV and Nuxt. ::: :: ## Learn more ::read-more{to="https://nitro.unjs.io/deploy/providers/deno-deploy" target="_blank"} Head over **Nitro documentation** to learn more about the deno-deploy deployment preset. :: # DigitalOcean > Deploy your Nuxt Application to DigitalOcean infrastructure. Nuxt supports deploying on the [DigitalOcean App Platform](https://docs.digitalocean.com/products/app-platform/) with minimal configuration. ## Setup 1. Create a new DigitalOcean app following the [guide](https://docs.digitalocean.com/products/app-platform/how-to/create-apps/). 2. Next, you'll need to configure environment variables. In your app settings, ensure the following [app-level environment variables](https://docs.digitalocean.com/products/app-platform/how-to/use-environment-variables/): ```bash SERVER_PRESET=digital-ocean ``` 3. You will need to ensure you set an `engines.node` field in your app's `package.json` to ensure DigitalOcean uses a supported version of Node.js: <!-- Supported Node versions: https://docs.digitalocean.com/products/app-platform/reference/buildpacks/nodejs/ --> ```json [package.json] { "engines": { "node": "24.x" } } ``` 4. You'll also need to add a run command so DigitalOcean knows what command to run after a build. You can do so by adding a start script to your `package.json`: ```json [package.json] { "scripts": { "start": "node .output/server/index.mjs" } } ``` 5. Finally, you'll need to add this start script to your DigitalOcean app's run command. Go to `Components > Settings > Commands`, click "Edit", then add `npm run start` ::tip Your Nuxt app should be live at a DigitalOcean generated URL and you can now follow [the rest of the DigitalOcean deployment guide](https://docs.digitalocean.com/products/app-platform/how-to/manage-deployments/). :: ## Learn more ::read-more{to="https://nitro.unjs.io/deploy/providers/digitalocean" target="_blank"} Head over **Nitro documentation** to learn more about the digitalocean deployment preset. :: # Docker > Deploy your Nuxt Application with Docker. Nuxt builds to a standalone Node.js server by default, which makes it a natural fit for a container image. Running `nuxt build` produces a self-contained `.output` directory that only needs a Node.js runtime to start, so the final image does not have to ship your source code, your `node_modules` or your package manager. ::note No extra configuration is required. `nuxt build` uses the `node-server` preset unless another one is detected or set. :: ## Dockerfile Use a multi-stage build: install dependencies and build in the first stage, then copy only `.output` into a clean runtime stage. ::code-group{sync="pm"} ```dockerfile [pnpm] FROM node:24-alpine AS build WORKDIR /app RUN corepack enable COPY --link package.json pnpm-lock.yaml pnpm-workspace.yaml ./ RUN pnpm install --frozen-lockfile COPY --link . . RUN pnpm build FROM node:24-alpine AS runtime WORKDIR /app ENV NODE_ENV=production COPY --link --from=build /app/.output ./.output USER node EXPOSE 3000 CMD ["node", ".output/server/index.mjs"] ``` ```dockerfile [yarn] FROM node:24-alpine AS build WORKDIR /app RUN corepack enable COPY --link package.json yarn.lock ./ RUN yarn install --immutable COPY --link . . RUN yarn build FROM node:24-alpine AS runtime WORKDIR /app ENV NODE_ENV=production COPY --link --from=build /app/.output ./.output USER node EXPOSE 3000 CMD ["node", ".output/server/index.mjs"] ``` ```dockerfile [npm] FROM node:24-alpine AS build WORKDIR /app COPY --link package.json package-lock.json ./ RUN npm ci COPY --link . . RUN npm run build FROM node:24-alpine AS runtime WORKDIR /app ENV NODE_ENV=production COPY --link --from=build /app/.output ./.output USER node EXPOSE 3000 CMD ["node", ".output/server/index.mjs"] ``` ```dockerfile [bun] FROM oven/bun:1-alpine AS build WORKDIR /app COPY --link package.json bun.lock ./ RUN bun install --frozen-lockfile COPY --link . . RUN bun run build FROM node:24-alpine AS runtime WORKDIR /app ENV NODE_ENV=production COPY --link --from=build /app/.output ./.output USER node EXPOSE 3000 CMD ["node", ".output/server/index.mjs"] ``` :: ::tip Pick a base image that satisfies the `engines.node` range of the Nuxt version you are using. The build output itself is plain JavaScript, so the runtime stage can use Node.js even when you build with another package manager or runtime. :: ::note The `pnpm` example copies `pnpm-workspace.yaml` because pnpm reads its `allowBuilds` setting from that file. Without it, recent pnpm versions stop the install when a dependency such as `esbuild` has a build script. If your project has no `pnpm-workspace.yaml`, remove it from the `COPY` line. The `yarn` example targets Yarn 2 or later; on Yarn Classic use `--frozen-lockfile` instead. The `bun` example expects the text `bun.lock` introduced in Bun 1.2 — copy `bun.lockb` if your project still uses the binary lockfile. :: ## Ignore files Add a `.dockerignore` file next to your `Dockerfile` so local artifacts stay out of the build context. Copying a host `node_modules` into the image is a common source of native-module errors, and a stale `.nuxt` or `.output` can silently override the freshly built one. ```bash [.dockerignore] .git .gitignore .nuxt .output .data node_modules dist Dockerfile .dockerignore .env .env.* *.log ``` `COPY . .` sends everything this list does not exclude, so extend it with any other file that carries credentials — an `.npmrc` holding a registry token, for example, unless the build needs it. ## Build and run ```bash [Terminal] docker build -t my-nuxt-app . docker run --rm -p 3000:3000 my-nuxt-app ``` Your application is now available on `http://localhost:3000`. ## Environment variables The server reads the following variables at startup: - `NITRO_PORT` or `PORT` (defaults to `3000`) - `NITRO_HOST` or `HOST` (defaults to `0.0.0.0`) Values under [`runtimeConfig`](https://nuxt.com/docs/api/nuxt-config#runtimeconfig) are overridden by matching `NUXT_`-prefixed variables, so a single image can be promoted across environments. Given this configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { apiSecret: '', public: { siteUrl: '' } } }) ``` `NUXT_API_SECRET` and `NUXT_PUBLIC_SITE_URL` replace those two keys at startup: ```bash [Terminal] docker run --rm -p 8080:8080 \ -e PORT=8080 \ -e NUXT_API_SECRET=your-secret \ -e NUXT_PUBLIC_SITE_URL=https://example.com \ my-nuxt-app ``` ::important The server variables above and `runtimeConfig` keys, through their `NUXT_*` equivalents, are read when the container starts, so one image can serve every environment. Values resolved during the build cannot be changed afterwards — `import.meta.env` in client code, for instance. To vary one of those per environment, declare an `ARG` in the build stage, pass it with `--build-arg`, and rebuild the image. The `.dockerignore` above keeps `.env` files out of the build context, so anything they hold has to be supplied at run time. :: ## Docker Compose ```yaml [compose.yml] services: app: build: . ports: - '3000:3000' environment: NUXT_PUBLIC_SITE_URL: https://example.com restart: unless-stopped ``` ```bash [Terminal] docker compose up --build ``` ## Static site If your application is fully pre-rendered, build it with `nuxt generate` and serve `.output/public` from any static web server. This example uses nginx and configures it to serve the generated `404.html` page for unknown URLs: ```dockerfile [Dockerfile] FROM node:24-alpine AS build WORKDIR /app RUN corepack enable COPY --link package.json pnpm-lock.yaml pnpm-workspace.yaml ./ RUN pnpm install --frozen-lockfile COPY --link . . RUN pnpm generate FROM nginx:alpine AS runtime COPY --link --from=build /app/.output/public /usr/share/nginx/html COPY <<'EOF' /etc/nginx/conf.d/default.conf server { listen 80; root /usr/share/nginx/html; location / { try_files $uri $uri/index.html =404; } error_page 404 /404.html; } EOF EXPOSE 80 ``` ::tip To let the client-side router handle URLs that were not pre-rendered, replace `=404` with `/200.html`. Those URLs then return a `200` status instead of `404`. :: ::read-more{to="https://nuxt.com/docs/getting-started/deployment#static-hosting"} Learn more about **static hosting** and fallback pages. :: ## Learn more ::read-more{to="https://nitro.build/deploy/runtimes/node" target="_blank"} Head over **Nitro documentation** to learn more about the Node.js runtime. :: # Firebase > Deploy your Nuxt Application to Firebase infrastructure. ## Firebase App Hosting (recommended) ::note You will need to be on the [**Blaze plan**](https://firebase.google.com/pricing) (Pay as you go) to get started. :: ::read-more{title="Firebase App Hosting" to="https://firebase.google.com/docs/app-hosting"} :: ### Project Setup 1. Go to the Firebase [console](https://console.firebase.google.com/) and set up a new project. 2. Select **Build > App Hosting** from the sidebar. - You may need to upgrade your billing plan at this step. 3. Click **Get Started**. - Choose a region. - Import a GitHub repository (you’ll need to link your GitHub account). - Configure deployment settings (project root directory and branch), and enable automatic rollouts. - Choose a unique ID for your backend. 4. Click Finish & Deploy to create your first rollout. When you deploy with Firebase App Hosting, the App Hosting preset will be run automatically at build time. ## Firebase Functions (deprecated) ::important This deployment method is deprecated and is not recommended. Firebase App Hosting is the recommended way to deploy Nuxt apps on Firebase. :: To use the more recent and recommended generation of Firebase functions, set the `firebase.gen` option to `2`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { firebase: { gen: 2 } } }) ``` ::note If you cannot use configuration for any reason, alternatively you can use `NITRO_FIREBASE_GEN=2` environment variable. :: If you already have a deployed version of your website and want to upgrade to 2nd gen, [see the Migration process on Firebase docs](https://firebase.google.com/docs/functions/2nd-gen-upgrade). Namely, the CLI will ask you to delete your existing functions before deploying the new ones. ::tip{to="https://firebase.google.com/docs/functions/version-comparison" target="_blank"} Comparison between 1st and 2nd generation functions :: ## Project Setup You may instead prefer to set up your project with the Firebase CLI, which will fetch your project ID for you, add required dependencies (see above) and even set up automated deployments via GitHub Actions (for hosting only). [Learn about installing the firebase CLI](https://firebase.google.com/docs/cli#windows-npm). 1. Install the latest version of the Firebase CLI. ```bash [Terminal] npm install -g firebase-tools@latest ``` 2. Initialize your Firebase Project ```bash [Terminal] firebase login firebase init hosting ``` ::note When prompted, you can enter `.output/public` as the public directory. In the next step, **do not** configure your project as a single-page app. :: Once complete, add the following to your `firebase.json` to enable server rendering in Cloud Functions: ```json [firebase.json] { "functions": { "source": ".output/server" }, "hosting": [ { "site": "<your_project_id>", "public": ".output/public", "cleanUrls": true, "rewrites": [{ "source": "**", "function": "server" }] } ] } ``` ## Local Preview You can preview a local version of your site if you need to test things out without deploying. ```bash npm run build -- --preset=firebase firebase emulators:start ``` ## Build and Deploy Deploy to Firebase Hosting by running a Nuxt build and then running the `firebase deploy` command. ```bash npm run build -- --preset=firebase firebase deploy ``` ## Options You can set options for Firebase functions in your `nuxt.config.ts` file: ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { firebase: { gen: 2, httpsOptions: { region: 'europe-west1', maxInstances: 3, }, }, }, }); ``` ### Runtime Node.js Version You can set a custom Node.js version in configuration: <!-- Supported Node runtimes: https://docs.cloud.google.com/functions/docs/runtime-support --> ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { firebase: { nodeVersion: '24' }, }, }); ``` Firebase tools use the `engines.node` version in `package.json` to determine which node version to use for your functions. Nuxt automatically writes to the `.output/server/package.json` with configured Node.js version. You might also need to add a runtime key to your `firebase.json` file: ```json [firebase.json] { "functions": { "source": ".output/server", "runtime": "nodejs24" } } ``` ::read-more{to="https://firebase.google.com/docs/functions/manage-functions?gen=2nd#set_nodejs_version" target="_blank"} You can read more about this in **Firebase Docs**. :: ## Other Cloud Functions Firebase may warn that other Cloud Functions will be deleted when you deploy. That warning depends on `codebase` identifiers, not on how many function sources you list. Without a unique `codebase`, the CLI treats functions outside the current deploy as candidates for deletion. A single functions source can coexist with other functions if you give it a unique `codebase` (for example in a separate repository that deploys to the same Firebase project). Use a different `codebase` in each repository's `firebase.json`. In one repository, configure multiple function sources with distinct `codebase` values: ```json [firebase.json] { "functions": [ { "source": "functions", "codebase": "default" }, { "source": ".output/server", "codebase": "nuxt" } ], "hosting": [ { "site": "<your_project_id>", "public": ".output/public", "cleanUrls": true, "rewrites": [{ "source": "**", "function": "server" }] } ] } ``` ::read-more{to="https://firebase.google.com/docs/functions/organize-functions?gen=2nd#managing_multiple_source_packages_monorepo" target="_blank"} See **Managing multiple source packages** in the Firebase docs. :: To deploy only the Nuxt codebase and hosting: ```bash firebase deploy --only functions:nuxt,hosting ``` With a single functions source (no separate `codebase`), target the `server` function instead: ```bash firebase deploy --only functions:server,hosting ``` ::read-more{to="https://nitro.unjs.io/deploy/providers/firebase" target="_blank"} Head over to the **Nitro documentation** to learn more about the Firebase deployment preset. :: ## Using Cookies in Production When using Firebase Hosting together with Cloud Functions or Cloud Run, cookies are generally stripped from incoming requests to allow for efficient CDN cache behavior. Only the specially-named `__session` cookie is permitted to pass through to your app. ::read-more{to="https://firebase.google.com/docs/hosting/manage-cache#using_cookies" target="\_blank"} For more information, refer to the **Firebase documentation**. :: ## Working with Environment Variables To set environment variables for your Firebase functions, you need to copy the `.env` file to the `.output/server` directory. You can do this by adding a `postbuild` script to your `package.json`, that will automatically run after the build command: ```json [package.json] { "scripts": { "postbuild": "cp .env .output/server/.env" } } ``` ::read-more{to="https://firebase.google.com/docs/functions/config-env?gen=2nd#env-variables" target="\_blank"} For more information, refer to the **Firebase documentation**. :: # Flightcontrol > Deploy your Nuxt Application to Flightcontrol infrastructure. Nitro supports deploying to [AWS via Flightcontrol](https://flightcontrol.dev?ref=nuxt) with minimal configuration. ::tip **Zero Configuration ✨** Integration with Flightcontrol is possible with zero configuration. :: ## Set Up your Flightcontrol account On a high level, the steps you will need to follow to deploy a project for the first time are: 1. Create an account at [Flightcontrol](https://app.flightcontrol.dev/signup?ref=nuxt) 2. Create an account at [AWS](https://portal.aws.amazon.com/billing/signup) (if you don't already have one) 3. Link your AWS account to the Flightcontrol 4. Authorize the Flightcontrol GitHub App to access your chosen repositories, public or private. 5. Create a Flightcontrol project with configuration via the Dashboard or with configuration via `flightcontrol.json`. ## Create a Project with Configuration via the Dashboard 1. Create a Flightcontrol project from the Dashboard. Select a repository for the source. 2. Select the `GUI` config type. 3. Select the Nuxt preset. 4. Select your preferred AWS server size. 5. Submit the new project form. ## Create a Project with Configuration via `flightcontrol.json` 1. Create a Flightcontrol project from your dashboard. Select a repository for the source. 2. Select the `flightcontrol.json` config type. 3. Add a new file at the root of your repository called `flightcontrol.json`. Here is an example configuration that creates an AWS fargate service for your app: ```json [flightcontrol.json] { "$schema": "https://app.flightcontrol.dev/schema.json", "environments": [ { "id": "production", "name": "Production", "region": "us-west-2", "source": { "branch": "main" }, "services": [ { "id": "nitro", "buildType": "nixpacks", "name": "My Nitro site", "type": "fargate", "domain": "www.yourdomain.com", "outputDirectory": ".output", "startCommand": "node .output/server/index.mjs", "cpu": 0.25, "memory": 0.5 } ] } ] } ``` 4. Submit the new project form. ::read-more{to="https://www.flightcontrol.dev/docs?ref=nuxt" target="_blank"} Learn more about Flightcontrol's configuration. :: ::read-more{to="https://nitro.unjs.io/deploy/providers/flightcontrol" target="_blank"} Head over **Nitro documentation** to learn more about the flightcontrol deployment preset. :: # Fly.io > Deploy your Nuxt Application to Fly.io infrastructure. Nuxt supports deploying on [Fly.io](https://fly.io/) with minimal configuration. ## Using the Fly CLI 1. Install [flyctl](https://docs.fly.io/flyctl/install) and sign in. ```bash [Terminal] fly auth login ``` 2. From your project's root directory, create and deploy a new Fly app. `fly launch` detects Nuxt and generates a `Dockerfile` and `fly.toml` for you. ```bash [Terminal] fly launch ``` 3. Deploy subsequent changes. ```bash [Terminal] fly deploy ``` ## Learn more ::read-more{to="https://docs.fly.io/js/frameworks/nuxtjs" target="_blank"} Head over to **[Fly.io](http://Fly.io) documentation** to learn more about running Nuxt on [Fly.io](http://Fly.io). :: # GitHub Pages > Deploy your Nuxt Application to GitHub Pages infrastructure. Nuxt supports deploying to [GitHub Pages](https://pages.github.com/) with minimal configuration. ::caution GitHub Pages only support static sites, Nuxt will pre-render your application to static HTML files. :: ::caution If you are deploying a **project site** without a custom domain, you need to set `NUXT_APP_BASE_URL` to your repository slug for your build step. **Project-site example**: `https://<user>.github.io/<repository>/`: `NUXT_APP_BASE_URL=/<repository>/ npx nuxt build --preset github_pages` :: ::tip **Generic GitHub Action Configuration ✨** For a project site without a custom domain, you can pass the GitHub Actions context variable `github.event.repository.name` as `NUXT_APP_BASE_URL: /${{ github.event.repository.name }}/` - see [Deployment](https://nuxt.com/deploy/github-pages#deployment) below. For a user or organization site named `<owner>.github.io`, or for a custom domain, omit `NUXT_APP_BASE_URL` or set it to `/`. :: ::read-more{to="https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs#github-context" target="_blank"} Head over **GitHub Actions Context** to learn more about the GitHub Actions context variables. :: ## Setup Follow the steps to [create a GitHub Pages site](https://docs.github.com/en/pages/getting-started-with-github-pages/creating-a-github-pages-site). ## Deployment Here is an example GitHub Actions workflow to deploy your site to GitHub Pages using the `github_pages` preset: <!-- Supported Node versions: https://github.com/actions/setup-node#supported-version-syntax --> ```yaml [.github/workflows/deploy.yml] # https://github.com/actions/deploy-pages#usage name: Deploy Nuxt App to GitHub Pages on: workflow_dispatch: push: branches: - main jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 with: persist-credentials: false - run: corepack enable - uses: actions/setup-node@v7 with: node-version: "24" # Pick your own package manager and build script - run: npm install # Drop the env var below if your GitHub Pages is configured for a custom domain - run: npx nuxt build --preset github_pages env: NUXT_APP_BASE_URL: /${{ github.event.repository.name }}/ - name: Upload artifact uses: actions/upload-pages-artifact@v5 with: path: ./.output/public # Deployment job deploy: # Add a dependency to the build job needs: build # Grant GITHUB_TOKEN the permissions required to make a Pages deployment permissions: pages: write # to deploy to Pages id-token: write # to verify the deployment originates from an appropriate source # Deploy to the github_pages environment environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} # Specify runner + deployment step runs-on: ubuntu-latest steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@v5 ``` ::read-more{to="https://nitro.unjs.io/deploy/providers/github-pages" target="_blank"} Head over **Nitro documentation** to learn more about the github-pages deployment preset. :: ## Favicons Favicons typically are expected at hosting domain root. In case of a GitHub Pages deployment this does not work. For this reason favicon URLs should be defined for the page header starting with the GitHub Page's base URL as absolute path to work for a single page app as well as for a multi-route app. ::note Favicon files should be maintained within the project's `public/` folder and your `app/app.vue` file must be configured according to [SEO and Meta](https://nuxt.com/docs/getting-started/seo-meta#nuxt-config) to make use of the base URL defined by `NUXT_APP_BASE_URL`. :: # GitLab Pages > Deploy your Nuxt Application to GitLab Pages. Nuxt supports deploying on the [GitLab Pages](https://docs.gitlab.com/ee/user/project/pages) with minimal configuration. ::caution GitLab Pages only support static sites, Nuxt will pre-render your application to static HTML files. :: ::caution If you are **not** using a custom domain, you need to set `NUXT_APP_BASE_URL` to your repository-slug for your build step. **Example**: `https://<group/user>.gitlab.io/<repository>/`: `NUXT_APP_BASE_URL=/<repository>/ npm run generate` :: ## Deployment 1. Here is an example GitLab Pages workflow to deploy your site to GitLab Pages: ```yaml [.gitlab-ci.yml] # Job name has to be `pages`. See https://docs.gitlab.com/ee/user/project/pages/#how-it-works pages: image: node before_script: - npm ci --cache .npm --prefer-offline script: # Specify the steps involved to build your app here - npm run generate cache: # https://docs.gitlab.com/ee/ci/caching/#cache-nodejs-dependencies key: files: - package-lock.json paths: - .npm/ artifacts: paths: # The directory that contains the built files to be published - .output/public # The directory that contains the built files to be published publish: .output/public rules: # This ensures that only pushes to the default branch # will trigger a pages deploy - if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH ``` ## Learn more ::read-more{to="https://docs.gitlab.com/ee/user/project/pages/getting_started_part_one.html#project-website-examples" target="_blank"} Head over **GitLab Pages default domain names and URLs** to learn more about the GitLab Pages default domain names. :: # Heroku > Deploy your Nuxt Application to Heroku infrastructure. Nuxt supports deploying on [Heroku](https://heroku.com/) with minimal configuration. ## Using the Heroku CLI 1. Create a new Heroku app. ```bash [Terminal] heroku create myapp ``` 2. Configure Heroku to use the nodejs buildpack. ```bash [Terminal] heroku buildpacks:set heroku/nodejs ``` 3. Configure your app. ```bash [Terminal] heroku config:set SERVER_PRESET=heroku ``` 4. Ensure you have `start` and `build` commands in your `package.json` file. ```json [package.json] { "scripts": { "build": "nuxt build", "start": "node .output/server/index.mjs" } } ``` ## Learn more ::read-more{to="https://nitro.unjs.io/deploy/providers/heroku" target="_blank"} Head over **Nitro documentation** to learn more about the Heroku deployment preset. :: # Hostinger > Deploy your Nuxt Application to Hostinger Node.js Hosting. ## Deploy using Git 1. Push your code to a GitHub repository. 2. Log in to [hPanel](https://hpanel.hostinger.com), go to **Websites** and click **Add Website**. 3. Select **Node.js Apps**, then choose **Import Git Repository**. 4. Authorize Hostinger to access your GitHub account and select your repository. 5. Hostinger will automatically detect your Nuxt app and suggest build settings. 6. Click **Deploy** — your application will be built and go live automatically. After deployment, every push to your repository will trigger a new build and redeploy your application automatically. ## Deploy by uploading files 1. Compress your project files into a `.zip` archive. 2. In hPanel, go to **Websites → Add Website → Node.js Apps**, then select **Upload your website files**. 3. Upload the `.zip` file and confirm the build settings. 4. Click **Deploy**. Learn more about [Node.js deployments](https://www.hostinger.com/support/how-to-deploy-a-nodejs-website-in-hostinger/) on Hostinger. # IIS > Deploy your Nuxt Application to IIS infrastructure. ## Using IISnode 1. Install the latest LTS version of [Node.js](https://nodejs.org/en/) on your Windows Server. 2. Install [IISnode](https://github.com/azure/iisnode/releases) 3. Install [IIS `URLRewrite` Module](https://www.iis.net/downloads/microsoft/url-rewrite). 4. In IIS, add `.mjs` as a new mime type and set its content type to `application/javascript`. 5. Build you application with the following command: ```bash [Terminal] npx nuxi build --preset=iis_node ``` 6. Deploy the contents of your `.output` folder to your website in IIS. ## More options ::read-more{to="https://nitro.unjs.io/deploy/providers/iis" target="_blank"} Head over **Nitro documentation** to learn more about the IIS deployment presets. :: # Koyeb > Deploy your Nuxt Application to Koyeb infrastructure. Nuxt supports deploying on the [Koyeb serverless platform](https://www.koyeb.com/docs) with minimal configuration. ## Setup 1. Create a new Koyeb app for Nuxt following the [guide](https://www.koyeb.com/docs/deploy/nuxt). 2. Set the `engines.node` field in your project's `package.json` file to a [Koyeb-supported version of Node.js](https://www.koyeb.com/docs/build-and-deploy/build-from-git/nodejs#runtime): <!-- Supported Node versions: https://www.koyeb.com/docs/build-and-deploy/build-from-git/nodejs --> ```json [package.json] { "engines": { "node": "24.x" } } ``` 3. Ensure that `build` and `start` scripts are defined within the project's `package.json` file to define how to build and run the application: ```json [package.json] { "scripts": { "build": "nuxt build", "start": "node .output/server/index.mjs" } } ``` 4. During deployment, you'll need to configure environment variables. In your service settings, set the following [environment variable](https://www.koyeb.com/docs/build-and-deploy/environment-variables): ```bash SERVER_PRESET=koyeb ``` 5. Click "Deploy" to build and deploy your Nuxt app. ## Learn more ::read-more{to="https://nitro.unjs.io/deploy/providers/koyeb" target="_blank"} Head over **Nitro documentation** to learn more about the Koyeb deployment preset. :: # Netlify > Deploy your Nuxt Application to Netlify infrastructure. ::tip **Zero Configuration ✨** Integration with Netlify is possible with zero configuration, [learn more](https://nitro.unjs.io/deploy#zero-config-providers). :: ## Setup Nuxt will auto-detect that you are in a [Netlify](https://www.netlify.com) build environment and build an optimized version of your server. For new sites, Netlify will detect that you are using Nuxt 3 and set the publish directory to `dist` and build command to `npm run build`. ::note If you are upgrading an existing site from Nuxt 2 you should check these and update them if needed. :: If you want to add custom redirects, you can do so with [`routeRules`](https://nuxt.com/docs/guide/concepts/rendering#hybrid-rendering) or by adding a [`_redirects`](https://docs.netlify.com/routing/redirects/#syntax-for-the-redirects-file) file to your `public` directory. ::tip{color="green" icon="i-lucide-check-circle"} For deployment, just push to your git repository [as you would normally do for Netlify](https://docs.netlify.com/configure-builds/get-started/). :: ## Netlify Edge Functions ::read-more{to="https://www.netlify.com/blog/announcing-serverless-compute-with-edge-functions" target="_blank"} Netlify Edge Functions use Deno and the powerful V8 JavaScript runtime to let you run globally distributed functions for the fastest possible response times. :: Set the following environment variable to run Nuxt on Edge Functions: ```bash SERVER_PRESET=netlify_edge ``` ## On-demand Builders On-demand Builders are serverless functions used to generate web content as needed that’s automatically cached on Netlify’s Edge CDN. They enable you to build pages for your site when a user visits them for the first time and then cache them at the edge for subsequent visits until the next deployment. ::read-more{to="https://docs.netlify.com/configure-builds/on-demand-builders/" target="_blank"} Read More about Netlify on-demand builders :: Set the following environment variable to enable on-demand builders: ```bash SERVER_PRESET=netlify_builder ``` ::read-more{to="https://nitro.unjs.io/deploy/providers/netlify" target="_blank"} Head over **Nitro documentation** to learn more about the netlify deployment preset. :: # Northflank > Deploy your Nuxt Application to Northflank infrastructure. Nuxt supports deploying on [Northflank](https://www.northflank.com) with minimal configuration. ## Setup 1. Link your [Git provider](https://northflank.com/docs/v1/application/getting-started/link-your-git-account) and [create a new project](https://northflank.com/docs/v1/application/getting-started/create-a-project) in Northflank. 2. In your project, create a [Service](https://northflank.com/docs/v1/application/getting-started/build-and-deploy-your-code) and connect it to your Nuxt repository. 3. Ensure your package.json includes a start script that runs the Nuxt production server. ```json [package.json] { "scripts": { "start": "node .output/server/index.mjs" } } ``` 4. Click "Create Service" to build and deploy your Nuxt app. ::read-more{to="https://northflank.com/docs" target="_blank"} For more information, refer to the **Northflank documentation**. :: # Render > Deploy your Nuxt Application to Render infrastructure. Nuxt supports deploying on [Render](https://render.com/) with minimal configuration. ## Set up application 1. [Create a new Web Service](https://dashboard.render.com/select-repo?type=web) and select the repository that contains your code. 2. Ensure the 'Node' environment is selected. 3. Depending on your package manager, set the build command to `yarn && yarn build`, `npm install && npm run build`, or `pnpm i --shamefully-hoist && pnpm build`. 4. Update the start command to `node .output/server/index.mjs` 5. Click 'Advanced' and add the following environment variables <!-- Supported Node versions: https://render.com/docs/node-version --> ```bash SERVER_PRESET=render_com NODE_VERSION=24 ``` 6. Click on `Create Web Service`. ## More options ::read-more{to="https://nitro.unjs.io/deploy/providers/render" target="_blank"} Head over **Nitro documentation** to learn more about the Render deployment presets. :: # SST > Deploy your Nuxt Application to AWS with SST. Nuxt supports deploying on [SST](https://sst.dev/) with minimal configuration. ## Quick start 1. Create a Nuxt project. 2. Init SST in your project. ```bash npx sst@latest init ``` 3. It should detect that your are using Nuxt and ask you to update your `nuxt.config.ts` file. ```ts nitro: { preset: 'aws-lambda' } ``` 4. Once you are ready to deploy, run. ```bash npx sst deploy --stage production ``` You can [read the full Nuxt on SST tutorial here](https://sst.dev/docs/start/aws/nuxt). ## More options You can also deploy Nuxt to a container using SST. Head over to the [SST docs to learn more](https://sst.dev/docs/start/aws/nuxt). # Stormkit > Deploy your Nuxt Application to Stormkit infrastructure. ::tip **Zero Configuration ✨** Integration with [Stormkit](https://www.stormkit.io/) is possible with zero configuration, [learn more](https://nitro.unjs.io/deploy#zero-config-providers). :: ## Setup Follow the steps to [create a new app](https://app.stormkit.io/apps/new) on Stormkit. ## Deployment By default, Stormkit will deploy your apps automatically when you push changes to your main branch. But to trigger a manual deploy (for example, you might do this for the very first deployment), you may click `Deploy now`. ::read-more{to="https://nitro.unjs.io/deploy/providers/stormkit" target="_blank"} Head over **Nitro documentation** to learn more about the stormkit deployment preset. :: # Vercel > Deploy your Nuxt Application to Vercel infrastructure. ::tip **Zero Configuration ✨** Integration with Vercel is possible with zero configuration, [learn more](https://nitro.unjs.io/deploy#zero-config-providers). :: ::important Use [@nuxthub/core](https://nuxt.com/modules/hub) to add database, KV, blob storage, and cache to your Nuxt application. NuxtHub works on Vercel, Cloudflare, Netlify, and more. Learn more on [hub.nuxt.com](https://hub.nuxt.com). :: ## Deploy using Git 1. Push your code to your git repository (GitHub, GitLab, Bitbucket). 2. [Import your project](https://vercel.com/new) into Vercel. 3. Vercel will detect that you are using Nitro and will enable the correct settings for your deployment. 4. Your application is deployed! After your project has been imported and deployed, all subsequent pushes to branches will generate [Preview Deployments](https://vercel.com/docs/deployments/environments#preview-environment-pre-production), and all changes made to the Production Branch (commonly “main”) will result in a [Production Deployment](https://vercel.com/docs/deployments/environments#production-environment). Learn more about Vercel’s [Git Integration](https://vercel.com/docs/git). ## Custom Build Output Configuration You can provide additional [build output configuration](https://vercel.com/docs/build-output-api) using `nitro.vercel.config` key inside `nuxt.config.ts`. It will be merged with built-in auto generated config. ## Templates ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: Nuxt Vercel ISR to: https://github.com/danielroe/nuxt-vercel-isr target: _blank ui.icon.base: text-black dark:text-white ``` Example of a Nuxt application with hybrid rendering deployed on Vercel. ::: :::card ```yaml [props] icon: i-simple-icons-github title: Nuxt on the Edge on Vercel to: https://github.com/pi0/nuxt-on-the-edge target: _blank ui.icon.base: text-black dark:text-white ``` Example of a Nuxt application running on Vercel Edge Functions. ::: :: ## Learn More ::read-more{to="https://nitro.unjs.io/deploy/providers/vercel" target="_blank"} Head over **Nitro documentation** to learn more about On-Demand Incremental Static Regeneration or more advanced options. :: # Zeabur > Deploy your Nuxt Application to Zeabur. Nuxt supports deploying on [Zeabur](https://zeabur.com) with minimal configuration. ## Setup 1. Create a new Zeabur app for Nuxt following the [guide](https://zeabur.com/docs/guides/nodejs/nuxt). 2. During the deployment process, you can configure environment variables in Zeabur dashboard. In your service page, open the variables tab set the following [environment variable](https://zeabur.com/docs/deploy/variables): ```bash SERVER_PRESET=zeabur ``` ## Learn more ::read-more{to="https://nitro.unjs.io/deploy/providers/zeabur" target="_blank"} Head over **Nitro documentation** to learn more about the Zeabur deployment preset. :: # Zerops > Deploy your Nuxt Application to Zerops infrastructure. **Nodejs Preset**: `SERVER_PRESET: zerops` **Static Preset**: `SERVER_PRESET: zerops-static` ::read-more{title="Zerops" to="https://zerops.io"} :: <!-- Supported Node versions: https://docs.zerops.io/zerops-yaml/base-list --> ::tip **Nuxt x Zerops Quickrun ✨** Want to test running Nuxt on Zerops without installing or setting up anything? Using repositories [Zerops x Nuxt - Static](https://github.com/zeropsio/recipe-nuxt-static) or [Zerops x Nuxt - SSR on Node.js](https://github.com/zeropsio/recipe-nuxt-nodejs) you can deploy example Nuxt app with a single click. :: Zerops supports deploying both static and server-side rendered apps with a simple configuration file in your project root. ## Static Projects and services can be added either through a [Project add wizard](https://app.zerops.io/dashboard/project-add) or imported using a YAML structure: ### Creating a Project ```yml [zerops-project-import.yml] project: name: recipe-nuxt services: - hostname: app type: static ``` This will create a project called `recipe-nuxt` with a Zerops Static service called `app`. ### Setting up Zerops YAML To tell Zerops how to build and run your app, add a `zerops.yml` to your root: ```yml [zerops.yml] zerops: - setup: app build: base: nodejs@24 buildCommands: - yarn - yarn nuxi generate deployFiles: - .output/public/~ run: base: static ``` Now you can trigger the [build & deploy pipeline using the Zerops CLI](#building-deploying-your-app) or by connecting the app service with your [GitHub](https://docs.zerops.io/references/github-integration/) / [GitLab](https://docs.zerops.io/references/gitlab-integration) repository from inside the service detail. ## SSR Node.js Projects and services can be added either through a [Project add wizard](https://app.zerops.io/dashboard/project-add) or imported using a YAML structure: ```yml [zerops-project-import.yml] project: name: recipe-nuxt services: - hostname: app type: nodejs@24 ``` This will create a project called `recipe-nuxt` with a Zerops Node.js service called `app`. ### Setting up Zerops YAML To tell Zerops how to build and run your app, add a `zerops.yml` to your root: ```yml [zerops.yml] zerops: - setup: nuxt build: base: nodejs@24 buildCommands: - yarn - yarn build deployFiles: - .output/~ run: base: nodejs@24 ports: - port: 3000 httpSupport: true start: node server/index.mjs ``` Now you can trigger the [build & deploy pipeline using the Zerops CLI](#building-deploying-your-app) or by connecting the app service with your [GitHub](https://docs.zerops.io/references/github-integration/) / [GitLab](https://docs.zerops.io/references/gitlab-integration) repository from inside the service detail. ## Building & Deploying your App - Install the [Zerops CLI](https://github.com/zeropsio/zcli). ```sh npm i -g @zerops/zcli ``` - Open [Settings > Access Token Management](https://app.zerops.io/settings/token-management) in the Zerops app and generate a new access token. - Log in using your access token with the following command: ```sh zcli login <token> ``` - Navigate to the root of your app (where `zerops.yml` is located) and run the following command to trigger the deploy: ```sh zcli push ``` Your code can be deployed automatically on each commit or a new tag by connecting the service with your [GitHub](https://docs.zerops.io/references/gitlab-integration) / [GitLab](https://docs.zerops.io/references/gitlab-integration) repository. This connection can be set up in the service detail. ::read-more{title="Zerops Documentation" to="https://docs.zerops.io/"} :: # The Nuxt Blog > Read the latest news about all Nuxt solutions, from framework announcements to integration tutorials. # Announcing Nuxt 3.0 stable We are thrilled to announce the first stable version of Nuxt 3.0.0 ✨ Nuxt 3 is a modern rewrite of the Nuxt framework based on [Vite](https://vitejs.dev/), [Vue3](https://vuejs.org/), and [Nitro](https://nitro.unjs.io/) with first-class TypeScript support and the result of more than two years of research, community feedback, innovation, and experiment to make a pleasant full-stack Developer Experience for Vue development to everyone. [Read More In the Documentation](https://nuxt.com/docs/getting-started/introduction) ## API Stability Nuxt 3.0.0 comes with a stable, production-ready API and 50+ [supported modules](https://nuxt.com/modules) built using [Nuxt Kit](https://nuxt.com/docs/guide/going-further/modules) by the community and Nuxt team. All composables, filesystem conventions, and configurations are guaranteed to be backward compatible with Nuxt 3.0.0. Due to the nature of the meta-frameworks, some changes happen when we upgrade the underlying dependencies (vite, rollup, and nitropack). Thanks to the new Nuxt Kit and Schema tools, such upgrades will be backward compatible as long as you are using documented features. Kit and Schema also guarantee better future compatibility. This makes it faster for us to iterate and plan the next major versions of Nuxt. ## The browser and Node.js support Nuxt 3 officially supports evergreen browsers only. The "core browser set" is what we (And [web.dev](http://web.dev) team) believe most developers need to support most of the time in the absence of specific constraints. It takes into account [usage numbers](https://caniuse.com/usage-table), developer expectations, and [existing support in](https://make.wordpress.org/core/handbook/best-practices/browser-support/) [the ecosystem](https://angular.io/guide/browser-support). The core browser set targets the **2 most recent major versions** of Chrome, Firefox, and Edge on a monthly basis and Safari on a yearly basis. On the server side, Nuxt 3 supports Node.js 14, 16, 18, and 19 at the moment. We encourage everyone to use the latest LTS releases of Node.js, we push them once **widely adopted by major deployment platforms**. This means we keep supporting Node.js versions as long as they are supported by the Node.js team on a rolling basis in non-major releases of Nuxt. Since 14.x is being end-of-life soon, we highly encourage you to update to the latest 18.x whenever possible. ## We Love Community Nuxt wouldn’t be possible today without an amazing community making amazing modules, feedback, and contributions every day. Check our [Community Documentation](https://nuxt.com/docs/community/getting-help) to be involved! ## To the Future Releasing Nuxt 3 is a big milestone for us and opens a future-proof basis for new ideas and trust for the users to build their enterprise projects with Nuxt 3. Server Component Islands, WebSocket layer, new Deployment presets, improved CLI and DevTools and Testing infra are a few to mention. Keep an eye on the [roadmap page](https://nuxt.com/docs/community/roadmap) and [GitHub discussions](https://github.com/nuxt/nuxt/discussions) for updates. NuxtLabs is working on [new product](https://nuxt.studio) and solutions on top of Nuxt 3 at the time of writing this article. Stay tuned for more exciting news and Happy Nuxting 💚 # Nuxt: A vision for 2023 This past year has been an exciting one. Looking into the new year, there is a lot we have planned as a team and we'd love to share it with you. ✨ This past year has been an exciting one, with the release of Nuxt 3 and Nitro and the launch of the new [nuxt.com](http://nuxt.com/) website. It's been the culmination of years of work, and has not only resulted in a new major version of Nuxt, but a new Nuxt architecture, a full-stack server framework ([Nitro](https://nitro.unjs.io/)), and a new GitHub organisation and ecosystem ([UnJS](https://github.com/unjs/)). Throughout that whole time, [Pooya Parsa](https://github.com/pi0) has led the Nuxt team, putting in countless hours of work and thought into building Nuxt 3. Now, at the start of 2023, he's handing over the helm of the Nuxt open-source work to me ([Daniel Roe](https://github.com/danielroe)). Pooya will continue to be actively contributing to the Nuxt project and of course driving the development of UnJS ecosystem and Nitro project. This is a real honour and I'm hugely pleased to be able to work with the rest of the team and the community to continue to drive Nuxt forward to be the intuitive way to build a web application using Vue. 😊 Looking into the new year, there is a lot we have planned as a team and we'd love to share it with you. ## Unifying Nuxt One important change will be unifying Nuxt into a single repository. As a complete rewrite of Nuxt 2, Nuxt 3 has been developed in a separate repository: `nuxt/framework`. Nuxt 3 even has its own documentation on [nuxt.com](http://nuxt.com/), versus the Nuxt 2 documentation on [v2.nuxt.com](https://nuxt.com/docs/2.x/getting-started/installation). In development, this helped us move faster but meant less attention on issues for Nuxt 2. It's also a bit confusing. So in the coming days, we'll be unifying the Nuxt repos into a single repository, `nuxt/nuxt`. We'll transfer all issues and discussions across, of course, clearly labeling them as to which version of Nuxt they affect. This will also provide us an opportunity to close out issues and RFCs that we've resolved or implemented in Nuxt 3. ## New Website This last year brought us the launch of [nuxt.com](http://nuxt.com/) and the unveiling of Nuxt's [new logo](https://nuxt.com/design-kit). ![Nuxt Website 2023](https://nuxt.com/assets/blog/website/new-website-2023.png){.rounded-lg.border.border-gray-700} We'd like to make this website the central place for everything Nuxt. That includes: - migrating Nuxt 2 documentation so there's a single website to check (with a version switcher) - documentation for community modules (using multi-source to pull them from their own repositories) - revamped [examples](https://nuxt.com/docs/examples/hello-world) that show off more real use cases, including authentication, monorepos and more We have some other exciting plans for the website, but I don't want to reveal too much, other than to say that we'll also (of course!) be open-sourcing the website soon. ## Key Modules The modules ecosystem is an incredibly powerful one, and we are grateful to all the module authors who extend Nuxt with so many features. Today we have more than 60 modules compatible with Nuxt 3. Our goal is to continue to empower module development as well as make sure that the most used modules in Nuxt 2 are updated or have a straightforward migration path. The main priorities at the start of the year are `nuxt/image`, PWA and `nuxt/auth`. We're also developing RFCs for `nuxt/font` and `nuxt/script` in conjunction with the Google Aurora team, which will make it much easier to apply best performance practices to your Nuxt apps. Watch this space! ## DX and Performance We particularly care a lot about making Nuxt a joy to use, and we'd like to keep pushing the boundary of great developer experience, which we believe results in the best experience for users of the apps we write too. In the coming months, there will be a continued focus on developer experience and performance. Expect to see Nuxt DevTools and CLI improvements for scaffolding - and more. On the performance side, Nuxt 3 + Nitro is a game-changer for speed, performance, and customisability, and we’ll be building on top of that to enable some amazing features. 🚀 ## A New Release Cycle It's important to know what's coming, and we're going to be spending some time making sure we communicate clearly about what's happening with Nuxt through regular updates like this one. On top of that, we're planning a consistent release cycle, following [semver](https://semver.org/). We'll aim for major framework releases every year, with an expectation of patch releases every week or so and minor releases every month or so. They should never contain breaking changes except within options clearly marked as `experimental`. One comment: We don't want there to be as big a gap (either in time or in breaking changes) between 3 -> 4 as there was between 2 -> 3, so, when the time comes for Nuxt 4, expect a much more gentle upgrade! In the upcoming 3.1.0 release, you can already find a host of bug fixes as well as: - experimental server-only components and a component island renderer - Nitro 2, Vite 4 and Rollup 3 support ## Migrating to Nuxt 3 On December 31st, 2023, Vue 2 will reach End of Life (EOL), and with it Nuxt 2. Both Vue and Nuxt will continue being available and working for many people, but at the same time, many companies and users will want to transition to Nuxt 3 (and we'd encourage this!). Part of our focus this year therefore will be supporting everyone who wants to migrate their apps to Nuxt 3. We'll also be working to backport key bug fixes and security fixes to Nuxt 2. In addition, there is Nuxt Bridge. It was built as a module to bring features and bug fixes from Nuxt 3 back to Nuxt 2, although it has not yet been released in a stable version. We plan to stabilise and release it in the next month or so, but our main focus over the course of the year will be on helping people transition to Nuxt 3. --- It’s a privilege to be part of this community, and we wish you a very Happy New Year! 💚 Daniel (on behalf of the whole Nuxt team) # Introducing Nuxt DevTools > Unleash the Developer Experience with Nuxt and understand your app better than ever. We announced the preview of [Nuxt DevTools](https://github.com/nuxt/devtools) on [Vue Amsterdam 2023](https://vuejs.amsterdam/), a new tool to help you understand your Nuxt app and improve the developer experience even further. Today we released a new minor version [`v0.3.0`](https://github.com/nuxt/devtools/releases/tag/v0.3.0) with a bunch of updates and improvements. ![devtools-tab-overview](https://nuxt.com/assets/blog/devtools/tab-overview.png) In this post, we will explore the reasons behind the creation of Nuxt DevTools, how it can enhance your development experience, and what you can expect in the future. ## Developer Experience Over the recent years, there has been an increasing focus on Developer Experience (DX). Tools and frameworks have been striving to improve the DX. Along the way, Nuxt introduced many innovative features and conventions to make your day-to-day development easier and more efficient. In Nuxt 3, we switched to [Vite](https://vitejs.dev/) as the default bundler for the instant hot module replacement (HMR) during development, creating a faster feedback loop to your workflow. Additionally, we have introduced [Nitro](https://github.com/unjs/nitro), a new server engine that allows you to deploy your Nuxt app to any hosting service, such as [Vercel](https://vercel.com), [Netlify](https://netlify.com), [Cloudflare](https://cloudflare.com) and [more](https://nitro.unjs.io/deploy) **with zero-config**. Nuxt offers many common practices built-in: - Write TypeScript and ESM out-of-the-box throughout your codebase. - Build single-page applications (SPA), server-side rendering (SSR), static site generation (SSG), or [hybrid them **per routes**](https://nuxt.com/docs/guide/concepts/rendering#route-rules) - using the same codebase isomorphically without any explicit setup. - Use several composables, like [`useState`](https://nuxt.com/docs/api/composables/use-state) and [`useAsyncData`](https://nuxt.com/docs/api/composables/use-async-data) for sharing states accessible across the server and client sides. - Leverage SEO utilities, like [`useHead`](https://nuxt.com/docs/api/composables/use-head) and [`useSeoMeta`](https://nuxt.com/docs/getting-started/seo-meta#useseometa) to make meta-tags management a breaze. Moreover, features such as the [layout system](https://nuxt.com/docs/guide/directory-structure/layouts), [plugins](https://nuxt.com/docs/guide/directory-structure/plugins), route [middleware](https://nuxt.com/docs/guide/directory-structure/middleware), and other tools make app creation easier and codebases more organized. Conventions like [file-based routing](https://nuxt.com/docs/guide/directory-structure/pages) and [file-based server APIs](https://nitro.unjs.io/guide/introduction/routing) making the routing intuitive and effortless. [Components auto-imports](https://nuxt.com/docs/guide/directory-structure/components) make it easy to create shared components that are directly available in any Vue file. Unlike global components, they are code-split. We also introduced [composables auto-import](https://nuxt.com/docs/guide/concepts/auto-imports), where all APIs from Vue are directly available. Nuxt modules can also provide their custom composables to be auto-imported, as well as your [local composables](https://nuxt.com/docs/guide/directory-structure/composables). Recently, we introduced client and server-only components, which can be used by adding `.client` and `.server` to the filename. All these conventions are fully typed and developers can even have type autocomplete when doing route navigation or fetching data from APIs. **These conventions significantly reduce boilerplate code, avoid duplications, and improve productivity.** When it comes to ecosystem, Nuxt has a large community of developers building modules around it, [with hundreds of high-quality modules](https://nuxt.com/modules) available. Modules allow developers to get integrations for features they want without worrying about configuration or best practices. ## The Problem Nuxt is capable of creating a large scale application at ease, however there is one problem: **the lack of transparency**. For every new feature and convention we introduce, we are adding a bit more abstraction to the framework. Abstractions are great things to transfer implementation complexity and make things easier to get more focus when building. On the other hand, they can also add extra burden for users to learn and understand what's going on under the hood. Leading also to implicitness, like where a auto-imported component is from, or how many modules is using a certain component, etc. It can also make things hard to debug. This might be considered as a trade-off of any tools, you have to learn and understand the tool to use it with efficiency. Despite improving the documentation and providing more examples, **we believe of an opportunity to improve the lack of transparency**. ## Introducing Nuxt DevTools {#introducing-nuxt-devtools} [Nuxt DevTools](https://github.com/nuxt/devtools) is a visual tool to help you understand your Nuxt app and improve the developer experience even further. It's created to provide a better transparency of Nuxt and your app, find performance bottlenecks and help you manage your app and configuration. It is shipped as an experimental module and provide the views right inside your app. Once installed, it will add a small icon on the bottom of your app. Clicking it will open the DevTools panel. To try it, please refer to the [installation guide](https://nuxt.com/modules/devtools). ### Overview Shows a quick overview of your app, including the Nuxt version, pages, components, modules, and plugins you are using. **It also check your Nuxt version and allow you to upgrade with a single click.** ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1679922926/nuxt3/nuxt-devtools-upgrade_dnfghq.jpg" controls autoplay} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1679922926/nuxt3/nuxt-devtools-upgrade_dnfghq.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1679922926/nuxt3/nuxt-devtools-upgrade_dnfghq.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1679922926/nuxt3/nuxt-devtools-upgrade_dnfghq.ogg" type="video/ogg"} ::: :: ### Pages The pages tab shows your current routes and provide a quick way to navigate to them. For dynamic routes, it also provide a form to fill with each params interactively. You can also use the textbox to play and test how each route is matched. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1679923373/nuxt3/nuxt-devtools-pages_csjoh0.jpg" controls autoplay} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1679923373/nuxt3/nuxt-devtools-pages_csjoh0.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1679923373/nuxt3/nuxt-devtools-pages_csjoh0.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1679923373/nuxt3/nuxt-devtools-pages_csjoh0.ogg" type="video/ogg"} ::: :: ### Components Components tab show all the components you are using in your app and where they are from. You can also search for them and go to the source code. ![nuxt-devtools-tab-components](https://nuxt.com/assets/blog/devtools/tab-components.png) It also provides a graph view that show the relationship between components. You can filter the components to see the dependencies of a specific component. This could help to identify unintended dependencies and improve the performance and bundle size of pages. ![nuxt-devtools-components-graph](https://nuxt.com/assets/blog/devtools/tab-components-graph-all.png) ![nuxt-devtools-components-graph-filtered](https://nuxt.com/assets/blog/devtools/tab-components-graph-filtered.png) You can also use the "Inspector" feature to inspect the DOM tree and see which component is rendering it. Click to go to your editor of the specific line. Making it much easier to make changes, without the requirement of understanding the project structure thoroughly. ![nuxt-devtools-tab-components-inspector](https://nuxt.com/assets/blog/devtools/tab-components-inspector.png) ### Imports Imports tab shows all the auto-imports registered to Nuxt. You can see which files are importing them, and where they are from. Some entries can also provide short descriptions and documentation links. ![nuxt-devtools-tab-imports](https://nuxt.com/assets/blog/devtools/tab-imports.png) ### Modules Modules tab shows all the modules you have installed and providing the links to their documentation and source code. You can find more modules available in [Nuxt Modules](https://nuxt.com/modules). ![nuxt-devtools-tab-modules](https://nuxt.com/assets/blog/devtools/tab-modules.png) Recently we introduce the experimental upgrade feature, which allows you to upgrade your Nuxt or modules with ease. With the [Terminal tab](#terminals), it shows the output of the upgrade process transparently. ![nuxt-devtools-tab-modules-upgrade](https://nuxt.com/assets/blog/devtools/tab-modules-upgrade.png) ### Assets The assets tab that shows all your static assets and their information. You can copy the paths of the assets, or the code snippets of using them. In the future, with the integrations of [Nuxt Image](https://image.nuxtjs.org/), you can even optimize images with a single click. ![nuxt-devtools-tab-assets](https://nuxt.com/assets/blog/devtools/tab-assets.png) ### Plugins Plugins tab shows all the plugins you are using in your app. As plugins runs before the app is mounted,the time spent in each plugin should be minimal to avoid blocking the app from rendering. The time cost of each plugin provided can be helpful to find performance bottlenecks. ![nuxt-devtools-tab-plugins](https://nuxt.com/assets/blog/devtools/tab-plugins.png) ### Hooks Hooks tab can help you to monitor the time spent in each hook from both client and server side. You can also see how many listeners are registered to each hook, and how many times they have been invoked. This can be helpful to find performance bottlenecks. ![nuxt-devtools-tab-hooks](https://nuxt.com/assets/blog/devtools/tab-hooks.png) ### App Config You can inspect and modify the app config in DevTools, try different configurations and see the effects immediately. ![nuxt-devtools-app-config](https://nuxt.com/assets/blog/devtools/tab-app-config.png) ### Payload & Data This tab shows the state created by [`useState`](https://nuxt.com/docs/api/composables/use-state), [`useAsyncData`](https://nuxt.com/docs/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch). It can be helpful to understand how the data is fetched and how the state is managed, or change them reactively to see it they affects your app. For `useAsyncData` and `useFetch`, you can also manually the trigger the refetch. ![nuxt-devtools-tab-payload](https://nuxt.com/assets/blog/devtools/tab-payload.png) ### Terminals In some integrations, they might require to have subprocesses running to do certain jobs. Before DevTools, you either hide the output of the subprocess entirely and swallow the potential warnings/errors, or pipe to stdout and pollute your terminal with multiple outputs. Now you can now have the outputs in DevTools for each process and clearly isolated. ![nuxt-devtools-tab-terminals](https://nuxt.com/assets/blog/devtools/tab-terminals.png) ### Virtual Files Virtual Files tab shows the virtual files generated by Nuxt and Nitro to support the conventions. This can be helpful for advanced debugging. ![nuxt-devtools-tab-virtual-files](https://nuxt.com/assets/blog/devtools/tab-virtual-files.png) ### Inspect Inspect expose the [`vite-plugin-inspect`](https://github.com/antfu/vite-plugin-inspect) integration, allowing you to inspect transformation steps of Vite. It can be helpful to understand how each plugin is transforming your code and spot potential issues. ![nuxt-devtools-vite-plugin-inspect](https://nuxt.com/assets/blog/devtools/tab-inspect.png) ### VS Code Thanks to [VS Code Server](https://code.visualstudio.com/docs/remote/vscode-server), we are able to integrate a **full-featured** VS Code instance into DevTools. You can install extensions and sync your settings. This allows you to get closer feedback loop where you can change the code and see the result immediately without leaving the browser. ![nuxt-devtools-tab-vscode](https://nuxt.com/assets/blog/devtools/tab-vscode.png) ### Module Contributed View With the ecosystem in mind, Nuxt DevTools to designed to be flexible and extendable. Modules could contribute their own views to DevTools, to provide interactive data and playgrounds for their integrations. The following are a few examples: [VueUse module](https://nuxt.com/modules/vueuse) provides a search page for available composables and see their documentation. ![nuxt-devtools-tab-vueuse](https://nuxt.com/assets/blog/devtools/tab-vueuse.png) [UnoCSS module](https://nuxt.com/modules/unocss) provides an interactive inspector to see how each module contributes to the final CSS. ![nuxt-devtools-tab-unocss](https://nuxt.com/assets/blog/devtools/tab-unocss.png) [Nuxt Icon module](https://nuxt.com/modules/icon) provides a search engine for all icons available. ![nuxt-devtools-tab-nuxt-icon](https://nuxt.com/assets/blog/devtools/tab-icones.png) [Nuxt Vitest module](https://nuxt.com/modules/vitest) provides Vitest UI for tests running with the same pipeline as your Nuxt app. ![nuxt-devtools-tab-vitest](https://nuxt.com/assets/blog/devtools/tab-vitest.png) ### For Module Authors With the release of `v0.3.0`, we improved the ability for module authors to contribute to the DevTools. It includes: - Module contributed views - Access to client app's context and devtools' utils - Custom RPC functions to communicate between server and client - Subprocesses spawning and output steaming - [`@nuxt/devtools-kit`](https://nuxt.com/modules/devtools/utils-kit) - a set of utilities help you integrate your module with DevTools - [`@nuxt/devtools-ui-kit`](https://nuxt.com/modules/devtools/ui-kit) - the UI components used in DevTools, to make your module's view consistent with the rest of DevTools - Starter template to create module with DevTools integration Please check out the [Devtools Module Authors Guide](https://nuxt.com/modules/devtools/module-authors) to learn more. ## What to Expect Next? This is just the beginning of the journey. We are planning to add more features to DevTools, while exploring the ways to present the data in more intuitive and playful ways. The goals of Nuxt DevTools are to: - :icon{name="lucide:blend" .size-5} Improve transparency for conventions - :icon{name="lucide:gauge" .size-5} Inspecting performance & analysis - :icon{name="lucide:swatch-book" .size-5} Interactive & playful - :icon{name="lucide:file-pen-line" .size-5} Personalized documentations - :icon{name="lucide:blocks" .size-5} Manage and scaffold apps with ease - :icon{name="lucide:lightbulb" .size-5} Provide insights and improvements - :icon{name="lucide:user-check" .size-5} Make the development experience more enjoyable --- You can check our [Project Roadmap](https://github.com/nuxt/devtools/discussions/31) and share your [Ideas & Suggestions](https://github.com/nuxt/devtools/discussions/29), helping us to make the DevTools better. You can follow the latest updates by staring the [GitHub repository](https://github.com/nuxt/devtools), and following [Nuxt's official Twitter](https://x.com/nuxt_js). Thank you for reading, and we are looking forward to your feedback and contributions! # Announcing Nuxt 3 Release Candidate > Nuxt 3 beta was announced on October 12, 2021 after 16 months of work, introducing a new foundation based on Vue 3, Vite and Nitro. Six months later, we are happy to announce the first release candidate of Nuxt 3, code named “Mount Hope“ 🚀 We are excited to open source Nuxt 3 after more than a year of intense development. The repository is available on GitHub on [nuxt/nuxt](https://go.nuxt.com/github) under the [MIT](https://go.nuxt.com/license) license. ::tip The documentation is available on <https://nuxt.com>. :: ## A new foundation On top of supporting [Vue 3](https://vuejs.org) or [Vite](https://vitejs.dev), Nuxt 3 contains a new [server engine](https://nuxt.com/docs/guide/concepts/server-engine), unlocking new full-stack capabilities to Nuxt server and beyond. It's the first JavaScript application server that is portable across a variety of modern cloud hosting providers. In production, it builds your Vue application and server into one universal `.output` directory. This output is light: minified and without any other Node.js dependencies (except polyfills). You can deploy this output on any system supporting JavaScript, whether Node.js, Serverless, Workers, Edge-side rendering or purely static. **Bonus:** this server engine can be used on existing Nuxt 2 projects with [Nuxt Bridge](https://nuxt.com/docs/getting-started/bridge) 🚀 Head over the [Nuxt 3 homepage](https://nuxt.com) to learn more about Nuxt Nitro and Nuxt Bridge. ## Important notes Nuxt 3 is currently in beta, so expect things to break (and be fixed quickly). We have [plenty of work left](https://github.com/nuxt/nuxt/issues) but we want to open it publicly to gather feedback and contributions from the community 💚 **Do not use it for production until we reach the first release candidate.** During the beta, almost every commit will [trigger a new npm release](https://github.com/nuxt/nuxt/blob/main/.github/workflows/ci.yml#L111-L119); you may want to look at the [merged pull requests](https://github.com/nuxt/nuxt/pulls?q=is%3Apr+is%3Amerged) until we begin generating automated changelogs in the documentation. We are working every day to improve the documentation, explaining as much as possible all the concepts, features and usage of Nuxt 3. Check out the community section of the Nuxt 3 website for [getting help](https://nuxt.com/docs/community/getting-help), [reporting bugs](https://nuxt.com/docs/community/reporting-bugs) or [contributing to the framework](https://nuxt.com/docs/community/contribution). ## Timeline Here some major milestones we've achieved on the way to Nuxt 3: - **Jul 2, 2020**: Nuxt 3 first commit with full TypeScript rewrite - **Aug 7, 2020**: Webpack 5 support - **Sep 15, 2020**: [`pages/`](https://nuxt.com/docs/guide/directory-structure/pages) support - **Oct 29, 2020**: [Vue 3](https://vuejs.org) support with bundle-renderer - **Nov 2, 2020**: [Nuxt Nitro](https://nuxt.com/guide/concepts/server-engine) initial work - **Jan 22, 2021**: Initial [Vite](https://vitejs.dev) support - **Feb 4, 2021**: Nuxt can deploy on [major serverless platforms](https://nuxt.com/docs/getting-started/deployment) - **Mar 6, 2021**: [UnJS](https://github.com/unjs) organisation created on GitHub - **Mar 28, 2021**: Init Nuxt Kit and Nuxt CLI ([nuxi](https://nuxt.com/docs/api/commands/add)) - **May 20, 2021**: [`app.vue`](https://nuxt.com/docs/guide/directory-structure/app) support (`pages/` becomes optional) - **Jun 30, 2021**: [`layouts/`](https://nuxt.com/docs/guide/directory-structure/layouts) support - **Jul 15, 2021**: Native ESM support - **Aug 10, 2021**: Auto import of composables and components - **Sep 5, 2021**: Init [Nuxt Bridge](https://nuxt.com/docs/bridge/overview) for improving Nuxt 2 experience - **Sep 7, 2021**: Support Vite build for production - **Oct 11, 2021**: Add [`useState`](https://nuxt.com/docs/getting-started/state-management) and [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch) composables So far, we've merged [385 pull requests](https://github.com/nuxt/nuxt/pulls?q=is%3Apr+is%3Amerged), closed [229 issues](https://github.com/nuxt/nuxt/issues?q=is%3Aissue+is%3Aclosed) and made [925+ commits](https://github.com/nuxt/nuxt/commits/main). We are excited to hear your thoughts and we thank you for your patience. Now you can go over the [Nuxt 3 documentation](https://nuxt.com) 😊 Don't forget to follow us on [Twitter](https://x.com/nuxt_js) to get the latest news about Nuxt! # Nuxt 2 End-of-Life (EOL) > Nuxt 2 will reach End of Life (EOL) on June 30th, 2024. We've partnered with HeroDevs on offering Never-Ending Support (NES). Released in 2018, Nuxt 2.0 marked a major milestone, establishing it as a mainstream framework. Over the past six years, many developers adopted Nuxt 2, leading to the creation of numerous impressive projects across the web. However, looking forward, maintaining Nuxt 2 is no longer sustainable. With the recent end-of-life of Vue 2 and the maturity of Nuxt 3 and its ecosystem, it's time for our team to concentrate our efforts on the latest major version and upcoming versions. ::tip{to="https://www.herodevs.com/support/nuxt-nes?utm_source=nuxt&utm_medium=nuxt-eol-article" target="_blank"} Jump over HeroDevs' Nuxt Never-Ending Support (NES) :: ## What happens on June 30th, 2024? After this date, Nuxt 2 will continue to be available on the NPM package manager, but will no longer receive updates, such as security and browser compatibility fixes. In other words, your applications will continue to work, but you may get deprecation warnings from your package manager reminding you that Nuxt 2 is no longer a supported version. ::note Vue 2 reached its end-of-life date on December 31st, 2023. :: ## What’s Next? Nuxt 3 has been the default version of Nuxt since November 16th, 2022. On top of using Vue 3 and the composition API, it is shipped with features and innovations: - Universal & Hybrid Rendering: Benefits of both SPA and SSR, with fine-grained control over route rendering - Support for serverless environments (AWS Lambda, Deno, Cloudflare Workers) with minimal cold-start. - First-Class TypeScript Support: Full typing across all components and configurations. - Vite integration for a faster developer experience - Server & API routes with end-to-end typing powered by Nitro - Auto import of composables & utils - Layers feature for domain driven development When and if you can, consider migrating to Nuxt 3 to take advantage of these powerful features. ::read-more{to="https://nuxt.com/docs/getting-started/upgrade#nuxt-2-vs-nuxt-3"} See a full comparison table between Nuxt 2 and Nuxt 3, as well as detailed upgrade guides to Nuxt 3 and Nuxt Bridge (as a part of upgrading to Nuxt 3). :: ::read-more{to="https://nuxt.com/enterprise/support" icon="i-lucide-life-buoy"} If you need support, including upgrading to Nuxt 3, NuxtLabs provides professional support and consultancy in a wide range of areas. :: ## Still on Nuxt 2? Here Are Your Options. Recognizing the various situations that arise during transitions, we are fully aware that users may need other options until they can migrate, or maybe migration simply isn't a feasible path. Here are some other options to consider: ### Update to the Nuxt 2 latest release We expect to release 2.18.0 at the end of June 2024, which will include a few final fixes. ::note We strongly encourage you to update to 2.18.0 once it's out. This will be the starting point for extended support mentioned below. :: ### Purchase Extended Support for Nuxt 2 If you have to stay on Nuxt 2 post-EOL, we have partnered with HeroDevs to offer Nuxt 2 Never-Ending Support (NES). Nuxt 2 NES provides ongoing security and compatibility patches for Nuxt 2 and all official Nuxt Modules (modules released by the Nuxt team, labeled `@nuxt/...` in the marketplace) even after EOL so that applications with strict compliance requirements remain secure and compliant. It also guarantees that Nuxt 2 applications will continue to operate effectively in modern browsers and maintain compatibility with essential libraries like Vue 2. Moreover, Nuxt 2 NES has continuous security monitoring and an enterprise level SLA with respect to support and fixes. Nuxt 2 NES is the continuation of the flavor of security support you’ve enjoyed during the Nuxt 2 LTS period — but indefinitely. ::tip{to="https://www.herodevs.com/support/nuxt-nes?utm_source=nuxt&utm_medium=nuxt-eol-article" target="_blank"} Read more about HeroDevs' Nuxt Never-Ending Support (NES) :: ### Notify Your Users of Your Nuxt 2 Post-EOL Plan If migrating to Nuxt 3 or using Nuxt 2 NES isn't feasible right now, but you're still on Nuxt 2, it's essential to plan how you'll communicate your security strategy to your customers. This doesn't affect all Nuxt users, but many teams face restrictions against deploying unsupported software due to SLAs, contracts, or other obligations to clients and partners. These requirements might come from customers, regulatory bodies, or internal company policies. Increasingly, regulatory bodies are setting higher standards for software accountability. For those with such responsibilities, informing your customers, managers, CISO, or other stakeholders about your plans to handle support and manage potential CVEs is crucial. While Nuxt 2 has only had few vulnerabilities, CVEs can emerge even in well-maintained EOL projects through direct vulnerabilities or compromised dependencies. Staying updated with CVE notifications from organizations like [OpenCVE](https://www.opencve.io), and [Snyk](https://snyk.io) can help you identify issues as they arise. Additionally, browsers occasionally update in ways that can affect legacy libraries—though rare, it’s a possibility to be aware of. ## Looking Forward It is with a heavy heart that I am saying goodbye to Nuxt 2. After many years of working on it and seeing so many websites made with it, this famous loading bar at the top of each Nuxt 2 website will be something I’ll miss a lot! It is with excitement and energy that I will keep working with the core team on the future of Nuxt to keep innovating like we've been doing over these past 8 years. # Introducing Nuxt 3 Beta > 468 days after the first commit, the Nuxt 3 beta has finally arrived. Discover what's inside and what to expect from it. Yes, it includes Vue 3 and Vite ⚡️ We are excited to open source Nuxt 3 after more than a year of intense development. The repository is available on GitHub on [nuxt/nuxt](https://github.com/nuxt/nuxt) under the [MIT](https://github.com/nuxt/nuxt/blob/main/LICENSE) license. ::tip The documentation is available on <https://nuxt.com>. :: ## A new foundation On top of supporting [Vue 3](https://vuejs.org) or [Vite](https://vitejs.dev), Nuxt 3 contains a new [server engine](https://nuxt.com/docs/guide/concepts/server-engine), unlocking new full-stack capabilities to Nuxt server and beyond. It's the first JavaScript application server that is portable across a variety of modern cloud hosting providers. In production, it builds your Vue application and server into one universal `.output` directory. This output is light: minified and without any other Node.js dependencies (except polyfills). You can deploy this output on any system supporting JavaScript, whether Node.js, Serverless, Workers, Edge-side rendering or purely static. **Bonus:** this server engine can be used on existing Nuxt 2 projects with [Nuxt Bridge](https://nuxt.com/docs/getting-started/bridge) 🚀 Head over the [Nuxt 3 homepage](https://nuxt.com) to learn more about Nuxt Nitro and Nuxt Bridge. ## Important notes Nuxt 3 is currently in beta, so expect things to break (and be fixed quickly). We have [plenty of work left](https://github.com/nuxt/nuxt/issues) but we want to open it publicly to gather feedback and contributions from the community 💚 **Do not use it for production until we reach the first release candidate.** During the beta, almost every commit will [trigger a new npm release](https://github.com/nuxt/nuxt/blob/main/.github/workflows/ci.yml#L111-L119); you may want to look at the [merged pull requests](https://github.com/nuxt/nuxt/pulls?q=is%3Apr+is%3Amerged) until we begin generating automated changelogs in the documentation. We are working every day to improve the documentation, explaining as much as possible all the concepts, features and usage of Nuxt 3. Check out the community section of the Nuxt 3 website for [getting help](https://nuxt.com/docs/community/getting-help), [reporting bugs](https://nuxt.com/docs/community/reporting-bugs) or [contributing to the framework](https://nuxt.com/docs/community/contribution). ## Timeline Here some major milestones we've achieved on the way to Nuxt 3: - **Jul 2, 2020**: Nuxt 3 first commit with full TypeScript rewrite - **Aug 7, 2020**: Webpack 5 support - **Sep 15, 2020**: [`pages/`](https://nuxt.com/docs/guide/directory-structure/pages) support - **Oct 29, 2020**: [Vue 3](https://vuejs.org) support with bundle-renderer - **Nov 2, 2020**: [Nuxt Nitro](https://nuxt.com/guide/concepts/server-engine) initial work - **Jan 22, 2021**: Initial [Vite](https://vitejs.dev) support - **Feb 4, 2021**: Nuxt can deploy on [major serverless platforms](https://nuxt.com/docs/getting-started/deployment) - **Mar 6, 2021**: [UnJS](https://github.com/unjs) organisation created on GitHub - **Mar 28, 2021**: Init Nuxt Kit and Nuxt CLI ([nuxi](https://nuxt.com/docs/api/commands/add)) - **May 20, 2021**: [`app.vue`](https://nuxt.com/docs/guide/directory-structure/app) support (`pages/` becomes optional) - **Jun 30, 2021**: [`layouts/`](https://nuxt.com/docs/guide/directory-structure/layouts) support - **Jul 15, 2021**: Native ESM support - **Aug 10, 2021**: Auto import of composables and components - **Sep 5, 2021**: Init [Nuxt Bridge](https://nuxt.com/docs/bridge/overview) for improving Nuxt 2 experience - **Sep 7, 2021**: Support Vite build for production - **Oct 11, 2021**: Add [`useState`](https://nuxt.com/docs/getting-started/state-management) and [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch) composables So far, we've merged [385 pull requests](https://github.com/nuxt/nuxt/pulls?q=is%3Apr+is%3Amerged), closed [229 issues](https://github.com/nuxt/nuxt/issues?q=is%3Aissue+is%3Aclosed) and made [925+ commits](https://github.com/nuxt/nuxt/commits/main). We are excited to hear your thoughts and we thank you for your patience. Now you can go over the [Nuxt 3 documentation](https://nuxt.com) 😊 Don't forget to follow us on [Twitter](https://x.com/nuxt_js) to get the latest news about Nuxt! # Going Full Static > Long awaited features for JAMstack fans has been shipped in v2.13: full static export, improved smart prefetching, integrated crawler, faster re-deploy, built-in web server and new target option for config ⚡️ ## Too long to read 1. Upgrade nuxt to `2.14.0` 2. Set `target: 'static'` in your `nuxt.config.js` 3. Run `nuxt generate` 4. That's it ✨ *Bonus: you can run `nuxt start` to run a local server serving your generated static application.* ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1588095794/nuxt-full-static_rnnbvm.jpg" controls autoplay} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1588095794/nuxt-full-static_rnnbvm.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1588095794/nuxt-full-static_rnnbvm.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1588095794/nuxt-full-static_rnnbvm.ogg" type="video/ogg"} ::: :: Note: in this video we are using `nuxt export` which has been deprecated in favor of `nuxt generate`. ## History Nuxt had the static generation feature with `nuxt generate` since [v0.3.2](https://github.com/nuxt/nuxt.js/releases/tag/v0.3.2) (November 2016), since then we have improved it in multiple ways but never achieved full static generation. Today I am excited to announce that full static export is now possible with Nuxt 2.13. ## Current issues `nuxt generate` is mostly pre-rendering, when you navigate client-side, `asyncData` and `fetch` are called, *making a request to your API*. A lot of users asked to support a "full static" mode, meaning to not call these 2 hooks on navigation, since the next page has been already pre-rendered. Also, the developer experience is not optimal: - You have access to `req` or `res` on SSR but not when running `nuxt generate` - `process.static` is `true` only when running `nuxt generate`, making it slow to develop Nuxt modules or plugins for static generation - You have to specify all your [dynamic routes](https://nuxt.com/docs/2.x/getting-started/file-system-routing#dynamic-routes) in `generate.routes`, making it harder since you don't have access to nuxt modules there. - You cannot test the [SPA fallback](https://nuxt.com/docs/2.x/guide/concepts/static-site-generation#spa-fallback) in development, the fallback is a client-only version of your Nuxt application that loads when hitting a 404 page - `nuxt generate` runs `nuxt build` by default, making it slower to generate your website if only your content changed Note that it was possible to have full static support with [nuxt-payload-extractor](https://github.com/DreaMinder/nuxt-payload-extractor) module but it was more verbose to use and had limitations. ## New config option: `target` To improve the user experience as well as telling Nuxt that you want to export your application to static hosting, we are introducing a `target` option in your `nuxt.config.js`: ```vue <script setup> import { ref, computed } from '#imports' const count = ref(1) const double = computed(() => count.value * 2) </script> ``` ::important Full static doesn't work with `ssr: 'false'` (which is the same as the deprecated `mode: 'spa'`) as this is used for client-side rendering only (Single Page Applications). :: Running `nuxt dev` with the static target will improve the developer experience: - Remove `req` & `res` from context - Fallback to client-side rendering on 404, errors and redirects (see [SPA fallback](https://nuxt.com/docs/2.x/guide/concepts/static-site-generation#spa-fallback)) - `$route.query` will always be equal to `{}` on server-side rendering - `process.static` is `true` We are also exposing `process.target` for modules author to add logic depending on the user target. ## Smarter `nuxt generate` Now with `v2.14.0`, you can use `nuxt generate`, it will smartly know if it has to build or not. ### Crazy fast static applications `nuxt generate` with `target: 'static'` will pre-render all your pages to HTML and save a payload file in order to mock `asyncData` and `fetch` on client-side navigation, this means **no** **more HTTP calls to your API on client-side navigation.** By extracting the page payload to a js file, **it also reduces the HTML size** served as well as preloading it (from the in the header) for optimal performance. We also improved the [smart prefetching](https://nuxt.com/blog/introducing-smart-prefetching) when doing full static, it will also fetch the payloads, making navigation instant 👀 ### Crawler integrated On top of that, it also has a crawler inside, detecting every relative link and generating it: If you want to exclude a bunch of routes, use the [generate.exclude](https://nuxt.com/docs/2.x/api/configuration/configuration-generate#exclude). You can keep using [generate.routes](https://nuxt.com/docs/2.x/api/configuration/configuration-generate#routes) to add extra routes that the crawler could not detect. To disable the crawler, set `generate.crawler: false` in your `nuxt.config.js` ### Faster re-deploy By separating `nuxt build` and `nuxt export`, we are opening a new range of improvements: pre-render your pages only if you content has changed, this means: no webpack build → faster re-deployments. ## Smarter `nuxt start` Once you statically generated your Nuxt app into `dist/`, use `nuxt start` to start a production HTTP server and serve your static app, supporting [SPA Fallback](https://nuxt.com/docs/2.x/guide/concepts/static-site-generation#spa-fallback). This command is perfect to locally test your static application before pushing to your favorite static hosting provider. ## Preview mode We do support live preview out of the box to keep calling your API: ```js {}[plugins/preview.client.js] export default async function ({ query, enablePreview }) { if (query.preview) { enablePreview() } } ``` It will automatically refresh the page data (calling `nuxtServerInit`, `asyncData` and `fetch`). When the preview mode is activated, `asyncData` and `fetch` original methods will be called. ## Commands Depending of the `target`, you can run these commands. - `server` - `nuxt dev`: Start the development server - `nuxt build`: Bundle your Nuxt application for production - `nuxt start`: Start the production server - `static` - `nuxt dev`: Start the development server (static aware) - `nuxt generate`: Bundle your Nuxt application for production if needed (static aware) and export your application to static HTML in `dist/` directory - `nuxt start`: Serve your production application from `dist/` ### What to do next ::read-more{to="https://github.com/nuxt/website-v2/blob/main/content/en/tutorials/4.moving-from-nuxtjs-dotenv-to-runtime-config.md" target="_blank"} Learn more about how to move from `@nuxtjs/dotenv` to runtime config. :: # Introducing Smart Prefetching > Starting from Nuxt v2.4.0, Nuxt will automagically prefetch the code-split pages linked with a nuxt-link when visible in the viewport by default. ## Introducing Smart prefetching ⚡️ Starting from [Nuxt v2.4.0](https://github.com/nuxt/nuxt.js/releases/tag/v2.4.0), Nuxt will automagically prefetch the code-split pages linked with `<nuxt-link>` when visible in the viewport **by default**. This hugely improves the end-user performance, inspired by [quicklink](https://github.com/GoogleChromeLabs/quicklink). ![nuxt-prefetch-comparison](https://nuxt.com/assets/blog/nuxt-prefetch-comparison.gif){.rounded-lg.border.border-gray-700} Demos are online and we recommend you to try it out to feel the difference: - No prefetching (v2.3): <https://nuxt-no-prefetch.surge.sh> - With prefetching (v2.4): <https://nuxt-prefetch.surge.sh> You can learn more about this feature in the [`<nuxt-link>`](https://nuxt.com/docs/2.x/getting-started/nuxt-components#the-nuxtlink-component) section of the documentation. # Understanding how fetch works in Nuxt 2.12 > Explore different features of the fetch hook and learn a brand new way to bring data into Nuxt applications. Nuxt introduces a new `fetch` with the latest release of version 2.12. Fetch provides a brand new way to bring data into Nuxt applications. In this post, we will explore different features of the fetch hook and try to understand how it works. ## Fetch Hook and Nuxt Lifecycle In terms of Nuxt lifecycle hooks, `fetch` sits within Vue lifecycle after `created` hook. As we already know that, all Vue lifecycle hooks are called with their `this` context. The same applies to `fetch` hook as well. ![New fetch in Nuxt lifecycle](https://nuxt.com/assets/blog/new-fetch-lifecycle-hooks.png) Fetch hook is called after the component instance is created on the server-side. That makes `this` context available inside the `fetch`. ```js export default { fetch() { console.log(this) } } ``` Let’s see what this could mean for page components. ### Page Components With the help of `this` context, fetch is able to mutate component’s data directly. It means we can set the component’s local data without having to dispatch Vuex store action or committing mutation from the page component. As a result, Vuex becomes optional, but not impossible. We can still use `this.$store` as usual to access Vuex store if required. ## Availability of fetch hook With `fetch`, we can prefetch the data asynchronously **in any Vue components**. It means, other than page components found in `/pages` directory, every other `.vue` components found in `/layouts` and `/components` directories can also benefit from the fetch hook. Let's see what this could mean for layout and building-block components. ### Layout Components Using new `fetch`, now we can make API calls directly from the layout components. This was impossible prior to the release of v2.12. **Possible use cases** - Fetch config data from the back-end in Nuxt layouts to generate footer and navbar dynamically - Fetch user related data (i.e. user profile, shopping-cart item count) in the navbar - Fetch site relevant data on `layouts/error.vue` ### Building-block (Child/Nested) Components With `fetch` hook available in child components as well, we can off-load some of the data-fetching tasks from page-level components, and delegate them over to nested components. This was also impossible prior to the release of v2.12. This reduces the responsibility of route-level components to a great extent. **Possible use case -** We can still pass props to child components, but if the child components need to have their own data-fetching logic, now they can! ## Call order of multiple fetch hooks Since each component can have its own data-fetching logic, you may ask what would be the order in which each of them are called? Fetch hook is called on server-side once (on the first request to the Nuxt app) and then on client-side when navigating to further routes. But since we can define one fetch hook for each component, fetch hooks are called in sequence of their hierarchy. ### Disabling fetch on server-side In addition, we can even disable fetch on the server-side if required. ```js export default { fetchOnServer: false } ``` And this way, the fetch hook will only be called on client-side. When `fetchOnServer` is set to false, `$fetchState.pending` becomes `true` when the component is rendered on server-side. ## Error Handling New `fetch` handles error at component level. Let’s see how. Because we’re fetching data asynchronously, the new fetch() provides a `$fetchState` object to check whether the request has finished and progressed successfully. Below is what the `$fetchState` object looks like. ``` $fetchState = { pending: true | false, error: null | {}, timestamp: Integer }; ``` We have three keys, 1. **Pending -** lets you display a placeholder when fetch is being called on client-side 2. **Error -** lets you show an error message 3. **Timestamp -** shows timestamp of the last fetch which is useful for caching with `keep-alive` These keys are then used directly in the template area of the component to show relevant placeholders during the process of fetching data from the API. ```html <template> <div> <p v-if="$fetchState.pending">Fetching posts...</p> <p v-else-if="$fetchState.error">Error while fetching posts</p> <ul v-else> … </ul> </div> </template> ``` When error occurs at **component-level**, we can set HTTP status code on server-side by checking `process.server` in fetch hook and follow it up with `throw new Error()` statement. ```js async fetch() { const post = await fetch(`https://jsonplaceholder.typicode.com/posts/${this.$route.params.id}`) .then((res) => res.json()) if (post.id === this.$route.params.id) { this.post = post } else { // set status code on server and if (process.server) { this.$nuxt.context.res.statusCode = 404 } // use throw new Error() throw new Error('Post not found') } } ``` Setting the HTTP status code this way **is useful for correct SEO**. ## Fetch as a method New fetch hook also acts as a method that can be invoked upon user interaction or invoked programmatically from the component methods. ```html <!-- from template in template --> <button @click="$fetch">Refresh Data</button> ``` ```js // from component methods in script section export default { methods: { refresh() { this.$fetch() } } } ``` ## Making Nuxt pages more performant We can use `:keep-alive-props` prop and `activated` hook to make Nuxt page components more performant using a new fetch hook. Nuxt allows **caching a certain number of pages** in the memory along with their fetched data. And also allows **adding a number of seconds** before we can re-fetch the data. For any of the above methods to work, we have to use the `keep-alive` prop in generic `<nuxt />` and `<nuxt-child`> components. ```html {}[layouts/default.vue] <template> <div> <nuxt keep-alive /> </div> </template> ``` In addition, we can pass `:keep-alive-props` to `<nuxt />` component to cache a number of pages along with their fetched data. `:keep-alive-props` prop allow us to indicate the maximum number of pages that should be kept in the memory while we navigate elsewhere within the site. ```html {}[layouts/default.vue] <nuxt keep-alive :keep-alive-props="{ max: 10 }" /> ``` Above is one way to boost page performance which is more high-level and generic, while the next one drills down to optimize the fetch request calls by using the `timestamp` property of `$fetchState` and comparing it against the number of seconds delay before it re-fetches the data. Vue’s `activated` hook is used here with Nuxt's `keep-alive` prop to re-fetch the data. ```js export default { activated() { // Call fetch again if last fetch more than a minute ago if (this.$fetchState.timestamp <= Date.now() - 60000) { this.$fetch() } } } ``` ## asyncData vs Fetch As far as page components are concerned, new `fetch` seems way too similar to `asyncData()` because they both deal with the local data. But there are some key differences worth taking note of as below. As of Nuxt 2.12, `asyncData` method is still an active feature. Let’s examine some of the key differences between `asyncData` and new `fetch`. ### asyncData 1. `asyncData` is limited to only page-level components 2. `this` context is unavailable 3. Adds payload by **returning** the data ```js export default { async asyncData(context) { const data = await context.$axios.$get( `https://jsonplaceholder.typicode.com/todos` ) // `todos` does not have to be declared in data() return { todos: data.Item } // `todos` is merged with local data } } ``` ### New Fetch 1. `fetch` is available in all Vue components 2. `this` context is available 3. Simply **mutates** the local data ```js export default { data() { return { todos: [] } }, async fetch() { const { data } = await axios.get( `https://jsonplaceholder.typicode.com/todos` ) // `todos` has to be declared in data() this.todos = data } } ``` ## Fetch before Nuxt 2.12 If you have been working with Nuxt for a while, then you’ll know that the previous version of `fetch` was significantly different. > **Is this a breaking change?** > No, it isn't. Actually, the old fetch can still be used by passing the `context` as the first argument to avoid any breaking changes in your existing Nuxt applications. Here’s the list of notable changes in `fetch` hook compared with **before** and **after** v2.12. ### 1. Call order of `fetch` hook **Before -** `fetch` hook was called before initiating the component, hence `this` wasn’t available inside the fetch hook. **After -** `fetch` is called after the component instance is created on the server-side when the route is accessed. ### 2. `this` vs `context` **Before -** We had access to the Nuxt `context` on page-level components, given that the `context` is passed as a first parameter. ```js export default { fetch(context) { // … } } ``` **After -** We can access `this` context just like Vue client-side hooks without passing any parameters. ```js export default { fetch() { console.log(this) } } ``` ### 3. Availability of `fetch` hook **Before -** Only page (route-level) components were allowed to fetch data on the server-side. **After -** Now, we can prefetch the data asynchronously in any Vue components. ### 4. Call order of `fetch` hook **Before -** `fetch` could be called server-side once (on the first request to the Nuxt app) and client-side when navigating to further routes. **After -** New `fetch` is the same as an old fetch, but… …since we can have one `fetch` for each component, `fetch` hooks are called in sequence of their hierarchy. ### 5. Error Handling **Before -** We used the `context.error` function that showed a custom error page when an error occurred during API calls. **After -** New `fetch` uses the `$fetchState` object to handle errors in the template area during API calls. Error handling is performed at component level. > **Does this mean we cannot show users a custom error page like we did prior to Nuxt 2.12?** Yes we can, but only with `asyncData()` when it's about page-level component data. When using `fetch`, we can utilize `this.$nuxt.error({ statusCode: 404, message: 'Data not found' })` to show a custom error page. ## Conclusion New fetch hook brings a lot of improvements and provides more flexibility in fetching data and organizing route-level & building-block components in a whole new way! It will certainly make you think a little differently when you plan and design your new Nuxt project that requires multiple API calls within the same route. I hope this article has helped you get acquainted with the new `fetch` feature. I'd love to see what you build with it. # Nuxt 2 Static Improvements > With Nuxt version 2.13, the full-static mode has been introduced. In addition, a new command nuxt export was added to pre-render your pages without triggering a webpack build with the goal to separate the rendering and build process. The only issue was that most Nuxt users weren't able to unleash the full potential of the separation... until now. ## Introduction With Nuxt version 2.13, the [full-static mode](https://nuxt.com/blog/going-full-static) has been introduced. In addition, a new command `nuxt export` was added to pre-render your pages without triggering a webpack build with the goal to separate the rendering and build process. The only issue was that most Nuxt users weren't able to unleash the full potential of the separation... **until now.** ## Faster Static Deployments With v2.14, `nuxt generate` will **automagically skip webpack build step when no code has been changed** and use the previous build using cache. This will help to drastically improve static deployments time by avoiding unnecessary builds which is usually the most time-consuming part of generation process. Cache support is **platform-agnostic** and works on Netlify, Vercel, or any other CI/CD setup that is caching `node_modules`. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1595852304/nuxt-smart-generate_pjaat1.jpg" controls autoplay} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1595852304/nuxt-smart-generate_pjaat1.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1595852304/nuxt-smart-generate_pjaat1.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1595852304/nuxt-smart-generate_pjaat1.ogg" type="video/ogg"} ::: :: ## Generate time: cache vs full webpack build See the comparison in seconds between two `nuxt generate`: - `Build` is when a webpack build is required - `Cache` is only when the content has changed (webpack build skipped) ![Comparison between build VS cache time](https://nuxt.com/assets/blog/bar-chart-cache-build.png) ::tip The static site generation of our projects on content changes are now **\~3.6x times** faster 🚀 :: Project links: [Basic](https://github.com/pi0/nuxt-static-demo), [Strapi Module Docs](https://github.com/nuxt-community/strapi-module/tree/master/docs), [Content Module Docs](https://github.com/nuxt/content/tree/master/docs) and [Nuxt 2 Docs](https://github.com/nuxt/website-v2). ## Using in your projects 1. Update `nuxt` to the latest minor version, which is v2.14. ::code-group ```bash [npm] npm update ``` ```bash [yarn] yarn upgrade nuxt ``` :: 2. Ensure `target` is `static` inside your `nuxt.config.js` ```js {}[nuxt.config.js] export default { target: 'static' // ... } ``` `nuxt generate` will behave as before to avoid breaking changes and provide legacy compatibility if you keep `target: ‘server’` or don't specify target. 3. That’s it 🙌 Now, the `nuxt generate` command will build the project only if necessary, which is the case when files inside the project have been changed. It will always re-render your routes to static HTML files, like `nuxt export` is doing already. Now you only have to change your build command back from `nuxt build && nuxt export` to `nuxt generate` on the platform you are using. If you are using a CI, ensure that you are properly caching `node_modules`. ### Excluding Files from Cache By default, nuxt ignores these directories so if any change happens inside them, build will not be triggered: - Build directory (`.nuxt/`) - Static directory (`static/`) - Generate dist (`dist/`) - `node_modules` - `README.md` - Hidden dotfiles (like `.npmrc`) You can add more patterns using [generate.cache.ignore](https://nuxt.com/docs/2.x/api/configuration/configuration-generate#cache) option in `nuxt.config`: ```js {}[nuxt.config.js] export default { generate: { cache: { ignore: [ // When something changed in the docs folder, do not re-build via webpack 'docs' ] } } } ``` It is also possible to use a function for `ignore` option to override default ignore entries. ### Module Authors What if you are developing a nuxt module that is working with files that should not trigger a rebuild? The best example is for [@nuxt/content](https://content.nuxt.com) module that reads markdown files from the repository. In this case, these files are used within a runtime module, which is the case when using `@nuxt/content`, the module itself can tell nuxt to ignore these files for you already so you don't have to do anything! Module authors can use the new `generate:cache:ignore` hook to do so: ```js nuxt.hook('generate:cache:ignore', ignore => ignore.push('content')) ``` ## How it works When using the new `nuxt generate` with `static` target, a snapshot including checksum of non-ignored project files as well as nuxt version and some other configuration will be written `.nuxt/build.json`. In addition, we also move the build directory to `node_modules/.cache/nuxt`. Because `node_modules` is cached by all major platforms (Netlify, Vercel, ...) and common CI/CD scripts, this solution works out of the box without additional configuration. When `nuxt generate` is called subsequently, it will again create a checksum based on your project files and then compare it to the existing one inside `node_modules/.cache/nuxt/build.json`. If they match, it means that nothing is changed that needs rebuild so we can directly start rendering pages. If a mismatch is detected, it means that a full rebuild would be necessary. You can also see what file caused rebuild by checking console logs. After the build, nuxt generate will save the new checksum inside `.nuxt/build.json`. You can check full implementation [here](https://github.com/nuxt/nuxt.js/pull/7712). ### Back to old school commands With Nuxt v2.13, we introduced `nuxt export` and `nuxt serve` specially designed for the static target. With Nuxt v2.14, they are deprecated as `nuxt generate` and `nuxt start` is smart to detect the target and build when necessary. Server target: - `nuxt dev` = development server - `nuxt build` = build your application for production - `nuxt start` = start the production server (use it for Node.js hosting like Heroku, DigitalOcean, etc) Static target: - `nuxt dev` = development server - `nuxt generate` = build if needed and statically export to `dist/` - `nuxt start` = serve the `dist/` directory like your static hosting would do (Netlify, Vercel, Surge, etc), great for testing before deploying ## What to do next - Upgrade your project to [nuxt@2.14.0](https://github.com/nuxt/nuxt.js/releases/tag/v2.14.0) - Use `nuxt generate` instead of `nuxt export` - Use `nuxt start` instead of `nuxt serve` - Enjoy fast deployments 🤙 # Nuxt 2: From Terminal to Browser > How we changed the developer experience to stop switching between the terminal and browser. > Nuxt is a Vue.js framework to create different kind of web applications with the **same directory structure & conventions**: Universal, Single Page, PWA or Static Generated. *ℹ️ These features are all available with [v2.8.0 release](https://github.com/nuxt/nuxt.js/releases/tag/v2.8.0).* ## Problems 1. Developing JavaScript applications with Webpack or any bundler requires to switch between your browser and terminal for debugging purpose. 2. Using `console.log` to debug when the app is server rendered requires to remember that logs will be displayed on the terminal when refreshing the page. ## Solutions 1. Forwarding Webpack build state right in the browser and display them in a fancy manner. ![forward-webpack-build-state](https://nuxt.com/assets/blog/forward-webpack-build-state.gif){.rounded-lg.border.border-gray-700} 2. Same for Hot Module Replacement (really useful when the project gets bigger and takes more time to re-build). ![nuxt-build-indicator-hmr](https://nuxt.com/assets/blog/nuxt-build-indicator-hmr.gif){.rounded-lg.border.border-gray-700} 3. Forwarding SSR logs to the browser in development mode ![nuxt-ssr-logs-forwarding](https://nuxt.com/assets/blog/nuxt-ssr-logs-forwarding.gif){.rounded-lg.border.border-gray-700} ## Nuxt Vision The purpose to these changes is to use the terminal for commands only. Now you can focus right on your code and its visual result 🙂 > Be lazy, be smart, be Nuxt. Links: - Nuxt 2 docs: [nuxt.com/docs/2.x](https://nuxt.com/docs/2.x/getting-started/installation) - GitHub: <https://github.com/nuxt/nuxt.js> - Loading Screen source code: <https://github.com/nuxt/loading-screen> - Twitter: <https://x.com/nuxt_js> # Nuxt 3.3 > The 3.3.0 is a minor (feature) release with lots of performance and DX improvements, bug fixes and new features to play with. ## ✨ Local module development DX We've landed a raft of changes to enable local modules and improve DX. We now auto-scan your [`~/modules`](https://nuxt.com/docs/guide/directory-structure/modules) folder and register top level files there as modules in your project ([#19394](https://github.com/nuxt/nuxt/pull/19394)). When these files are changed, we'll automatically restart the nuxt server. ```diff export default defineNuxtConfig({ modules: [ '@nuxtjs/tailwindcss', - '~/modules/purge-comments' ] }) ``` We also now expose `nuxt/kit` for easy access to kit composables in your local project without having to install `@nuxt/kit` ([#19422](https://github.com/nuxt/nuxt/pull/19422)). [Read the documentation about local modules](https://nuxt.com/docs/guide/directory-structure/modules). ## ♻️ Restarting Nuxt You can add files to the `watch` array to automatically restart the server ([#19530](https://github.com/nuxt/nuxt/pull/19530)). This is likely to be particularly useful for module authors. You can also trigger a restart of the Nuxt server with the new `restart` hook ([#19084](https://github.com/nuxt/nuxt/pull/19084)). ## 🔥 Performance improvements We've increased static asset maxAge to 1 year as a matter of best practice ([#19335](https://github.com/nuxt/nuxt/pull/19335)), and support tree-shaking more of your build ([#19508](https://github.com/nuxt/nuxt/pull/19508)). ![nuxt-performance-improvements](https://nuxt.com/assets/blog/nuxt-performance-improvements.png){.rounded-lg.border.border-gray-700} We also now support preloading [`<NuxtLink>`](https://nuxt.com/docs/api/components/nuxt-link) with a route in object-syntax ([#19120](https://github.com/nuxt/nuxt/pull/19120)): ```html <NuxtLink :to="{ name: 'home', query: { year: '2023' } }">Home</NuxtLink> ``` We also track how long it takes each module you use to perform its setup, and warn if it takes too long. You can see all these values by running your dev server with `DEBUG=1` ([#18648](https://github.com/nuxt/nuxt/pull/18648)). ```sh DEBUG=1 npx nuxt dev ℹ Module pages took 1.5ms to setup. ℹ Module meta took 3.15ms to setup ℹ Module components took 4.5ms to setup. ... ``` You can also opt-in to some of Nuxt's internal optimisations by configuring composables to be treeshaken in a particular environment ([#19383](https://github.com/nuxt/nuxt/pull/19383)) or to have magic keys automatically injected ([#19490](https://github.com/nuxt/nuxt/pull/19490)). ## 🐛 Error handling We now handle chunk errors by default ([#19086](https://github.com/nuxt/nuxt/pull/19086)), meaning if your site updates with a redeploy, **we automatically handle reloading it on navigation**. To disable this behavior, set `experimental.emitRouteChunkError` option to `'manual'` and handle it yourself with the new [`reloadNuxtApp`](https://nuxt.com/docs/api/utils/reload-nuxt-app) composable. Learn more how we implemented in our [chunk-reload.client.ts plugin](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/plugins/chunk-reload.client.ts). You can also set `experimental.restoreState` to preserve some of your app state across reloads: ```ts [nuxt.config.ts] defineNuxtConfig({ experimental: { restoreState: true } }) ``` We also have a new experimental error handling component: [`<NuxtClientFallback>`](https://nuxt.com/docs/api/components/nuxt-client-fallback) ([#8216](https://github.com/nuxt/framework/pull/8216)) which can capture errors rendering on server, replace them with fallback content, and granularly trigger rerendering the part with an error on the client. This can be enabled with `experimental.clientFallback` - feedback very welcome! ## ⚡️ Head improvements We've migrated to use [unhead](https://github.com/unjs/unhead) directly ([#19519](https://github.com/nuxt/nuxt/pull/19519)) - and automatically tree-shake server-only head composables like `useServerHead` from your client build ([#19576](https://github.com/nuxt/nuxt/pull/19576)), meaning you can have great SEO without needing to include meta tag logic that's relevant only for crawlers in your client build. There's also a new [`useHeadSafe`](https://nuxt.com/docs/api/composables/use-head-safe) composable that handles sanitising untrusted user input ([#19548](https://github.com/nuxt/nuxt/pull/19548)). ## 🪵 Better logging in browser DevTools Working with the Chrome DevTools team, we've landed a couple of features across the unjs + Nuxt ecosystem meaning we now have first-class support for hiding Nuxt internal stack traces from logs in your (Chromium-based, for now) browser [#19243](https://github.com/nuxt/nuxt/pull/19243). We also landed a couple of improvements with stacktraces involving Nuxt hooks ([unjs/hookable#69](https://github.com/unjs/hookable/pull/69) and [unjs/hookable#68](https://github.com/unjs/hookable/pull/68)) implementing [`console.createTask`](https://developer.chrome.com/blog/devtools-modern-web-debugging/#linked-stack-traces). | Before | After | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | <img width="529" alt="before-nuxt-console-improvements" src="https://user-images.githubusercontent.com/28706372/220933126-56d9a0e5-e846-4958-a40a-e528a48bcb32.png"> | <img width="534" alt="after-nuxt-console-improvements" src="https://user-images.githubusercontent.com/28706372/220932932-932f193b-59a6-4385-8796-a62dcfd59c20.png"> | ## 💪 Type improvements Types for server API routes are now more correct - with non-serialisable types stripped out of the return type ([unjs/nitro#1002](https://github.com/unjs/nitro/pull/1002)). We also now type more of `NuxtApp` and correctly type unknown injections for greater type-safety ([#19643](https://github.com/nuxt/nuxt/pull/19643)). And if you were struggling with correct types when using `transform` + `default` with Nuxt data fetching composables, fear no more - we now infer the types correctly ([#19487](https://github.com/nuxt/nuxt/pull/19487)). ## ⚗️ Nitro enhancements This release comes with Nitro v2.3, which brings lots of improvements of its own. Check out [the release](https://github.com/unjs/nitro/releases/tag/v2.3.0) for more info. We now support [`useAppConfig`](https://nuxt.com/docs/api/composables/use-app-config) in nitro server routes ([#19489](https://github.com/nuxt/nuxt/pull/19489)) - a long-awaited change. Now [`useAppConfig`](https://nuxt.com/docs/api/composables/use-app-config) is consistently available throughout your app for non-runtime configuration from layers, modules, etc. We've also added a `nitro:build:public-assets` hook to allow modifying assets output from nitro's prerender/build phase ([#19638](https://github.com/nuxt/nuxt/pull/19638)). ## 🛠️ Build changes As part of moving towards [first-class support for PNP and pnpm support without `--shamefully-hoist`](https://github.com/nuxt/nuxt/issues/14146), we've dropped support for some internal (deprecated) utilities using CJS resolve patterns ([#19537](https://github.com/nuxt/nuxt/pull/19537) and [#19608](https://github.com/nuxt/nuxt/pull/19608)). We also now resolve dependencies like `nuxt`, `@nuxt/kit` and more using ESM search-paths. We'll be keeping a close eye on this. We're also preparing the groundwork for support of new TypeScript Node16 module resolution ([#19606](https://github.com/nuxt/nuxt/issues/19606)), and as part of this have changed the format of our runtime output (using `.js` instead of `.mjs` extensions, providing `types` fields for subpath exports, and more). ## 🗺️ Custom config schema (advanced) We've been testing out an experimental feature to allow modules and users to extend the Nuxt config schema ([#15592](https://github.com/nuxt/nuxt/issues/15592)), and we've now enabled this by default ([#19172](https://github.com/nuxt/nuxt/pull/19172)). We expect this will be particularly useful for module and layer/theme authors, and should result in some nicer DX for their users. ## Changelog See the full changelog by [comparing the changes](https://github.com/nuxt/nuxt/compare/v3.2.3...v3.3.0) or checkout the [release on GitHub](https://github.com/nuxt/nuxt/releases/tag/v3.3.0). We would like to thank all the 28 contributors who helped on this release 💚 # Nuxt 3.4 > Nuxt 3.4.0 is the latest release of Nuxt 3, bringing exciting new features, including support for the View Transitions API, transferring rich JavaScript payloads from server to client - and much more. ## 🪄 View Transitions API Support ::article-video{cloudinary="v1681229056/nuxt3/nuxt-view-transitions_cruvma"} :: You can check a demo on <https://nuxt-view-transitions.surge.sh> and the [source on StackBlitz](https://stackblitz.com/edit/nuxt-view-transitions). You may have noticed that Chromium-based browsers now ship a new web platform API: the [**View Transitions API**](https://developer.chrome.com/docs/web-platform/view-transitions/). This is an exciting new ability for native browser transitions which (among other things) have the ability to transition between unrelated elements on different pages. Nuxt now ships with an experimental implementation, which will be under active development during the v3.4 release cycle. See the known issues in the [linked PR](https://github.com/nuxt/nuxt/pull/20092). ```ts export default defineNuxtConfig({ experimental: { viewTransition: true } }) ``` ## ✨ Payload Enhancements We've merged a **[significant change to how Nuxt handles payloads](https://github.com/nuxt/nuxt/pull/19205)** (under an experimental flag). Payloads are used to send data from the server to the client when doing server-side rendering and avoid double data-fetching during the hydration phase. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { renderJsonPayloads: true } }) ``` With this new option enabled, this now means that **various rich JS types are supported out-of-the-box**: regular expressions, dates, Map and Set and BigInt as well as NuxtError - and Vue-specific objects like `ref`, `reactive`, `shallowRef` and `shallowReactive`. You can find [an example](https://github.com/nuxt/nuxt/blob/main/test/fixtures/basic/pages/json-payload.vue) in our test suite. This is all possible due to [Rich-Harris/devalue#58](https://github.com/Rich-Harris/devalue/pull/58). For a long time, Nuxt has been using our own fork of devalue owing to issues serialising Errors and other non-POJO objects, but we now have transitioned back to the original. You can even register your own custom types with a new object-syntax Nuxt plugin: ```ts [plugins/custom-payload-type.ts] export default definePayloadPlugin(() => { definePayloadReducer('BlinkingText', data => data === '<original-blink>' && '_') definePayloadReviver('BlinkingText', () => '<revivified-blink>') }) ``` You can read more about how this works [here](https://github.com/rich-harris/devalue#custom-types). **Note**: this only affects payloads of the Nuxt app, that is, data stored within `useState`, returned from `useAsyncData` or manually injected via `nuxtApp.payload`. It does not affect data fetched from Nitro server routes via `$fetch` or `useFetch` although this is one area I am keen to explore further. Preliminary testing shows a significant speed-up: **25% faster in total server response time** for a very minimal app with a large JSON payload, but I'd urge you to run your own tests and share the results with us. As mentioned, we're merging this behind a flag so we can test this broadly and gather feedback on the new approach. The most significant potential change is that the payload is now no longer available on `window.__NUXT__` immediately. Instead, we now need to initialise the Nuxt app to parse the payload so any code that accesses `__NUXT__` will need to be run in a plugin or later in the Nuxt app lifecycle. Please feel free to raise an issue if you foresee or encounter issues in your projects. ## 🎁 Object-syntax Nuxt plugins We now support object-syntax Nuxt plugins for better control over plugin *order* and easier registration of hooks. ```ts [plugins/my-plugin.ts] export default defineNuxtPlugin({ name: 'my-plugin', enforce: 'pre', // or 'post' async setup (nuxtApp) { // this is the equivalent of a normal functional plugin }, hooks: { // You can directly register Nuxt app hooks here 'app:created'() { const nuxtApp = useNuxtApp() // } } }) ``` In future we plan to enable build optimizations based on the metadata you pass in your Nuxt plugins. ## 🛠️ Easier Devtools Configuration It's even easier to enable Nuxt DevTools in your project: just set `devtools: true` in your `nuxt.config.ts` file to enable devtools. ```ts [nuxt.config.ts] export default defineNuxtConfig({ devtools: true }) ``` If it's not already installed, Nuxt will prompt to install it locally. This means you no longer need to have Nuxt DevTools enabled globally. **Note**: the DevTools is still experimental and under active development, so do be prepared for occasional unexpected behaviour, and please report issues directly to <https://github.com/nuxt/devtools> 🙏 ## 📚 Layers Improvements We now support [transforming `~`/`~~`/`@`/`@@` aliases within layers](https://github.com/nuxt/nuxt/pull/19986), meaning you now no longer need to use relative paths when importing within layers. This should mean it is much easier to use a 'normal' Nuxt project as a [layer](https://nuxt.com/docs/getting-started/layers#layers) without needing to specially write it as one. ## 🧸 Better Context Transforms We [now transform certain keys](https://github.com/nuxt/nuxt/pull/20182) of `definePageMeta` and `defineNuxtComponent` which means you should have fewer issues with a missing Nuxt instance. This includes support accessing the Nuxt instance after an `await` within `asyncData` and `setup` functions for those still using the Options API. And you no longer need to wrap `middleware` and `validate` with `defineNuxtRouteMiddleware` when using async functions. ## ♻️ Ecosystem Updates As usual, this release will pull in upstream improvements, including the new [Consola v3](https://github.com/unjs/consola) and [Nitropack v2.3.3](https://github.com/unjs/nitro) (a new minor is expected shortly). ## 🚨 'Breaking fixes' We've also taken the opportunity to do some cleanup in this minor release. 1. Previously it was possible to pass the `x-nuxt-no-ssr` header (undocumented) to force SPA rendering. We've now disabled this behaviour by default but you can get it back by setting `experimental.respectNoSSRHeader` to true. Alternatively, you can set `event.context.nuxt.noSSR` on the server to force SPA rendering. 2. We've [removed the (deprecated) `#head` alias](https://github.com/nuxt/nuxt/pull/20111) and also disabled the [polyfill for `@vueuse/head` behaviour](https://github.com/nuxt/nuxt/pull/20131) by default. (It can still be enabled with `experimental.polyfillVueUseHead`.) 3. We've [removed the (deprecated) `experimental.viteNode` option](https://github.com/nuxt/nuxt/pull/20112). It can be configured instead with `vite.devBundler`. 4. We've [deprecated accessing public runtime config without the `public` key](https://github.com/nuxt/nuxt/pull/20082). This was an undocumented compatibility measure with Nuxt 2 and we plan to remove it entirely in v3.5. 5. To fix a bug with our vue-router integration, we now generate a slightly different path matching syntax. If you were relying on the exact path generated, have a look at <https://github.com/nuxt/nuxt/pull/19902> for more information. ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. # Nuxt 3.5 > Nuxt 3.5.0 is out, bringing Vue 3.3, new defaults, interactive server components, typed pages, environment config - and much more. ## ⚡️ Vue 3.3 released Vue 3.3 has been released, with lots of exciting features, particularly around type support. - new `defineOptions` macro - 'generic' components - typed slots and using external types in defineProps - ... and more This also brings a significant improvement to data fetching when navigating between nested pages ([#20777](https://github.com/nuxt/nuxt/pull/20777)), thanks to [@antfu](https://github.com/antfu) and [@baiwusanyu-c](https://github.com/baiwusanyu-c). Read **[the full release announcement](https://blog.vuejs.org/posts/vue-3-3)** for more details. ### 🙌 Nitropack v2.4 We've been working on lots of improvements to Nitro and these have landed already in Nitro v2.4 - you may already have this upgrade, which contains a lot of bug fixes, updates to the module worker format for Cloudflare, Vercel KV support and more. One note: if you're deploying to Vercel or Netlify and want to benefit from incremental static regeneration, you should now update your route rules: ```diff routeRules: { -- '/blog/**': { swr: 3000 }, ++ '/blog/**': { isr: 3000 }, } ``` Read **[the full release notes](https://github.com/unjs/nitro/releases/tag/v2.4.0)**. ### 💖 Rich JSON payloads **Rich JSON payload serialisation** is now enabled by default ([#19205](https://github.com/nuxt/nuxt/pull/19205), [#20770](https://github.com/nuxt/nuxt/pull/20770)). This is both faster and allows serialising complex objects in the payload passed from the Nuxt server to client (and also when extracting payload data for prerendered sites). This now means that **various rich JS types are supported out-of-the-box**: regular expressions, dates, Map and Set and BigInt as well as NuxtError - and Vue-specific objects like `ref`, `reactive`, `shallowRef` and `shallowReactive`. You can find [an example](https://github.com/nuxt/nuxt/blob/main/test/fixtures/basic/pages/json-payload.vue) in our test suite. This is all possible due to [Rich-Harris/devalue#58](https://github.com/Rich-Harris/devalue/pull/58). For a long time, Nuxt has been using our own fork of devalue owing to issues serialising Errors and other non-POJO objects, but we now have transitioned back to the original. You can even register your own custom types with a new object-syntax Nuxt plugin: ```ts [plugins/custom-payload-type.ts] export default definePayloadPlugin(() => { definePayloadReducer('BlinkingText', data => data === '<original-blink>' && '_') definePayloadReviver('BlinkingText', () => '<revivified-blink>') }) ``` You can read more about how this works [here](https://github.com/rich-harris/devalue#custom-types). ## 🛝 Interactive server components This feature should be considered highly experimental, but thanks to some great work from @huang-julien we now support interactive content within server components via *slots* ([#20284](https://github.com/nuxt/nuxt/pull/20284)). You can follow the server component roadmap at [#19772](https://github.com/nuxt/nuxt/issues/19772). ## ⏰ Environment config You can now configure fully typed, per-environment overrides in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ $production: { routeRules: { '/**': { isr: true } } }, $development: { // } }) ``` If you're authoring layers, you can also use the `$meta` key to provide metadata that you or the consumers of your layer might use. Read more [about per-environment overrides](https://github.com/nuxt/nuxt/pull/20329). ## 💪 Fully typed pages You can benefit from fully typed routing within your Nuxt app via this experimental integration with [unplugin-vue-router](https://github.com/posva/unplugin-vue-router) - thanks to some great work from [@posva](https://github.com/posva)! Out of the box, this will enable typed usage of [`navigateTo`](https://nuxt.com/docs/api/utils/navigate-to), [`<NuxtLink>`](https://nuxt.com/docs/api/components/nuxt-link), `router.push()` and more. You can even get typed params within a page by using `const route = useRoute('route-name')`{lang="ts"}. Enable this feature directly in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { typedPages: true } }) ``` ## 🔎 'Bundler' module resolution We now have full support within Nuxt for the `bundler` strategy of [module resolution](https://www.typescriptlang.org/docs/handbook/module-resolution.html). We would recommend adopting this if possible. It has type support for subpath exports, for example, but more exactly matches the behaviour of build tools like Vite and Nuxt than `Node16` resolution. ```ts [nuxt.config.ts] export default defineNuxtConfig({ typescript: { tsConfig: { compilerOptions: { moduleResolution: 'bundler' } } } }) ``` This turns on TypeScript's ability to 'follow' Node subpath exports. For example, if a library has a subpath export like `mylib/path` that is mapped to `mylib/dist/path.mjs` then the types for this can be pulled in from `mylib/dist/path.d.ts` rather than requiring the library author to create `mylib/path.d.ts`. ## ⚗️ Separate server types We plan to improve clarity within your IDE between the 'nitro' and 'vue' part of your app, we've shipped the first part of this via a separate generated `tsconfig.json` for your [`~/server`](https://nuxt.com/docs/guide/directory-structure/server) directory ([#20559](https://github.com/nuxt/nuxt/pull/20559)). You can use by adding an additional `~/server/tsconfig.json` with the following content: ```json { "extends": "../.nuxt/tsconfig.server.json" } ``` Although right now these values won't be respected when type checking (`nuxi typecheck`), you should get better type hints in your IDE. ## 💀 Deprecations Although we have not typed or documented the `build.extend` hook from Nuxt 2, we have been calling it within the webpack builder. We are now explicitly deprecating this and will remove it in a future minor version. ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## 📃 Full changelog Read the full release note on <https://github.com/nuxt/nuxt/releases/tag/v3.5.0> # Nuxt 3.6 > Nuxt 3.6 is out, bringing performance improvements, fully static server components, better style inlining, static presets, increased type safety - and much more. ## <img style="display:inline" src="https://nuxt.com/assets/design-kit/icon-green.svg" width="36" height="36" alt="SPA loading indicator" valign="center">  SPA loading indicator If your site is served with `ssr: false` or you have disabled server-rendering on some of your pages, you might be particularly interested in the new [built-in SPA loading indicator](https://github.com/nuxt/nuxt/pull/21640). You can now place an HTML file in `~/app/spa-loading-template.html` with some HTML you would like to use to render a loading screen that will be rendered until your app is hydrated on these pages. ::note In Nuxt v4 (or with `compatibilityMode: 4`), this path is now `~/spa-loading-template.html`. :: 👉 **By default an animated Nuxt icon is rendered**. You can completely disable this indicator by setting `spaLoadingTemplate: false` in your nuxt configuration file. ## ⚡️ Performance improvements The first thing that happens when your app is hydrated is that your plugins run, and so we now perform [build-time optimisations on your plugins](https://github.com/nuxt/nuxt/pull/21611), meaning they do not need to be normalised or reordered at runtime. We also include your error component JS in your main entrypoint, meaning that if an error occurs when a user has no connectivity, you can still handle it with your `~/error.vue`. (This also should decrease your total bundle size.) 👉 Compared to Nuxt 3.5.3, the minimal client bundle has decreased by \~0.7kB. Let's keep this up! ## 🔥 Fully static server components It has been possible to use server components on static pages, but until now they would increase the payload size of your application. That is no longer true. We now store [rendered server components as separate files, which are preloaded before navigation](https://github.com/nuxt/nuxt/pull/21461). 👉 **This does rely on the new, richer JSON payload format**, so make sure you have not disabled this by setting `experimental.renderJsonPayloads` to false. ## 🎨 Better style inlining If you're monitoring your metrics closely and have not turned off `experimental.inlineSSRStyles`, you should see more CSS inlined in your page, and a significantly external CSS file. We're now [better at deduplicating global CSS](https://github.com/nuxt/nuxt/pull/21573), particularly added by libraries like tailwind or unocss. ## 🎬 Animation controls To give you more fine-grained control over your page/layout components, for example to create custom transitions with GSAP or other libraries, we now allow you to set [`pageRef` on `<NuxtPage>`](https://github.com/nuxt/nuxt/pull/19403) and [`layoutRef` on `<NuxtLayout>`](https://github.com/nuxt/nuxt/pull/19465). These will get passed through to the underlying DOM elements. ## ✨ Automatic 'static' preset detection Up to now, running `nuxt generate` produced the same output on every deployment provider, but with Nuxt 3.6 we now enable [static provider presets](https://github.com/nuxt/nuxt/pull/21655) automatically. That means if you are deploying a static build (produced with `nuxt generate`) to a supported provider (currently vercel and netlify with cloudflare and github pages coming soon) we'll prerender your pages with special support for that provider. This means we can configure any route rules (redirects/headers/etc) that do not require a server function. So you should get the best of both worlds when deploying a site that doesn't require runtime SSR. It also unblocks use of [Nuxt Image](https://github.com/nuxt/image) on Vercel (with more potential for automatic provider integration coming soon). ## 💪 Increased type safety We now have better support for server-specific `#imports` and augmentations if you are using the new `~/server/tsconfig.json` we shipped in Nuxt 3.5. So when importing from `#imports` in your server directory, you'll get IDE auto-completion for the right import locations in Nitro, and won't see Vue auto-imports like `useFetch` that are unavailable within your server routes. You should now also have [type support for runtime Nitro hooks](https://github.com/nuxt/nuxt/pull/21666). Finally, we have [removed more locations where objects had a default `any` type](https://github.com/nuxt/nuxt/pull/21700). This should improve type safety within Nuxt in a number of locations where unspecified types fell back to any: - `RuntimeConfig` - `PageMeta` - `NuxtApp['payload']` (accessible now from `NuxtPayload` interface) - `ModuleMeta` You can find out more about how to [update your code](https://github.com/nuxt/nuxt/pull/21700) if this affects you in the original PR. ## ⚗️ Nitro 2.5 built-in This release ships with new Nitro 2.5, which has a [whole list of exciting improvements](https://github.com/unjs/nitro/releases/tag/v2.5.0) that are worth checking out. Of particular note is experimental support for streaming, which is also enabled by a [couple of changes](https://github.com/nuxt/nuxt/pull/21665) in Nuxt itself. ## 🛠️ New tools for module authors This release brings a number of utilities for modules authors to easily [add type templates](https://github.com/nuxt/nuxt/pull/21331) and [assert compatibility](https://github.com/nuxt/nuxt/pull/21246) with a given version of *another* module. In addition, this release will finally unlock a new `nuxt/module-builder` mode that should improve type support for module authors. If you're a module author, you might consider following [these migration steps](https://github.com/nuxt/starter/pull/392) to try it out in the coming days. ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## 📃 Full changelog Read the full release notes on <https://github.com/nuxt/nuxt/releases/tag/v3.6.0> # Nuxt on the Edge > Learn how we made Nuxt 3 capable of running on edge runtimes to run with server-side rendering close to your users. ## Introduction In September 2017, Cloudflare [introduced Cloudflare Workers](https://blog.cloudflare.com/introducing-cloudflare-workers/), giving the ability to run JavaScript on their [edge network](https://www.cloudflare.com/network/). This means your code will deploy on the entire edge network in over a hundred locations worldwide in about 30 seconds. This technology allows you to focus on writing your application close to your users, wherever they are in the world (\~50ms latency). The worker's runtime is not the same as Node.js or the Browser, it executes the code using V8, the JavaScript engine developed by Google Chrome. Until now, what you could run on their platform were small scripts running on the edge before hitting your server to increase the performance or add some logic based on request headers, for example. In November 2020, while working on Nuxt 3, **we made the bet to run Nuxt in-production on edge runtimes / V8 isolates**. It unlocks the ability to server-render pages in \~50ms from all over the world when using a platform like CloudFlare Workers, without having to deal with servers, load balancers and caching, for about [$0.3 per million requests](https://developers.cloudflare.com/workers/platform/pricing/). As of today, new platforms are coming to let run apps on V8 isolates such as Deno Deploy. ::note **2024 update:** I released [NuxtHub](https://hub.nuxt.com) to let you build full-stack applications with Nuxt on the edge, on your Cloudflare account with zero configuration. It includes a database, blob storage, KV, remote storage and more. :: ::note **2026 update:** NuxtHub is now multi-providers. It adds database, KV, blob storage, and cache to your Nuxt application and works on Vercel, Cloudflare, Netlify, Deno, and more. Learn more on [hub.nuxt.com](https://hub.nuxt.com). :: ## The Challenge In order to make Nuxt run in workers, we had to rewrite some parts of Nuxt to be environmentally agnostic (runs in Node.js, Browser or V8). We started with our server and created [unjs/h3](http://github.com/unjs/h3): a minimal http framework built for high performance and portability. It replaces [Connect](https://github.com/senchalabs/connect) we used in Nuxt 2 but has compatibility with it so you can keep using Connect/Express middleware. In the workers, for each incoming request, it starts Nuxt in production, sends the request to it and sends back the response. In Nuxt 2, the duration to start the server in production in memory (also named cold start) was about \~300ms, because we had to load all the dependencies of your server and application in order to handle the request. By working on h3, we decided to code-split each handler attached to the server and lazy-load them only when requested. When you start Nuxt 3, we only load h3 in memory and the corresponding handlers. When a request comes in, we load the handler corresponding to the route and execute it. ::video{src="https://res.cloudinary.com/nuxt/video/upload/v1689236511/nuxt3/nuxt3-server-performance.mp4" poster="https://res.cloudinary.com/nuxt/video/upload/v1689236511/nuxt3/nuxt3-server-performance.jpg" controls} :: By adopting this approach, **we reduced the cold start from \~300ms to \~2ms**. We had another challenge in order to run Nuxt on the edge: the production bundle size. This includes the server, Vue app and Node.js dependencies combined. Cloudflare workers currently have a limit of 1MB (free plan) and 5MB ($5 per month plan) for the worker size. In order to achieve this, we created [unjs/nitro](https://nitro.unjs.io/), our server engine, when running the `nuxt build` command, it bundles your whole project and includes all dependencies into the final output. It uses [Rollup](https://rollupjs.org/) and [vercel/nft](https://github.com/vercel/nft) to trace only the code used by the `node_modules` to remove unnecessary code. **The total size of the generated output for a basic Nuxt 3 application is about 700kB gzip.** Lastly, to provide the same developer experience between development (Node.js) and production on Cloudflare (Edge runtime), we created [unjs/unenv](https://github.com/unjs/unenv): a library to convert JavaScript code to run everywhere (platform agnostic) by mocking or adding polyfills for known dependencies. **At Nuxt, we believe that you should have the freedom to choose the hosting provider that fits you best.** This is why you can deploy a Nuxt application with edge-side rendering on: - [NuxtHub](https://hub.nuxt.com) - [Cloudflare Page](https://nitro.unjs.io/deploy/providers/cloudflare#cloudflare-pages) - [Deno Deploy](https://nitro.unjs.io/deploy/providers/deno-deploy) - [Vercel Edge Functions](https://nitro.unjs.io/deploy/providers/vercel#vercel-edge-functions) (using CloudFlare Workers under the hood) - [Netlify Edge Functions](https://nitro.unjs.io/deploy/providers/netlify#netlify-edge-functions) (using Deno under the hood) We also support many other deployment providers, including [static hosting](https://nuxt.com/docs/getting-started/deployment#static-hosting) or [traditional Node.js serverless and server hosts](https://nuxt.com/docs/getting-started/deployment#nodejs-server). ## Pushing Full-stack Capabilities Now that we have Nuxt running on edge runtime, we can do more than render a Vue application. Thanks to the [server directory](https://nuxt.com/docs/guide/directory-structure/server), creating an API route is a TypeScript file away. To add the `/api/hello` route, create a `server/api/hello.ts` file: ```ts [server/api/hello.ts] export default defineEventHandler((event) => { return { hello: 'world' } }) ``` You can now universally call this API in your pages and components: ```vue [pages/index.vue] <script setup> const { data } = await useFetch('/api/hello') </script> <template> <pre>{{ data }}</pre> </template> ``` One important thing to note when we created [useFetch](https://nuxt.com/docs/api/composables/use-fetch) and [$fetch](https://nuxt.com/docs/api/utils/dollarfetch) is that during server-side rendering, if you call your API routes, it will emulate the request and call the function code directly: **avoiding an HTTP request and reducing page’s rendering time**. In terms of developer experience, you will notice that when creating server files, the Nuxt server keeps running without rebuilding the Vue app. **This is because Nuxt 3 supports Hot Module Replacement (HMR) when creating API and server routes.** Furthermore, by leveraging Object Relational Mapping (ORM) like [drizzle-orm](https://orm.drizzle.team/), developers can connect Edge & Serverless databases such as [D1](https://developers.cloudflare.com/d1/), [Turso](https://turso.tech/), [Neon](https://neon.tech), [Planetscale](https://planetscale.com/) and more. I created [Atidone](https://todos.nuxt.dev/), an open source demo to showcase a full-stack application with authentication and a database running on the edge. The source code is available on GitHub under the MIT license at [atinux/atidone](https://github.com/atinux/atidone). ## Conclusion We are excited about edge-side rendering and what it unlocks. Our team at Nuxt can’t wait to see what you will build on top of this! Feel free to join our [Discord server](https://discord.com/invite/nuxt) or mention [@nuxt_js](https://x.com/nuxt_js) on Twitter to share your work. # Nuxt 3.7 > Nuxt 3.7 is out, bringing a new CLI, native web streams and response, rendering optimisations, async context support - and much more. ### 🐣 A New CLI We've refactored `nuxi` using [unjs/citty](http://github.com/unjs/citty) and this marks the first Nuxt release that depends on the new version, safely in its own repository. We have grand plans for this - check out some of the features + roadmap discussions in [nuxt/cli](https://github.com/nuxt/cli) and please feel free to contribute! - <a href="https://github.com/nuxt/cli/discussions/3" target="_blank"><strong>Project Goals</strong></a> - <a href="https://github.com/nuxt/cli/discussions/4" target="_blank">Feedbacks and Ideas</a> - <a href="https://github.com/nuxt/cli/discussions/7" target="_blank">The journey of Nuxt CLI Generations</a> Nuxi is now decoupled from the main `nuxt` version - we plan to iterate and release nuxi more quickly in future so you can expect new things coming soon! ### 🕸️ Native Web Streams and `Response` With improvements in [unjs/h3](https://github.com/unjs/h3) and [unjs/nitro](https://github.com/unjs/nitro), it's now possible to directly return a `Response` object from server routes, meaning it's *also* possible to return and handle streams natively in Nuxt. 👉 Check out the full detail in the [unjs/h3](https://github.com/unjs/h3/releases) and [unjs/nitro](https://github.com/unjs/nitro/releases) release notes. ### 🔥 HTML Rendering Optimisations This release comes with a couple of improvements in rendering HTML responses from the server. We now determine whether to preload/prefetch resources at build time (so you can customise this in the `build:manifest` hook). We also now manage rendering the HTML for them directly in `unhead` ([#22179](https://github.com/nuxt/nuxt/pull/22179)), which means you can configure the *order* for `<link>`, `<meta>`, `<script>`, `<style>`, and more. And - in our preliminary testing - it's even faster! It's possible to opt-in to upcoming head improvements with the `experimental.headNext` flag. This currently includes a new ordering algorithm based on [capo.js](https://github.com/rviscomi/capo.js) ([#22431](https://github.com/nuxt/nuxt/pull/22431)) and allows enabling future optimisations as they are released in `unhead`: ```ts export default defineNuxtConfig({ experimental: { headNext: true } }) ``` We'd love your thoughts - you can respond with any issues/feedback in [this discussion](https://github.com/nuxt/nuxt/discussions/22632). ### 🛠️ Build Environment Shortcuts In your Nuxt config you can now use `$client` and `$server` shortcuts to easily define configuration that is specific to just the Vite client/server ([#22302](https://github.com/nuxt/nuxt/pull/22302)) or webpack client/server ([#22304](https://github.com/nuxt/nuxt/pull/22304)) builds. This previously was only possible with the `vite:extendConfig` and `webpack:config` hooks. For example: ```ts export default defineNuxtConfig({ vite: { $client: { build: { rollupOptions: { output: { chunkFileNames: '_nuxt/[hash].js', assetFileNames: '_nuxt/[hash][extname]', entryFileNames: '_nuxt/[hash].js' } } } } } }) ``` ### ⚡️ Vite 4.4 We've chosen to unpin Vite from minor versions, meaning whenever Vite releases a new feature version you can opt-in straight away. Vite 4.4 brings a lot of exciting things, including experimental Lightning CSS support - and much more! 👉 Check out the [Vite release notes](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#440-2023-07-06) for more. ### 💪 TypeScript Updates We now use purely relative paths in the generated `tsconfig.json` instead of setting a `baseUrl`. This means better support for dev environments like docker images where the absolute path may not match your IDE ([#22410](https://github.com/nuxt/nuxt/pull/22410)). We also set a couple of additional compiler flag defaults to match Vite/TS recommendations ([#22468](https://github.com/nuxt/nuxt/pull/22468)). Plus, you should now get type hinted access to layouts in `setPageLayout` and also in `<NuxtLayout name>` ([#22363](https://github.com/nuxt/nuxt/pull/22362)). ### 🦄 Async Context support If you've ever got an issue with 'Nuxt context unavailable' this might be one for you. We now support native async context for Bun and Node under an experimental flag, in both Nuxt and Nitro ([#20918](https://github.com/nuxt/nuxt/pull/20918)). This enables using Nuxt composables on the server *without* needing to ensure they are being called directly in a setup function. It also allows the same in Nitro, with a new `useEvent()` utility that is usable in server routes. To try it out, you can enable `experimental.asyncContext`: ```ts export default defineNuxtConfig({ experimental: { asyncContext: true } }) ``` ### 👓 Watcher Updates We've fixed a couple of issues with watchers, meaning that you should need to restart your server less often - and you should see a significant performance increase if you are using layers. ### ⚗️ Nitro 2.6 There lots more exciting features coming directly from Nitro 2.6, including smaller, lighter servers and new persistent data storage in a `.data` directory. 👉 Read more in [the full release article](https://unjs.io/blog/2023-08-25-nitro-2.6). ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the [unjs](https://github.com/unjs) ecosystem. ## 📃 Full changelog Read the full release notes on <https://github.com/nuxt/nuxt/releases/tag/v3.7.0> # A New Website > We are thrilled to release the new nuxt.com, powered by Nuxt UI and now open source. [Nuxt.com](http://Nuxt.com) is the main entry point when you want to learn Nuxt. With **more than 300k visitors every month**, it was time to give it a new look and feel. ## New Design We are back to the original colors of Nuxt, with a navy background (`#020420`) and its signature shiny green (`#00DC82`). ::nuxt-img ```yaml [props] src: https://nuxt.com/assets/blog/website/nuxt-website.png alt: Nuxt Website Screenshot width: '832' height: '497' class: rounded-lg border border-gray-700 ``` :: ::read-more{to="https://nuxt.com/design-kit" icon="i-lucide-palette"} Discover the **Nuxt Design Kit** as well as our **Logo History**. :: We wanted to achieve a consistent design across all our official documentations: ::div{.grid.sm:grid-cols-2.gap-4} :::nuxt-link{to="https://image.nuxt.com" target="_blank" .hover:border-transparent} ::::nuxt-img ```yaml [props] src: https://nuxt.com/assets/blog/website/nuxt-image.png alt: Nuxt Image width: '408' height: '255' class: m-0 border rounded-md border-gray-700 ``` :::: ::: :::nuxt-link{to="https://content.nuxt.com" target="_blank" .hover:border-transparent} ::::nuxt-img ```yaml [props] src: https://nuxt.com/assets/blog/website/nuxt-content.png alt: Nuxt Content width: '408' height: '255' class: m-0 border rounded-md border-gray-700 ``` :::: ::: :::nuxt-link{to="https://nuxt.com/modules/devtools" .hover:border-transparent} ::::nuxt-img ```yaml [props] src: https://nuxt.com/assets/blog/website/nuxt-devtools.png alt: Nuxt DevTools width: '408' height: '255' class: m-0 border rounded-md border-gray-700 ``` :::: ::: :::nuxt-link{to="https://ui.nuxt.com" target="_blank" .hover:border-transparent} ::::nuxt-img ```yaml [props] src: https://nuxt.com/assets/blog/website/nuxt-ui.png alt: Nuxt UI width: '408' height: '255' class: m-0 border rounded-md border-gray-700 ``` :::: ::: :: We really love this new design and hope you do too. **This is only the first step toward many improvements coming to the website.** ## Improved Navigation From now on, you can easily jump between the five main documentation categories: ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1697548111/nuxt3/nuxt-website-docs-nav.jpg" controls .rounded.dark:border.dark:border-gray-700} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697548111/nuxt3/nuxt-website-docs-nav.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697548111/nuxt3/nuxt-website-docs-nav.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697548111/nuxt3/nuxt-website-docs-nav.ogg" type="video/ogg"} ::: :: On the right side, you can see the table of contents as well as community shortcuts: Edit this page, Chat on Discord, etc. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1697549697/nuxt3/nuxt-website-docs-aside.jpg" controls .rounded.dark:border.dark:border-gray-700} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697549697/nuxt3/nuxt-website-docs-aside.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697549697/nuxt3/nuxt-website-docs-aside.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697549697/nuxt3/nuxt-website-docs-aside.ogg" type="video/ogg"} ::: :: ## Source Code Buttons When looking at Nuxt built-in [components](https://nuxt.com/docs/api/components), [composables](https://nuxt.com/docs/api/composables), [utils](https://nuxt.com/docs/api/utils), [commands](https://nuxt.com/docs/api/commands) and [kit utilities](https://nuxt.com/docs/api/kit), you can now jump to the source code by clicking on the :u-button{size="xs" color="gray" icon="i-simple-icons-github"}[Source] button. ::nuxt-img ```yaml [props] src: https://nuxt.com/assets/blog/website/nuxt-website-source-button.png alt: Nuxt Source Code Button width: '818' height: '343' class: border rounded border-gray-700 ``` :: ::read-more{to="https://nuxt.com/docs/api/components/nuxt-link"} Checkout an example on `<NuxtLink>` documentation page. :: ## Improved Search Feature You may notice a new modal when hitting :kbd{value="meta"} :kbd{value="K"}. We leverage the Nuxt UI [`<CommandPalette>`](https://ui.nuxt.com/components/command-palette) components combined with Nuxt Content data (search & navigation) to provide a better search experience. With the command palette, you can: - Jump to a page - Search in the documentation - Search a module - Switch the color mode We plan to add more commands soon. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1697550571/nuxt3/nuxt-website-search.jpg" controls .rounded.dark:border.dark:border-gray-700} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697550571/nuxt3/nuxt-website-search.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697550571/nuxt3/nuxt-website-search.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697550571/nuxt3/nuxt-website-search.ogg" type="video/ogg"} ::: :: ## Migration to Nuxt UI The new website is powered by [Nuxt UI](https://ui.nuxt.com), our UI library tailored made for Nuxt and built on top of [Tailwind CSS](https://tailwindcss.com) & [Headless UI](https://headlessui.com/). The website also uses [Nuxt UI Pro](https://ui.nuxt.com/pro), a set of premium components built on top of Nuxt UI to create beautiful & responsive Nuxt applications in minutes. It includes components such as `<UHeader>`, `<UFooter>`, `<ULandingHero>`, `<ULandingCard>` and more. ::note We plan to launch the full documentation of Nuxt UI Pro at the end of October. If you cannot wait and want early access, you can already [purchase a license](https://ui.nuxt.com/pro/purchase) now and get access to our private repository on GitHub. :: This [migration](https://github.com/nuxt/nuxt.com/pull/1365) was a great opportunity to improve Nuxt UI & UI Pro and fix some bugs, as well as a difference of [+9,004]{.text-primary} / [-23,113]{.text-error} lines of code changed. ::read-more{to="https://ui.nuxt.com" icon="i-simple-icons-nuxtdotjs" target="_blank"} Read more about **Nuxt UI**. :: ## Open Graph Images We are big fans of having a custom image when we share a link on social media. That's why we have added OG images on all our documentation pages. Example of the [Installation page](https://nuxt.com/docs/getting-started/installation): ![Nuxt OG Image](https://nuxt.com/__og-image__/image/docs/getting-started/introduction/og.png){width="1200" height="630" .border.rounded.border-gray-700}::read-more{to="https://nuxtseo.com/og-image/getting-started/installation" target="_blank"} Discover the **Nuxt OG Image** module. :: ## Available on GitHub We are proud to announce that the website is **now open source** and available on GitHub. ::read-more ```yaml [props] to: https://github.com/nuxt/nuxt.com icon: i-simple-icons-github color: gray target: _blank ``` Check out `nuxt/nuxt.com` on GitHub. :: ## What's next? This new website is the beginning of upcoming changes we are planing, some of them are: - Team & Contributors pages - Integrations page to showcase all the possibilities with Nuxt: Hosting, CMS, Database, etc. - Templates page (currently [nuxt.new](https://nuxt.new)) to list official and community starters - And more... **We are looking forward to your feedback on [Twitter](https://x.com/nuxt_js), [Discord](https://discord.com/invite/nuxt) or [GitHub](https://github.com/nuxt/nuxt.com)**. Thank you for reading this blog post, and happy Nuxting 🤟 # Nuxt 3.8 > Nuxt 3.8 is out, bringing built-in DevTools, automatic Nuxt Image install, a new app manifest and much more. ### 💻 CLI Improvements Just to remind you, we're now using [the new Nuxt CLI](https://github.com/nuxt/cli) which is now versioned separately. ::tip You can now install a module with `nuxi module add <module-name>` :: ::note{icon="i-lucide-rocket"} We now share the same port with the Vite websocket, meaning better support for docker containers in development. :: ::read-more ```yaml [props] to: https://github.com/nuxt/cli/releases/tag/v3.9.0 icon: i-simple-icons-github color: gray target: _blank ``` Read the Nuxt CLI `v3.9.0` release notes. :: ### ✨ Built-in Nuxt DevTools Nuxt DevTools v1.0.0 is out and we now think it's ready to be shipped as a direct dependency of Nuxt. ::read-more{to="https://nuxt.com/blog/nuxt-devtools-v1-0" icon="i-simple-icons-github" color="gray"} Check out Nuxt DevTools v1.0 announcement. :: ### 📸 Nuxt Image Auto-install [`<NuxtImg>`](https://nuxt.com/docs/api/components/nuxt-img) and [`<NuxtPicture>`](https://nuxt.com/docs/api/components/nuxt-picture) first-class built-in components. We now auto-installing `@nuxt/image` the first time that they are used ([#23717](https://github.com/nuxt/nuxt/pull/23717)). ::video{poster="https://res.cloudinary.com/nuxt/video/upload/v1697721767/nuxt3/nuxt-image-auto-install_uqkptq.jpg" controls .rounded.dark:border.dark:border-gray-700} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697721767/nuxt3/nuxt-image-auto-install_uqkptq.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697721767/nuxt3/nuxt-image-auto-install_uqkptq.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1697721767/nuxt3/nuxt-image-auto-install_uqkptq.ogg" type="video/ogg"} ::: :: ::tip We advise using [`@nuxt/image`](https://image.nuxt.com) if you're using images in your site; it can apply optimisations to make your site more performant. :: ### 📂 Deeper Layout Scanning ::caution This is a behaviour change so do take care with this one. :: We now support scanning layouts within subfolders in [`~/layouts`](https://nuxt.com/docs/guide/directory-structure/layouts) in the same way as we do with [`~/components`](https://nuxt.com/docs/guide/directory-structure/components). | File | Layout name | | ------------------------------- | ----------------- | | ~/layouts/desktop/default.vue | 'desktop-default' | | ~/layouts/desktop-base/base.vue | 'desktop-base' | | ~/layouts/desktop/index.vue | 'desktop' | ::read-more{to="https://nuxt.com/docs/guide/directory-structure/layouts#named-layout"} Read more about **Named Layouts**. :: ### 📊 App Manifest We now support a built-in app manifest (see [PR #21641](https://github.com/nuxt/nuxt/pull/21641)), which generates a manifest at `/_nuxt/builds/meta/<buildId>.json`. It enables loading payloads **only for prerendered routes**, if a site is generated with `nuxt generate`, preventing 404s in the console. It also enables **client-side route rules**. Only `redirect` route rules is supported for now; they will now redirect when performing client-side navigation. ::code-group ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/about': { redirect: '/about-us' } } }) ``` ```vue [pages/index.vue] <template> <div> <!-- Will be redirected to /about-us on client-side --> <NuxtLink to="/about">About</NuxtLink> </div> </template> ``` :: ::tip{icon="i-lucide-rocket"} The app manifest also enables future enhancements including detection of a new deployment by checking `/_nuxt/builds/latest.json`. :: ::note You can **opt-on from this behaviour if you need to** by setting `experimental.appManifest` to `false` in your `nuxt.config.ts` file. :: ### 🤝 Scope and Context Improvements We now define a 'scope' for Nuxt composables executed in plugins ([#23667](https://github.com/nuxt/nuxt/pull/23667)), which allows running synchronous cleanup before navigating away from your site, using the Vue [`onScopeDispose`](https://vuejs.org/api/reactivity-advanced.html#onscopedispose) lifecycle method. ::note This should fix an edge case with cookies ([#23697](https://github.com/nuxt/nuxt/pull/23697)) and also improves memory management such as Pinia stores ([#23650](https://github.com/nuxt/nuxt/issues/23650)). :: ::read-more{to="https://vuejs.org/api/reactivity-advanced.html#effectscope" icon="i-simple-icons-vuedotjs" target="_blank"} Read more about Vue effect scopes. :: We also now support [**native async context**](https://nodejs.org/api/async_context.html) for the *Vue composition API* ([#23526](https://github.com/nuxt/nuxt/pull/23526)). In case you're unaware, we support native async context on Node and Bun, enabled with [`experimental.asyncContext`](https://nuxt.com/docs/guide/going-further/experimental-features#asynccontext). If you experience issues with `Nuxt instance unavailable`, enabling this option may solve your issues: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { asyncContext: true } }) ``` ::note Once we have cross-runtime support, we will enable it by default. :: ### 🔗 NuxtLink Defaults You can define your own [`<NuxtLink>`](https://nuxt.com/docs/api/components/nuxt-link) components with the [`defineNuxtLink`](https://nuxt.com/docs/api/components/nuxt-link#definenuxtlink-signature) utility. Today, you can customize the options for the built-in [`<NuxtLink>`](https://nuxt.com/docs/api/components/nuxt-link), directly in your `nuxt.config.ts` file ([#23724](https://github.com/nuxt/nuxt/pull/23724)). This can enable you to enforce trailing slash behaviour across your entire site, for example: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { activeClass: 'nuxt-link-active', trailingSlash: 'append' } } } }) ``` ### ⚡️ Data Fetching Improvements We have two very significant new features for [`useAsyncData`](https://nuxt.com/docs/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch): 1. You can now set `deep: false` to prevent deep reactivity on the `data` object returned from these composables ([#23600](https://github.com/nuxt/nuxt/pull/23600)). It should be a performance improvement if you are returning large arrays or objects. The object will still update when refetched; it just won't trigger reactive effects if you change a property deep within the `data`. 2. You can now use the `getCachedData` option to handle custom caching for these composables ([#20747](https://github.com/nuxt/nuxt/pull/20747)) ```vue [pages/index.vue] <script setup> const nuxtApp = useNuxtApp() const { data } = await useAsyncData(() => { /* fetcher */ }, { // this will not refetch if the key exists in the payload getCachedData: key => nuxtApp.payload.static[key] ?? nuxtApp.payload.data[key] }) </script> ``` ::video-accordion{title="Watch the video from Alexander Lichter about Client-side caching with getCachedData." videoId="aQPR0xn-MMk"} :: We also support configuring some default values for these composables in an app-wide way ([#23725](https://github.com/nuxt/nuxt/pull/20747)): ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { useAsyncData: { deep: false }, useFetch: { retry: false, retryDelay: 100, retryStatusCodes: [500], timeout: 100 } } } }) ``` ### 🔢 Layer Improvements We now more carefully load layer plugins ([#22889](https://github.com/nuxt/nuxt/pull/22889) and [#23148](https://github.com/nuxt/nuxt/pull/23148)) and middleware ([#22925](https://github.com/nuxt/nuxt/pull/22925) and [#23552](https://github.com/nuxt/nuxt/pull/23552)) in the order of the layers, always loading your own plugins and middleware last. This should mean you can rely on utilities that layers may inject. And probably one of the most significant changes - if you are using remote layers we now clone these within your [`node_modules/`](https://nuxt.com/docs/guide/directory-structure/node_modules) folder ([#109](https://github.com/unjs/c12/pull/109)) so layers can use dependencies with your project. See [`c12` release notes](https://github.com/unjs/c12/releases/tag/v1.5.1) for full details. ::tip{icon="i-lucide-check-circle"} We've also added a test suite to cover these layer resolution changes. :: ### 😴 Nightly Release Channel Every commit to the `main` branch of Nuxt is automatically deployed to a new release, for easier testing before releases. We've renamed this from the 'edge release channel' to the 'nightly release channel' to avoid confusion with *edge deployments*. And probably also with Microsoft Edge (though I haven't heard that anyone was confused with that one!) - `nuxt3` is now `nuxt-nightly` - `nuxi-edge` is now `nuxi-nightly` - `@​nuxt/kit-edge` is now `@​nuxt/kit-nightly` - ... and so on. ::read-more{to="https://nuxt.com/docs/guide/going-further/nightly-release-channel#nightly-release-channel"} Read more about the **Nightly Release Channel**. :: ### ⚗️ Nitro v2.7 Nitro v2.7 has been released with lots of improvements and bug fixes. ::tip{icon="i-lucide-rocket"} 🔥 One of the most significant is that we now save **40% of bundle size in production** by using native `fetch` supported in Node 18+ ([#1724](https://github.com/unjs/nitro/pull/1724)). So if possible, we'd recommend you update your Node version to at least 18. :: ::read-more ```yaml [props] to: https://github.com/unjs/nitro/releases/tag/v2.7.0 icon: i-simple-icons-github color: gray target: _blank ``` Check out Nitro v2.7 release note. :: ### 💪 Type Import Changes ::warning This is likely to need code changes in your project. :: Vue requires that type imports be explicit (so that the Vue compiler can correctly optimise and resolve type imports for props and so on). See [core Vue `tsconfig.json`](https://github.com/vuejs/tsconfig/blob/main/tsconfig.json#L30-L33). We've therefore taken the decision to turn on `verbatimModuleSyntax` by default in Nuxt projects, which will throw a type error if types are imported without an explicit `type` import. To resolve it you will need to update your imports: ```diff - import { someFunction, SomeOptions } from 'some-library' + import { someFunction } from 'some-library' + import type { SomeOptions } from 'some-library' ``` You may also encounter modules in the Nuxt ecosystem that need to be updated; please open an issue for those modules. I'm also very happy to help if you're encountering any problems with this, if you're a module author. Just tag me and I'll take a look. If for whatever reason you need to undo this change in your project you can set the following configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ typescript: { tsConfig: { compilerOptions: { verbatimModuleSyntax: false } } } }) ``` However, we'd recommend only doing that temporarily, as Vue does need this option to be set for best results. ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi upgrade ``` ## Full Release Notes ::read-more{to="https://github.com/nuxt/nuxt/releases/tag/v3.8.0" icon="i-simple-icons-github" color="gray"} Read the full release notes of Nuxt `v3.8.0`. :: Thank you for reading this far! We hope you enjoy the new release. Please do let us know if you have any feedback or issues. **Happy Nuxting ✨** # Nuxt DevTools v1.0 > Nuxt DevTools v1.0 is out, generally available to all Nuxt projects! We are thrilled to announce the release of [Nuxt DevTools v1.0](https://github.com/nuxt/devtools)! 🎉 ::tip Since this release, Nuxt DevTools is now enabled with [Nuxt v3.8](https://nuxt.com/blog/v3-8) and onwards by default. Generally available to all Nuxt projects! :: You can start playing with it by upgrading to the latest Nuxt, and press `Shift + Option + D` (macOS) or `Shift + Alt + D` (Windows) in your browser to open the DevTools. By default the floating panel is hidden to reduce the distraction. You can enable it inside Nuxt DevTools settings page, or explicitly enabled it in your config: ```ts [nuxt.config.ts] export default defineNuxtConfig({ devtools: { enabled: true } }) ``` ## The Reasons We Built Nuxt DevTools Over the recent years, there has been an increasing focus on Developer Experience (DX). Tools and frameworks have been striving to improve the DX. Along the way, Nuxt introduced many innovative features and conventions to make your day-to-day development easier and more efficient. For example, [file-based routing](https://nuxt.com/docs/guide/directory-structure/pages), [layout system](https://nuxt.com/docs/guide/directory-structure/layouts), [plugins](https://nuxt.com/docs/guide/directory-structure/plugins), [route middleware](https://nuxt.com/docs/guide/directory-structure/middleware), [composables auto-import](https://nuxt.com/docs/guide/concepts/auto-imports), [file-based server APIs](https://nitro.unjs.io/guide/routing), [powerful module system](https://nuxt.com/modules) and many more. ![List of Nuxt features that enhance developer experience](https://nuxt.com/assets/blog/devtools/slide-dx.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} Nuxt is capable of creating various range of applications, from simple hobby projects to large scale applications, from simple client-rendered single-page applications to hybrid rendered sites with serverless functions on edge, etc. We provide those features and conventions to make it easier to support all those use cases and scenarios. ### The Problem Out of all these benefits of having a powerful framework, we have to made some trade-offs. Sometimes, we have to sacrifice a bit of the transparency of the underlying implementation to make things easier to use. !["Transparency" as the trade offs of having "Conventions", "Abstractions", "Sensible Defaults" and "Normalizations"](https://nuxt.com/assets/blog/devtools/slide-transparency.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} Conventional abstractions are great things to transfer implementation complexity and make things easier to get more focus when building. On the other hand, they can also add extra burden for users to learn and understand what's going on under the hood. Leading also to implicitness, like where a auto-imported component is from, or how many modules is using a certain component, etc. It can also make things hard to debug. Trade-offs are inevitable. Generally we believe those trade-offs are worth it, as they would help organizing users' codebase and make it easier to maintain in the long run. In the meantime, we also want to compensate the transparency we lost by providing a tool to help you understand what's going on under the hood and make the learning curve smoother. That's where Nuxt DevTools comes in! We [first introduced it](https://nuxt.com/blog/introducing-nuxt-devtools) in February 2023 to experiment with the idea. After a few months of exploration and development, from surprisingly positive feedbacks from the community, this idea has been proven to be useful and we decided to make it a core part of your Nuxt development experience. ## Features [Nuxt DevTools](https://github.com/nuxt/devtools) is a set of visual tools to help you understand your Nuxt app and improve the developer experience even further. It's created to provide better transparency between Nuxt and your app, find performance bottlenecks and help you manage your app and configuration. ### In App DevTools From the overview, Nuxt DevTools is an in-app DevTools that lives alongside your app. It will show up as a floating panel that you can click to open. ::article-video{cloudinary="v1700132388/devtools/0-intro_ilgwel"} :: We believe this is a better approach than the traditional browser extension DevTools, as it's: - **Works across all browsers**, and even on mobile devices! - The capability of browser extension DevTools are limited by the APIs each browsers provides, and also maintain multiple extensions would require a lot of effort. This approach would allow us to focus more on the functionality and features, while having it accessible to users on any browsers and devices. - **Build tools integrations** - Traditionally browser extension DevTools are only able to access the runtime context of your app and have no access to the build tools. Having the DevTools comes with Nuxt, allows us to communicate with the build tools and provide much more insights and features. - **Avoid layout shifts** - Having the DevTools as a floating panel would avoid the layout shifts when toggling the DevTools. ### Pages View To help improving the implicitness of file-based routing, we introduced the Pages View in DevTools. It lists all the pages that have been registered in your app, that you can easily test and navigate between them. ::article-video{cloudinary="v1700132393/devtools/1-pages_kkbecx"} :: ### Components View The Components tab shows all the components you are using in your app and where they are from. You can also search for them and go to the source code. It also provides a graph view that show the relationship between components. You can filter the components to see the dependencies of a specific component. This could help to identify unintended dependencies and improve the performance and bundle size of pages. ::article-video{cloudinary="v1700132398/devtools/2-components_paj0uv"} :: ### Composables View Composables view shows all the auto-imported composables registered to Nuxt. You can see which files are importing them, and where they are from. Some entries can also provide short descriptions and documentation links. ::article-video{cloudinary="v1700132395/devtools/3-imports_qhahdf"} :: ### Modules Management The Modules tab shows all the modules that are registered in your app, with the links to their documentations and repositories. We also provide the ability for you to search for and explore the modules from the community. And install them with a single click! ::article-video{cloudinary="v1700132389/devtools/4-modules_v5ha5u"} :: ### Static Assets Management The Assets tab shows all the static assets under your `public` directory. It supports previewing images, videos, fonts, PDFs, and other files, that you can easily copy the URL or code snippet to use in your app. You may also drag and drop files to upload them directly from Nuxt DevTools. ::article-video{cloudinary="v1700132394/devtools/5-assets_mpzyrs"} :: ### Runtime Configs Editor The Runtime Configs tab shows the runtime configs of your app and provides an interactive editor for you to play with different values. ::article-video{cloudinary="v1700132393/devtools/6-runtime-configs_fzlrik"} :: ### Payload Editor Similar to the Runtime Configs Editor, the Payload Editor allows you to edit the payload from composables like [`useState`](https://nuxt.com/docs/api/composables/use-state) and [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch) to see what have been passed from server to client on server-side rendering. ::article-video{cloudinary="v1700132389/devtools/7-payload_nfzobp"} :: ### Open Graph Preview [Open Graph](https://ogp.me/) plays an important role in social media sharing as well as [SEO](https://en.wikipedia.org/wiki/Search_engine_optimization). In the traditional workflow, we usually need to first deploy our app to check if the Open Graph is working as expected on various social media platforms. With the Open Graph Preview, you can now preview the Open Graph in DevTools and update it live with an instant feedback loop. We also help you check the Open Graph tags in your app and provide suggestions to improve them. You can copy the generated code snippet and paste it to your routes to fix them in one go. ::article-video{cloudinary="v1700132390/devtools/8-open-graph_hjawen"} :: ### Plugins Overview Plugins Overview list all the [plugins](https://nuxt.com/docs/guide/directory-structure/plugins) registered in your app. As the plugins are executed in the runtime before your app renders, it's important to keep plugins performant and avoid blocking the rendering. We provide the execution time of each plugin and the total time of all plugins, so you can better identify the potential performance bottlenecks. ::article-video{cloudinary="v1700132390/devtools/9-plugins_bhcobr"} :: ### Timeline Timeline is a tool for you to check when and how each composable has been called. Different from browser DevTools' performance tools, this tab only checks the high-level composables combining with other events like route navigation, which is closer to day-to-day use. It also records the arguments and return values of each call, so you can better understand what's going on under the hood. ::warning As of November 2023, the Timeline is still an experimental feature that requires manually opt-in. :: ::article-video{cloudinary="v1700132392/devtools/10-timeline_zeei5s"} :: ### Production Build Analyzer While Nuxt DevTools is mostly focused on providing development tools, sometimes we might want to know how chunks are composed in production. The Build Analyzer allows you to fire up a production build and analyze the chunks and modules at any time and see how they are bundled. You can also do multiple builds on different branches to compare how your refactoring/changes affect the bundle size, etc. ::article-video{cloudinary="v1700132394/devtools/11-build-analyze_f3wx6q"} :: ### Server API Playground Nuxt provides a very convenient way to create server API functions with the [server routes](https://nuxt.com/docs/guide/directory-structure/server#server-routes). Since in Nuxt we have that information, we are able to provide a playground for you to test and debug your server API functions, similar to tools like Postman. We list all the server APIs available in your app automatically. And we execute those functions **within the same context of your app**, so you don't need to manually set them up in external tools. As always, we also have code snippets for you to copy and paste into your app. ::article-video{cloudinary="v1700132388/devtools/12-server-api_owjyjg"} :: ### Embedded Full-feature VS Code Thanks to the flexibility of our DevTools approach, we can leverage the power of the modern web to embed a full-featured VS Code right in the DevTools. In that VS Code, you can sign in to your account and synchronize your settings, and all the extensions just work as your normal VS Code client. This allows you to quickly edit your code without leaving your browser. ::article-video{cloudinary="v1700132395/devtools/13-vscode_je5x0m"} :: ### Component Inspector The Inspector allows you to inspect the DOM tree and see which component is rendering it. Click to go to your editor for the specific line. Making it much easier to make changes without the requirement of understanding the project structure thoroughly. ::article-video{cloudinary="v1700132391/devtools/0-inspector_fuxmr7"} :: ### Split Screens In the recent releases, we introduced a split screen feature, that allows you to open two tabs side-by-side. ::article-video{cloudinary="v1700132391/devtools/0-split-view_mdeiie"} :: ### UI Customization In DevTools setting, we provide a few options for you to customize the tabs you want to see, and the layout of the DevTools. ::article-video{cloudinary="v1700132391/devtools/0-settings_weflmu"} :: ## Ecosystem In Nuxt, we value the ecosystem a lot. Similar to how Nuxt modules enhance Nuxt core, we also designed Nuxt DevTools to be highly extensible, allowing modules to provide additional features and integrations. ### Community Modules We are proud to see the community has already started to build modules for Nuxt DevTools. Here are some of them: [Vitest module](https://nuxt.com/modules/vitest) provides Vitest UI for tests running with the same pipeline as your Nuxt app. Made it easier to debug your tests alongside your app. ::article-video{cloudinary="v1700132393/devtools/99-vitest_wwikpc"} :: [VueUse module](https://nuxt.com/modules/vueuse) provides a search page for available composables and see their documentation. ::article-video{cloudinary="v1700132390/devtools/99-vueuse_simsfj"} :: [SiteMap module](https://nuxt.com/modules/sitemap) provides an interactive editor for you to manage your sitemap. ::article-video{cloudinary="v1700132390/devtools/99-sitemap_xpsfek"} :: [TailwindCSS module](https://nuxt.com/modules/tailwindcss) provides the Tailwind Config Viewer for you to check what's available based on your config. ::article-video{cloudinary="v1700132388/devtools/99-tailwind_dgiodc"} :: [UnoCSS module](https://nuxt.com/modules/unocss) provides an interactive inspector to see how each module contributes to the final CSS. ::article-video{cloudinary="v1700132394/devtools/99-unocss_xvii5x"} :: [Storybook module](https://nuxt.com/modules/storybook) provides a Storybook UI for your components. ::article-video{cloudinary="v1700132388/devtools/99-storybook_ifxt4r"} :: And they are just a few of them! We are looking forward to see more modules coming to Nuxt DevTools! ### Projects Inspired by Nuxt DevTools In the meantime, we are also flattered that other frameworks are starting to build their own DevTools inspired by Nuxt DevTools: - [`webfansplz/vite-plugin-vue-devtools`](https://github.com/webfansplz/vite-plugin-vue-devtools) - A Nuxt DevTools port to support DevTools for Vite + Vue 3 apps. - [`pheno-agency/vite-plugin-devtools`](https://github.com/pheno-agency/vite-plugin-devtools) - An experiment on building framework-agnostic DevTools for Vite. - [Modern.js DevTools](https://github.com/Asuka109/modern.js/tree/dev/modernjs-devtools/packages/devtools/plugin) - In App DevTools for Modern.js - [Qwik DevTools](https://github.com/QwikDev/devtools) - DevTools for Qwik We are working closely with the maintainers of those projects to see how we can bring the experience of DevTools to the next level. ## What's Next Nuxt DevTools just reached v1.0, but t doesn't mean we are done. There are still a lot of things we want to explore and improve. Here are some of the ideas we are considering: - Nuxt Accessibility Integration - We are building an a11y integration for Nuxt ([#23255](https://github.com/nuxt/nuxt/issues/23255)). We'll build a dedicated view in Nuxt DevTools for you to check the accessibility hints interactively. - Vue DevTools Integration - We are working with the Vue team to bring the Vue DevTools experience to a shared tool that works for both browser extensions and in-app DevTools like `vite-plugin-vue-devtools` and Nuxt DevTools. - [Let us know your ideas/suggestions!](https://github.com/nuxt/devtools/discussions/29) ## The Future We are excited to see how Nuxt DevTools can help you build better apps and improve your developer experience. Going forward, we are imagining something bigger than Nuxt DevTools itself. We believe that having such framework-specific DevTools is the way onwards to provide even better developer experience. We also see there are many parts of such tools can actually be shared and reused across tools. We came up with the idea of the DevTools Kit. ### DevTools Kit DevTools Kit is an idea of the universal protocol that is still in the early brainstorming phase. We imagine that in the best world, each feature of the DevTools should be **composable, extensible, and collaborative**. Meta-frameworks could build their own features for their specific needs, while the common web-related tools could be shared and collaborated on between different frameworks. ![DevTools Kit](https://nuxt.com/assets/blog/devtools/slide-devtools-kit.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} Imagine we could have all these features, each as a standalone package. We could have general web-related tools like SEO, Accessibility, PWA, Static Assets, etc. Then low-level build tools related, like Vite build analyzer, Vite Inspector, or Webpack visualizer, etc. And finally, we could have framework and meta-framework specific tools like Vue Components view, or Nuxt Server API Playground, etc. ![Nuxt DevTools](https://nuxt.com/assets/blog/devtools/slide-nuxt-devtools.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} ![Nuxt DevTools](https://nuxt.com/assets/blog/devtools/slide-vue-devtools.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} At that time, Vue DevTools would be a composition of common web features and Vue-specific features. And Nuxt DevTools would essentially be a composition of the features above, inherit all features from Vue DevTools, and add Nuxt specific features on top of it. ![Your DevTools](https://nuxt.com/assets/blog/devtools/slide-your-devtools.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} It would even be possible to compose your own DevTools as you like. That said, we are still thinking about and discussing the details of the DevTools Kit. Stay tuned for more updates! ## Conclusion We hope you enjoy the new Nuxt DevTools experience! We are looking forward to seeing how it can help you build better apps and improve your developer experience. If you have any ideas or suggestions, feel free to let us know in the [Ideas & Suggestions](https://github.com/nuxt/devtools/discussions/29) discussion. Thank you for your support and happy hacking! 🚀 # Nuxt 3.9 > Nuxt 3.9 is out - a Christmas gift from the Nuxt team bringing Vite 5, interactive server components, new composables, a new loading API and more. A very merry Christmas to you and yours from all Nuxters involved in this release! 🎁🎄 We have lots of features packed into v3.9 and can't wait for you to try them out. ### ⚡️ Vite 5 This release comes with Vite 5 and Rollup 4 support. Module authors may need to check to ensure that any vite plugins you're creating are compatible with these latest releases. This comes with a whole host of great improvements and bug fixes - check out [the Vite changelog](https://vitejs.dev/guide/migration.html#migration-from-v4) for more info. ### ✨ Vue 3.4 ready This release is tested with the latest Vue 3.4 release candidate, and has the necessary configuration to take advantage of [new features in Vue 3.4](https://blog.vuejs.org/posts/vue-3-4), including debugging hydration errors in production (just set `debug: true`) in your Nuxt config. 👉 To take advantage, just update your `vue` version once v3.4 is released, or try out the release candidate today: ```json [package.json] { "dependencies": { "nuxt": "3.9.0", "vue": "3.4.0-rc.1", "vue-router": "latest" } } ``` ### 🏝️ Interactive Server Components This is a highly-experimental update, but it's now possible to play around with interactive components within Nuxt server components. You'll need to enable this new feature additionally to component islands: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { componentIslands: { selectiveClient: true } } }) ``` Now, within a server component, you can specify components to hydrate by using the `nuxt-client` directive: ```vue [components/SomeComponent.server.vue] <NuxtLink :to="/" nuxt-client /> ``` We're pretty excited about this one - so do let us know how you're using it! 🙏 ### 🔥 Automatic Server Optimisations We now use Vite's new AST-aware 'define' to perform more accurate replacements on server-side code, meaning code like this will no longer throw an error: ```vue [app.vue] <script setup lang="ts"> if (document) { console.log(document.querySelector('div')) } </script> ``` This hasn't been possible until now because we haven't wanted to run the risk of accidentally replacing normal words like `document` within non-JS parts of your apps. But Vite's new `define` functionality is powered by `esbuild` and is syntax-aware, so we feel confident in enabling this functionality. Nevertheless, you can opt out if you need to: ```ts [nuxt.config.ts] export default defineNuxtConfig({ hooks: { 'vite:extendConfig' (config) { delete config.define!.document } } }) ``` ### 🚦 Granular Loading API We now have a new hook-based system for [`<NuxtLoadingIndicator>`](https://nuxt.com/docs/api/components/nuxt-loading-indicator), including a `useLoadingIndicator` composable that lets you control/stop/start the loading state. You can also hook into `page:loading:start` and `page:loading:end` if you prefer. ::tip You can read more [in the docs](https://nuxt.com/docs/api/composables/use-loading-indicator) and in the original PR ([#24010](https://github.com/nuxt/nuxt/pull/24010)). :: ### 🏁 Run single events in `callOnce` Sometimes you only want to run code once, no matter how many times you load a page - and you don't want to run it again on the client if it ran on the server. For this, we have a new utility: [`callOnce`](https://nuxt.com/docs/api/utils/call-once) ([#24787](https://github.com/nuxt/nuxt/pull/24787)). ```vue [app.vue] <script setup> const websiteConfig = useState('config') await callOnce(async () => { console.log('This will only be logged once') websiteConfig.value = await $fetch('https://my-cms.com/api/website-config') }) </script> ``` Note that this utility is context-aware so it *must* be called in component setup function or Nuxt plugin, as with other Nuxt composables. ::read-more{to="https://nuxt.com/docs/api/utils/call-once"} :: ### 🚨 Error Types For a while now, errors returned by `useAsyncData` and `useFetch` have been typed pretty generically as `Error`. We've significantly improved the type possibilities for them to make them more accurate in terms of what you'll actually receive. (We normalise errors with the `h3` `createError` utility under the hood, so they can be serialised from server to client, for example.) We've tried to implement the type change in a backwards compatible way, but you might notice that you need to update the generic if you're manually configuring the generics for these composables. See ([#24396](https://github.com/nuxt/nuxt/pull/24396)) for more information, and do let us know if you experience any issues. ### 🔥 Schema Performance We've taken some time in this release to make some minor performance improvements, so you should notice some things are a bit faster. This is an ongoing project and we have ideas for improving initial load time of the Nuxt dev server. ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi upgrade ``` ## Full Release Notes ::read-more{to="https://github.com/nuxt/nuxt/releases/tag/v3.9.0" icon="i-simple-icons-github"} Read the full release notes of Nuxt `v3.9.0`. :: Thank you for reading this far! We hope you enjoy the new release. Please do let us know if you have any feedback or issues. **Happy Nuxting ✨** # Nuxt 3.10 > Nuxt 3.10 is out - packed with features and fixes. Here are a few highlights. v3.10 comes quite close on the heels of v3.9, but it's packed with features and fixes. Here are a few highlights. ### ✨ Experimental shared `asyncData` when prerendering When prerendering routes, we can end up refetching the same data over and over again. In Nuxt 2 it was possible to create a 'payload' which could be fetched once and then accessed in every page (and this is of course possible to do manually in Nuxt 3 - see [this article](https://roe.dev/blog/shared-data-nuxt-generate)). With [#24894](https://github.com/nuxt/nuxt/pull/24894), we are now able to do this automatically for you when prerendering your site. Your [`useAsyncData`](https://nuxt.com/docs/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/api/composables/use-fetch) calls will be deduplicated and cached between renders of your site. ```ts [nuxt.config.ts] export defineNuxtConfig({ experimental: { sharedPrerenderData: true } }) ``` ::important It is particularly important to make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` should do this automatically.) :: ::read-more{to="https://nuxt.com/docs/guide/going-further/experimental-features#sharedprerenderdata"} :: ### 🆔 SSR-safe accessible unique ID creation We now ship a `useId` composable for generating SSR-safe unique IDs ([#23368](https://github.com/nuxt/nuxt/pull/23368)). This allows creating more accessible interfaces in your app. For example: ```vue [components/MyForm.vue] <script setup> const emailId = useId() const passwordId = useId() </script> <template> <form> <label :for="emailId">Email</label> <input :id="emailId" name="email" type="email" > <label :for="passwordId">Password</label> <input :id="passwordId" name="password" type="password" > </form> </template> ``` ### ✍️ Extending `app/router.options` It's now possible for module authors to inject their own `router.options` files ([#24922](https://github.com/nuxt/nuxt/pull/24922)). The new `pages:routerOptions` hook allows module authors to do things like add custom `scrollBehavior` or add runtime augmenting of routes. ::read-more{to="https://nuxt.com/docs/guide/going-further/custom-routing#router-options"} :: ### :icon{name="i-vscode-icons-file-type-node"} Client-side Node.js support We now support (experimentally) polyfilling key Node.js built-ins ([#25028](https://github.com/nuxt/nuxt/pull/25028)), just as we already do via Nitro on the server when deploying to non-Node environments. That means that, within your client-side code, you can import directly from Node built-ins (`node:` and node imports are supported). However, nothing is globally injected for you, to avoid increasing your bundle size unnecessarily. You can either import them where needed. ```ts [some-file.ts] import { Buffer } from 'node:buffer' import process from 'node:process' ``` Or provide your own polyfill, for example, inside a Nuxt plugin. ```ts [plugins/node.client.ts] import { Buffer } from 'node:buffer' import process from 'node:process' globalThis.Buffer = Buffer globalThis.process = process export default defineNuxtPlugin({}) ``` This should make life easier for users who are working with libraries without proper browser support. However, because of the risk in increasing your bundle unnecessarily, we would strongly urge users **to choose other alternatives** if at all possible. ### 🍪 Better cookie reactivity We now allow you to opt-in to using the [CookieStore](https://developer.mozilla.org/en-US/docs/Web/API/CookieStore). If browser support is present, this will then be used instead of a BroadcastChannel to update `useCookie` values reactively when the cookies are updated ([#25198](https://github.com/nuxt/nuxt/pull/25198)). This also comes paired with a new composable, `refreshCookie` which allows manually refreshing cookie values, such as after performing a request. ::read-more{to="https://nuxt.com/docs/api/utils/refresh-cookie"} :: ### 🏥 Detecting anti-patterns In this release, we've also shipped a range of features to detect potential bugs and performance problems. - We now will throw an error if `setInterval` is used on server ([#25259](https://github.com/nuxt/nuxt/pull/25259)). - We warn (in development only) if data fetch composables are used wrongly ([#25071](https://github.com/nuxt/nuxt/pull/25071)), such as outside of a plugin or setup context. - We warn (in development only) if you are not using `<NuxtPage />` but have the `vue-router` integration enabled ([#25490](https://github.com/nuxt/nuxt/pull/25490)). (`<RouterView />` should not be used on its own.) ### 🧂 Granular view transitions support It's now possible to control view transitions support on a per-page basis, using `definePageMeta` ([#25264](https://github.com/nuxt/nuxt/pull/25264)). You need to have experimental view transitions support enabled first: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { viewTransition: true }, app: { // you can disable them globally if necessary (they are enabled by default) viewTransition: false } }) ``` And you can opt in/out granularly: ```vue [pages/index.vue] <script setup lang="ts"> definePageMeta({ viewTransition: false }) </script> ``` Finally, Nuxt will not apply View Transitions if the user's browser matches `prefers-reduced-motion: reduce` ([#22292](https://github.com/nuxt/nuxt/pull/22292)). You can set `viewTransition: 'always'`; it will then be up to you to respect the user's preference. ### 🏗️ Build-time route metadata It's now possible to access routing metadata defined in `definePageMeta` at build-time, allowing modules and hooks to modify and change these values ([#25210](https://github.com/nuxt/nuxt/pull/25210)). ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { scanPageMeta: true } }) ``` Please, experiment with this and let us know how it works for you. We hope to improve performance and enable this by default in a future release so modules like `@nuxtjs/i18n` and others can provide a deeper integration with routing options set in `definePageMeta`. ### 📦 Bundler module resolution With [#24837](https://github.com/nuxt/nuxt/pull/24837), we are now opting in to the TypeScript `bundler` resolution which should more closely resemble the actual way that we resolve subpath imports for modules in Nuxt projects. 'Bundler' module resolution is [recommended by Vue](https://github.com/vuejs/tsconfig/blob/mainz/tsconfig.json#L24-L26) and [by Vite](https://vitejs.dev/guide/performance.html#reduce-resolve-operations), but unfortunately there are still many packages that do not have the correct entries in their `package.json`. As part of this, we opened 85 PRs across the ecosystem to test switching the default, and identified and fixed some issues. If you need to switch off this behaviour, you can do so. However, please consider raising an issue (feel free to tag me in it) in the library or module's repo so it can be resolved at source. ```ts [nuxt.config.ts] export default defineNuxtConfig({ future: { typescriptBundlerResolution: false } }) ``` ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## Full Release Notes ::read-more{to="https://github.com/nuxt/nuxt/releases/tag/v3.10.0" icon="i-simple-icons-github" target="_blank"} Read the full release notes of Nuxt `v3.10.0`. :: Thank you for reading this far! We hope you enjoy the new release. Please do let us know if you have any feedback or issues. **Happy Nuxting ✨** # The Evolution of Shiki v1.0 > Shiki v1.0 came with many improvements and features - see how Nuxt drives the evolution of Shiki! [Shiki](https://github.com/shikijs/shiki) is a syntax highlighter that uses [TextMate grammars and themes](https://code.visualstudio.com/api/language-extensions/syntax-highlight-guide#textmate-grammars), the same engine that powers VS Code. It provides one of the most accurate and beautiful syntax highlighting for your code snippets. It was created by [Pine Wu](https://github.com/octref) back in 2018, when he was part of the VS Code team. It started as an experiment to use [Oniguruma](https://github.com/microsoft/vscode-oniguruma) to do syntax highlighting. Different from existing syntax highlighters like [Prism](https://prismjs.com/) and [Highlight.js](https://highlightjs.org/) that designed to run in the browser, Shiki took a different approach by **highlighting ahead of time**. It ships the highlighted HTML to the client, producing accurate and beautiful syntax highlighting with **zero JavaScript**. It soon took off and became a very popular choice, especially for static site generators and documentation sites. ::collapsible{name="Shiki Example"} For example, with the code snippet below: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/content', ], }) ``` Shiki will generate the following HTML: ```html <pre class="shiki material-theme-palenight" style="background-color:#292D3E;color:#babed8" tabindex="0"> <code> <span class="line"><span style="color:#89DDFF;font-style:italic">export</span><span style="color:#89DDFF;font-style:italic"> default</span><span style="color:#82AAFF"> defineNuxtConfig</span><span style="color:#BABED8">(</span><span style="color:#89DDFF">{</span></span> <span class="line"><span style="color:#F07178"> modules</span><span style="color:#89DDFF">:</span><span style="color:#BABED8"> [</span></span> <span class="line"><span style="color:#89DDFF"> '</span><span style="color:#C3E88D">@nuxt/content</span><span style="color:#89DDFF">'</span><span style="color:#89DDFF">,</span></span> <span class="line"><span style="color:#BABED8"> ]</span><span style="color:#89DDFF">,</span></span> <span class="line"><span style="color:#89DDFF">}</span><span style="color:#BABED8">)</span></span> </code> </pre> ``` It might look a bit overwhelming if you read it, but **this piece of HTML works everywhere without any JavaScript or CSS**. TextMate grammars has a very rich representation of the types of every token (TextMate scopes). Since Shiki flattens all the tokens into styled spans, it achieves accurate results that most traditional CSS-based highlighters have difficulties achieving. :: While Shiki is awesome, it's still a library that is designed to run on Node.js. This means it is limited to highlighting static code only and would have trouble with dynamic code, because Shiki doesn't work in the browser. In addition, Shiki relies on the WASM binary of Oniguruma, as well as a bunch of heavy grammar and theme files in JSON. It uses Node.js filesystem and path resolution to load these files, which is not accessible in the browser. To improve that situation, I [started this RFC](https://github.com/shikijs/shiki/issues/91) that later landed with [this PR](https://github.com/shikijs/shiki/pull/109) and shipped in Shiki v0.9. While it abstracted the file loading layer to use fetch or filesystem based on the environment, it's still quite complicated to use as you need to serve the grammars and theme files somewhere in your bundle or CDN manually, then call the `setCDN` method to tell Shiki where to load these files. The solution is not perfect but at least it made it possible to run Shiki in the browser to highlight dynamic content. We have been using that approach since then - until the story of this article began. ## The Start Nuxt is putting a lot effort in pushing the [web to the edge](https://nuxt.com/blog/nuxt-on-the-edge), making the web more accessible with lower latency and better performance. Like CDN servers, edge hosting services such as [CloudFlare Workers](https://workers.cloudflare.com/) are deployed all over the world. Users get the content from the nearest edge server without the round trips to the origin server which could be thousands of miles away. With the awesome benefits it provides, it also comes with some trade-offs. For example, edge servers use a restricted runtime environment. CloudFlare Workers also does not support file system access and usually don't preserve the state between requests. While Shiki's main overhead is loading the grammars and themes upfront, that wouldn't work well in the edge environment. It all started with a chat between [Sébastien](https://x.com/Atinux) and me. We were trying to make [Nuxt Content](https://github.com/nuxt/content) which uses Shiki to highlight the code blocks, to work on the edge. ![Chat History Between Sébastien and Anthony](https://nuxt.com/assets/blog/shiki-start-chat.png){.rounded-lg.shadow.max-w-[700px].border.dark:border-gray-700} I started the experiments by patching [`shiki-es`](https://github.com/pi0/shiki-es) (a ESM build of Shiki by [Pooya Parsa](https://github.com/pi0)) locally, to convert the grammars and themes files into [ECMAScript Module (ESM)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules) so that it could be understood and bundled by the build tools. This was done to create the code bundle for CloudFlare Workers to consume without using the filesystem nor making network requests. ```ts [Before - Read JSON assets from filesystem] import fs from 'fs/promises' const cssGrammar = JSON.parse(await fs.readFile('../langs/css.json', 'utf-8')) ``` ```ts [After - Using ESM import] const cssGrammar = await import('../langs/css.mjs').then(m => m.default) ``` We need to wrap the JSON files into ESM as inline literal so that we can use [`import()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import) to dynamically import them. The difference is that `import()` is a standard JavaScript feature that works everywhere, while `fs.readFile` is a Node.js specific API that only works in Node.js. Having `import()` statically would also make bundlers like [Rollup](https://rollupjs.org/) and [webpack](https://webpack.js.org/) able to construct the module relationship graph and [emit the bundled code as chunks](https://rollupjs.org/tutorial/#code-splitting). Then, I realized that it actually takes more than that to make it work on edge runtimes. Since bundlers expect imports to be resolvable at build time (meaning that in order to support all the languages and themes), we need to list all the import statements in every single grammar and theme file in the codebase. This would end up with a huge bundle size with a bunch of grammars and themes that you might not actually use. This problem is particularly important in the edge environment, where the bundle size is critical for performance. So, we needed to figure out a better middle ground to make it work better. ## The Fork - Shikiji Knowing this might fundamentally change the way Shiki works, and since we don't want to risk breaking the existing Shiki users with our experiments, I started a fork of Shiki called [Shikiji](https://github.com/antfu/shikiji). I rewrote the code from scratch while keeping the previous API design decisions in mind. The goal is to make Shiki runtime-agnostic, performant and efficient, like the philosophy we have at [UnJS](https://github.com/unjs). To make that happen, we need to make Shikiji completely ESM-friendly, pure and [tree-shakable](https://developer.mozilla.org/en-US/docs/Glossary/Tree_shaking). This goes all the way up to the dependencies of Shiki such as [`vscode-oniguruma`](https://github.com/microsoft/vscode-oniguruma) and [`vscode-textmate`](https://github.com/microsoft/vscode-textmate), which are provided in [Common JS (CJS)](https://requirejs.org/docs/commonjs.html) format. `vscode-oniguruma` also contains a WASM binding generated by [`emscripten`](https://github.com/emscripten-core/emscripten) that contains [dangling promises](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-floating-promises.md) that will make CloudFlare Workers fail to finish the request. We ended up by embedding the WASM binary into a [base64 string](https://en.wikipedia.org/wiki/Base64) and shipping it as an ES module, manually rewriting the WASM binding to avoid dangling promises, and [vendored `vscode-textmate`](https://github.com/shikijs/shiki/blob/main/CONTRIBUTING.md#clone) to compile from its source code and produce the efficient ESM output. The end result was very promising. We managed to get Shikiji working on any runtime environment, with even the possibility to [import it from CDN and run in the browser](https://shiki.style/guide/install#cdn-usage) with a single line of code. We also took the chance to improve the API and the internal architecture of Shiki. We switched from simple string concatenation to use [`hast`](https://github.com/syntax-tree/hast), creating an Abstract Syntax Tree (AST) for generating the HTML output. This opens up the possibility of exposing a [Transformers API](https://shiki.style/guide/transformers) to allow users to modify the intermediate HAST and do many cool integrations that would be very hard to achieve previously. Dark/Light mode support [was a frequently requested feature](https://github.com/shikijs/shiki/issues/33). Because of the static approach Shiki takes, it won't be possible to change the theme on the fly at rendering. The solution in the past was to generate the highlighted HTML twice, and toggle their visibility based on the user's preference - it wasn't efficient as it duplicate the payload, or used [CSS variables theme](https://github.com/shikijs/shiki/pull/212) which lost the granular highlighting Shiki is great for. With the new architecture that Shikiji has, I took a step back and rethought the problem, and [came up with the idea](https://github.com/shikijs/shiki/issues/33#issuecomment-1676362336) of breaking down the common tokens and merge multiple themes as inlined CSS variables, which provide efficient output while aligning with the Shiki's philosophy. You can learn more about it in [Shiki's documentation](https://shiki.style/guide/dual-themes). To make the migration easier, we also created the [`shikiji-compat` compatibility layer](https://shikiji.netlify.app/guide/compat), which uses Shikiji's new foundation and provides backward compatibility API. To get Shikiji to work on Cloudflare Workers, we had one last challenge as they don't support [initiating WASM instance](https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScript_interface/instantiate_static)s from inlined binary data. Instead it requires importing the static `.wasm` assets for security reasons. This means that our "All-in-ESM" approach does not work well on CloudFlare. This would require extra work for users to provide different WASM sources, which makes the experience more difficult than we intended. At this moment, [Pooya Parsa](https://github.com/pi0) stepped in and made the universal layer [`unjs/unwasm`](https://github.com/unjs/unwasm), which supports the upcoming [WebAssembly/ES Module Integration](https://github.com/WebAssembly/esm-integration/tree/main/proposals/esm-integration) proposal. It has been integrated into [Nitro to have automated WASM targets](https://github.com/unjs/nitro/pull/2037). We hope that `unwasm` will help developers to have a better experience when working with WASM. Overall, the Shikiji rewrite works well. [Nuxt Content](https://github.com/nuxt/content), [VitePress](https://vitepress.dev/) and [Astro](https://astro.build/) have been migrated to it. The feedback we have received has also been very positive. ## Merging Back I am a team member of Shiki and have helped to do releases from time to time. While [Pine](https://github.com/octref) is the lead of Shiki, he was busy on other stuff and Shiki's iterations slowed down. During the experiments in Shikiji, I [proposed a few improvements](https://github.com/shikijs/shiki/issues/510) that could help Shiki acquire a modern structure. While generally everyone agreed with that direction, there would have been quite a lot of work to do and no one started to work on that. While we were happy to use Shikiji to solve the problems we had, we certainly didn't want to see the community split by two different versions of Shiki. After a call with Pine, we made the consensus to merge the two projects into one: ::read-more{color="purple" icon="i-octicon-git-merge-24" to="https://github.com/shikijs/shiki/pull/557"} feat!: merge Shikiji back into Shiki for v1.0 [#557]{.opacity-50} :: We are really happy to see that our work in Shikiji has been merged back to Shiki, that not only works for ourselves, but also benefits the entire community. With this merge, it **solves around 95% of the open issues** we have had in Shiki for years: ![Shikiji Merged Back to Shiki](https://nuxt.com/assets/blog/shiki-merge-pr.png){.rounded-lg.shadow} Shiki now also got [a brand new documentation site](https://shiki.style/) where you can also play it right in your browser (thanks to the agnostic approach!). Many frameworks now has built-in integration with Shiki, maybe you are already using it somewhere! ## Twoslash [Twoslash](https://github.com/twoslashes/twoslash) is an integration tool to retrieve type information from [TypeScript Language Services](https://github.com/microsoft/TypeScript/wiki/Using-the-Language-Service-API) and generated to your code snippet. It essentially make your static code snippet to have hover type information similar to your VS Code editor. It's made by [Orta Therox](https://github.com/orta) for the [TypeScript documentation site](https://github.com/microsoft/TypeScript-Website), there you can find [the original source code here](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ts-twoslasher). Orta also created the [Twoslash integration for Shiki v0.x versions](https://github.com/shikijs/twoslash). Back then, Shiki [did not have proper plugin system](https://github.com/shikijs/shiki/issues/380), that makes the `shiki-twoslash` had to be built as a wrapper over Shiki, make it a bit hard to set up as the existing Shiki integrations won't directly work with Twoslash. We also took the chance to revise the Twoslash integrations when we were rewriting Shikiji, also a way to [dog-fooding](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) and verify the extensibility. With the new HAST internal, we are able to [integrate Twoslash as a transformer plugin](https://shiki.style/packages/twoslash), making it works everywhere that Shiki works and also in a composable way to be used with other transformers. With this, we started to think that we could probably get Twoslash to work on [nuxt.com](https://nuxt.com/), the website you are looking at. [nuxt.com](http://nuxt.com) uses [Nuxt Content](https://github.com/nuxt/content) under the hood, and different from other documentation tools like VitePress, one of the benefits Nuxt Content provides is that it's able to handle dynamic content and runs on the edge. Since Twoslash is relying on TypeScript as well as the giant types modules graph from your dependencies, that would be not ideal to ship all those things to the edge or browser. Sounds tricky, but challenge accepted! We first come up of fetching the types on-demand from CDN, using the [Auto-Type-Acquisition](https://github.com/microsoft/TypeScript-Website/tree/v2/packages/ata) technique that you will see on the [TypeScript playground](https://www.typescriptlang.org/play). We made the [`twoslash-cdn`](https://github.com/antfu/twoslash-cdn) that allows Twoslash to run in any runtime. However, still, it sounds like not the most optimal solution, as it would still require to make many network requests that might defeat the purpose of running on the edge. After a few iterations on the underlying tools (e.g. on [`@nuxtjs/mdc`](https://github.com/nuxt-modules/mdc/pull/129), the markdown compiler used by Nuxt Content), we managed to take the hybrid approach and made [`nuxt-content-twoslash`](https://github.com/antfu/nuxt-content-twoslash) that runs Twoslash on build time and caches the results for edge rendering. This way we could avoid shipping any extra dependencies to the final bundle, but still have the rich interactive code snippets on the website: ```vue twoslash <script setup> // Try hover on identifiers below to see the types const count = useState('counter', () => 0) const double = computed(() => count.value * 2) </script> <template> <button>Count is: {{ count }}</button> <div>Double is: {{ double }}</div> </template> ``` During that, we also took the chance to refactor [Twoslash](https://github.com/twoslashes/twoslash) with Orta to have a more efficient and modern structure. It also allows us have [`twoslash-vue`](https://github.com/twoslashes/twoslash/tree/main/packages/twoslash-vue) that provides the [Vue SFC](https://vuejs.org/guide/scaling-up/sfc.html) support as you are playing above. It's powered by [Volar.js](https://github.com/volarjs/volar.js) and [`vuejs/language-tools`](https://github.com/vuejs/language-tools). With Volar growing to be framework agnostic and frameworks to work together, we are looking forward to see such integrations to expand to more syntaxes like Astro and Svelte components files in the future. ## Integrations If you want to give Shiki a try in your own website, here you can find some integrations that we have made: - [Nuxt](https://shiki.style/packages/nuxt) - If using [Nuxt Content](https://content.nuxt.com/), Shiki is [build-in](https://content.nuxt.com/get-started/configuration#highlight). For Twoslash, you can add [`nuxt-content-twoslash`](https://github.com/antfu/nuxt-content-twoslash) on top. - If not, you can use [`nuxt-shiki`](https://github.com/pi0/nuxt-shiki) to use Shiki as Vue component or composables. - [VitePress](https://shiki.style/packages/vitepress) - Shiki is [built-in](https://vitepress.dev/guide/markdown#syntax-highlighting-in-code-blocks). For Twoslash, you can use [`vitepress-twoslash`](https://shiki.style/packages/vitepress#twoslash). - Low-level integrations - Shiki provides official integrations for markdown compilers: - [`markdown-it`](https://shiki.style/packages/markdown-it) - Plugin for [`markdown-it`](https://github.com/markdown-it/markdown-it) - [`rehype`](https://shiki.style/packages/rehype) - Plugin for [`rehype`](https://github.com/rehypejs/rehype) Check out more integrations on [Shiki's Documentation](https://shiki.style/) ## Conclusions **Our mission at Nuxt is not only to make a better framework for developers, but also to make the entire frontend and web ecosystem a better place.** We are keeping pushing the boundaries and endorse the modern web standards and best practices. We hope you enjoy the new [Shiki](https://github.com/shikijs/shiki), [unwasm](https://github.com/unjs/unwasm), [Twoslash](https://github.com/twoslashes/twoslash) and many other tools we made in the process of making Nuxt and the web better. # Nuxt 3.11 > Nuxt 3.11 is out - with better logging, preview mode, server pages and much more! This is possibly the last minor release before Nuxt v4, and so we've packed it full of features and improvements we hope will delight you! ✨ ## 🪵 Better logging When developing a Nuxt application and using `console.log` in your application, you may have noticed that these logs are not displayed in your browser console when refreshing the page (during server-side rendering). This can be frustrating, as it makes it difficult to debug your application. This is now a thing of the past! Now, when you have server logs associated with a request, they will be bundled up and passed to the client and displayed in your browser console. [Asynchronous context](https://nodejs.org/docs/latest-v20.x/api/async_context.html) is used to track and associate these logs with the request that triggered them. ([#25936](https://github.com/nuxt/nuxt/pull/25936)). For example, this code: ```vue [pages/index.vue] <script setup> console.log('Log from index page') const { data } = await useAsyncData(() => { console.log('Log inside useAsyncData') return $fetch('/api/test') }) </script> ``` will now log to your browser console when you refresh the page: ```bash Log from index page [ssr] Log inside useAsyncData at pages/index.vue ``` 👉 We also plan to support streaming of subsequent logs to the Nuxt DevTools in future. We've also added a `dev:ssr-logs` hook (both in Nuxt and Nitro) which is called on server and client, allowing you to handle them yourself if you want to. If you encounter any issues with this, it is possible to disable them - or prevent them from logging to your browser console. ```ts [nuxt.config.ts] export default defineNuxtConfig({ features: { devLogs: false // or 'silent' to allow you to handle yourself with `dev:ssr-logs` hook }, }) ``` ## 🎨 Preview mode A new `usePreviewMode` composable aims to make it simple to use preview mode in your Nuxt app. ```ts [plugins/test.client.ts] const { enabled, state } = usePreviewMode() ``` When preview mode is enabled, all your data fetching composables, like `useAsyncData` and `useFetch` will rerun, meaning any cached data in the payload will be bypassed. ::read-more{to="https://nuxt.com/docs/api/composables/use-preview-mode"} :: ## 💰 Cache-busting payloads We now automatically cache-bust your payloads if you haven't disabled Nuxt's app manifest, meaning you shouldn't be stuck with outdated data after a deployment ([#26068](https://github.com/nuxt/nuxt/pull/26068)). ## 👮‍♂️ Middleware `routeRules` It's now possible to define middleware for page paths within the Vue app part of your application (that is, not your Nitro routes) ([#25841](https://github.com/nuxt/nuxt/pull/25841)). ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/admin/**': { // or appMiddleware: 'auth' appMiddleware: ['auth'] }, '/admin/login': { // You can 'turn off' middleware that would otherwise run for a page appMiddleware: { auth: false } }, }, }) ``` ::read-more{to="https://nuxt.com/docs/guide/concepts/rendering#route-rules"} :: ## ⌫ New `clear` data fetching utility Now, `useAsyncData` and `useFetch` expose a `clear` utility. This is a function that can be used to set `data` to undefined, set `error` to `null`, set `pending` to `false`, set `status` to `idle`, and mark any currently pending requests as cancelled. ([#26259](https://github.com/nuxt/nuxt/pull/26259)) ```vue <script setup lang="ts"> const { data, clear } = await useFetch('/api/test') const route = useRoute() watch(() => route.path, (path) => { if (path === '/') clear() }) </script> ``` ::read-more{to="https://nuxt.com/docs/getting-started/data-fetching"} :: ## 🕳️ New `#teleports` target Nuxt now includes a new `<div id="teleports"></div>` element in your app within your `<body>` tag. It supports server-side teleports, meaning you can do this safely on the server: ```vue [app.vue] <template> <Teleport to="#teleports"> <span> Something </span> </Teleport> </template> ``` ## 🚦 Loading indicator and transition controls It's now possible to set custom timings for hiding the loading indicator, and forcing the `finish()` method if needed ([#25932](https://github.com/nuxt/nuxt/pull/25932)). There's also a new `page:view-transition:start` hook for hooking into the View Transitions API ([#26045](https://github.com/nuxt/nuxt/pull/26045)) if you have that feature enabled. ## 🛍️ Server- and client-only pages This release sees server- and client-only pages land in Nuxt! You can now add a `.server.vue` or `.client.vue` suffix to a page to get automatic handling of it. **Client-only pages** will render entirely on the client-side, and skip server-rendering entirely, just as if the entire page was wrapped in `<ClientOnly>`. Use this responsibly. The flash of load on the client-side can be a bad user experience so make sure you really need to avoid server-side loading. Also consider using `<ClientOnly>` with a `fallback` slot to render a skeleton loader ([#25037](https://github.com/nuxt/nuxt/pull/25037)). ⚗️ **Server-only pages** are even more useful because they enable you to integrate fully-server rendered HTML within client-side navigation. They will even be prefetched when links to them are in the viewport - so you will get instantaneous loading ([#24954](https://github.com/nuxt/nuxt/pull/24954)). ## 🤠 Server component bonanza When you are using server components, you can now use the `nuxt-client` attribute anywhere within your tree ([#25479](https://github.com/nuxt/nuxt/pull/25479)). ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { componentIslands: { selectiveClient: 'deep' } }, }) ``` You can listen to an `@error` event from server components that will be triggered if there is any issue loading the component ([#25798](https://github.com/nuxt/nuxt/pull/25798)). Finally, server-only components are now smartly enabled when you have a server-only component or a server-only page within your project or any of its layers ([#26223](https://github.com/nuxt/nuxt/pull/26223)). ::callout{type="warning"} Server components remain experimental and their API may change, so be careful before depending on implementation details. :: ## 🔥 Performance improvements We've shipped a number of performance improvements, including only updating changed virtual templates ([#26250](https://github.com/nuxt/nuxt/pull/26250)), using a 'layered' prerender cache ([#26104](https://github.com/nuxt/nuxt/pull/26104)) that falls back to filesystem instead of keeping everything in memory when prerendering - and lots of other examples. ## 📂 Public assets handling We have shipped a reimplementation of Vite's public asset handling, meaning that public assets in your `public/` directory or your layer directories are now resolved entirely by Nuxt ([#26163](https://github.com/nuxt/nuxt/pull/26163)), so if you have added `nitro.publicAssets` directories with a custom prefix, these will now work. ## 📦 Chunk naming We have changed the default `_nuxt/[name].[hash].js` file name pattern for your JS chunks. Now, we default to `_nuxt/[hash].js`. This is to avoid false positives by ad blockers triggering off your component or chunk names, which can be a very difficult issue to debug. ([#26203](https://github.com/nuxt/nuxt/pull/26203)) You can easily configure this to revert to previous behaviour if you wish: ```ts [nuxt.config.ts] export default defineNuxtConfig({ vite: { $client: { build: { rollupOptions: { output: { chunkFileNames: '_nuxt/[name].[hash].js', entryFileNames: '_nuxt/[name].[hash].js' } } } } }, }) ``` ## 💪 Type fixes Previously users with `shamefully-hoist=false` may have encountered issues with types not being resolved or working correctly. You may also have encountered problems with excessive type instantiation. We now try to tell TypeScript about certain key types so they can be resolved even if deeply nested ([#26158](https://github.com/nuxt/nuxt/pull/26158)). There are a whole raft of other type fixes, including some regarding import types ([#26218](https://github.com/nuxt/nuxt/pull/26218) and [#25965](https://github.com/nuxt/nuxt/pull/25965)) and module typings ([#25548](https://github.com/nuxt/nuxt/pull/25548)). ## ✅ Upgrading As usual, our recommendation for upgrading Nuxt is to run: ```bash nuxi upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## 👉 Full release notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.11.0"} Read the full release notes of Nuxt `v3.11.0`. :: Thank you for reading this far! We hope you enjoy the new release. Please do let us know if you have any feedback or issues. **Happy Nuxting ✨** # Nuxt: Looking forward > A lot of things have happened for Nuxt over the last year. Sébastien and Daniel share their thoughts on what we've achieved, and where we're going next. ## A Review of 2023 - Sébastien In January 2023, Daniel set out [**Nuxt: A vision for 2023**](https://nuxt.com/blog/vision-2023). We achieved most of the goals we set out to accomplish. Some of them are missing and we will tackle them this year! It was a productive year and the team shipped **9 minor releases**: from v3.1 to v3.9. In the first 365 days, Nuxt 3 reached new milestones: - :icon{name="i-lucide-star"} **49,5K stars** on GitHub ([add yours](https://github.com/nuxt/nuxt) 😊) - :icon{name="i-lucide-download"} **27M downloads** on npm - :icon{name="i-lucide-user-plus"} **612 contributors** on the [nuxt repository](https://github.com/nuxt/nuxt) - :icon{name="i-lucide-puzzle"} **184 modules** created by **142 maintainers** - :icon{name="i-lucide-circle-check"} **2,423 closed issues** - :icon{name="i-lucide-git-pull-request"} **1,728 merged pull request** - :icon{name="i-simple-icons-discord"} **26,300 members** on [Discord](https://chat.nuxt.dev) End of October, Nuxt 3 downloads [surpassed Nuxt 2 downloads](https://x.com/Atinux/status/1731980841142669379) 🚀. The same month, we released [Nuxt Image 1.0](https://image.nuxt.com) with Nuxt 3 support and new features to make sure your website stays performant when using Images. Nuxt now auto-installs it as soon as you start using the [`<NuxtImg>`](https://nuxt.com/docs/api/components/nuxt-img) or [`<NuxtPicture>`](https://nuxt.com/docs/api/components/nuxt-picture) component. We shipped [Nuxt DevTools](https://nuxt.com/blog/nuxt-devtools-v1-0), leveling up the Developer Experience we can expect from a Web framework. I am happy to see that we inspired other frameworks to adopt a similar approach: [Vue DevTools](https://x.com/vuejs/status/1741032977919053865), [Next DevTools](https://x.com/xinyao27/status/1741447261132145133), [Remix DevTools](https://x.com/AlemTuzlak59192/status/1741903214860009539), [Vite Plugin DevTools](https://github.com/pheno-agency/vite-plugin-devtools), [Astro Dev Toolbar](https://x.com/n_moore/status/1732164645778071888) and more. After many months of building our UI library internally, [Benjamin Canac](https://github.com/benjamincanac) open sourced [Nuxt UI](https://ui.nuxt.com) to let you create websites at a faster pace with highly customizable components built with TailwindCSS. [Nuxt Content](http://content.nuxt.com) has had 10 minor releases with various improvements: from performance to the MDC syntax. We have some ongoing work to better support [edge rendering](https://nuxt.com/blog/nuxt-on-the-edge) in the future. At the same time, the NuxtLabs team is working on [Nuxt Studio](https://nuxt.studio) to let users edit Markdown files with a Notion-like editor while keeping your content inside your git repository. ## Looking forward into 2024 - Daniel We are well into 2024, and this update is definitely overdue! Here are a few thoughts on the direction that I'll be seeking to lead Nuxt in over the next year. ### Continued development and reimagination I would love to see us continuing to adapt our best practices to the changing needs of the ecosystem, expand the boundaries of what good DX could be, and improve Nuxt. I am really pleased at what we currently offer in Nuxt. But I would never want to rest on our laurels. We need to keep pursuing new possibilities that appear as the web platform develops. We've committed to [Baseline Compatibility](https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility) going forward (and are included in conversations about it with the [W3C WebDX Community Group](https://github.com/web-platform-dx/web-features)). We have some strategies to enable us to do this while also prioritising stability for our existing users, and recognising the scope of the Nuxt ecosystem. 1. In general we follow a pattern of **introducing a module or experimental feature** that can be disabled in testing while we gather feedback and confirm the direction and API of new features. 2. We have adopted the **ecosystem-ci pattern** of validating that our code changes in Nuxt don't break downstream projects unintentionally. If you maintain a well-tested module or library that depends on Nuxt, I'd welcome a PR adding your project to [nuxt/ecosystem-ci](https://github.com/nuxt/ecosystem-ci). 3. We plan to release a **pull-based test** that will allow you to configure your CI to test itself against the latest nightly version of Nuxt to provide early feedback on new Nuxt releases, rather than facing an issue when upgrading. 4. Finally, we have adopted a **push-based approach to updating existing projects**. When we identify breaking changes or improvements that can be rolled out to the ecosystem, we will proactively raise PRs to Nuxt modules (and, to a lesser extent, other open-source projects using Nuxt). I hope this will allow us to continue to innovate and experiment with new features without sacrificing the stability and maintainability. In particular, look for active development on the following: - ✅ [nuxt/fonts](https://github.com/nuxt/fonts) - working up to v1.0.0 - ✅ [nuxt/eslint](https://github.com/nuxt/eslint) - working up to v1.0.0 - 🔥 [nuxt/scripts](https://github.com/nuxt/scripts) - soon to be released - 🚧 [nuxt/a11y](https://github.com/nuxt/a11y) - coming soon! - 🚧 [nuxt/auth](https://github.com/nuxt/auth) - coming soon! - 🚧 [nuxt/hints](https://github.com/nuxt/hints) - coming soon! ### Sustainable open source I want Nuxt to continue to be an independent, community-driven framework for the long term. I'm really delighted to see successful businesses founded on Nuxt, and initiatives springing up around Nuxt that become [sponsors](https://nuxt.com/sponsors) or otherwise giving back to the core framework, enabling ongoing Nuxt development. Obviously, [NuxtLabs](https://nuxtlabs.com) is the preeminent example of that. [Pooya Parsa](https://github.com/pi0), [Anthony Fu](https://github.com/antfu) and I are all able to work full-time on open source thanks to their paid services around Nuxt: [Nuxt UI Pro](https://ui.nuxt.com/pro/getting-started), [Nuxt Experts](https://nuxt.com/enterprise/support), [Nuxt Studio](https://nuxt.studio/) and [NuxtHub](https://hub.nuxt.com/) (soon). They also [sponsor community members](https://github.com/orgs/nuxtlabs/sponsoring) from Vue, Vite, UnJS, and Nuxt. For me, keeping Nuxt independent and sustainable for the future requires an active and growing team and community. In the weeks ahead, I'll be announcing a broader 'Nuxt team' alongside the core team. This is about recognising the tireless work of the many people whose contributions are already making Nuxt what it is. I would also love to see more of those team members or the wider community sponsored to work in the Nuxt ecosystem in the years ahead. If your company has benefited from Nuxt, please do consider sponsoring some of the developers working on Nuxt, whether they are team or wider community members. ### Friendly collaboration From the beginning of our work on Nuxt 3, we have sought to extract out utilities, libraries and even frameworks that we benefit from so they can *also* benefit a much broader audience. That's the origin of [UnJS](https://unjs.io/), of course, and it continues to thrive under the leadership of [Pooya Parsa](https://github.com/pi0) (who is also a key member of the Nuxt core team). We'd like to continue that attitude of friendly collaboration. Although building things ourselves or keeping them to ourselves might to be 'success', I don't have any time for zero-sum games. We will go further, both as a framework and as a web community, if we build *together*. Equally, one of our core values is platform-independence (alongside deep platform *integration*). We support 90+ providers across deployment, testing, stories, KV and cache, databases, image CDNs and font hosts. That breadth of ecosystem is one that bears rich rewards and frees people up to make choices about which providers they use based on the value they bring. But it *also* means we are dependent on our community who use these different providers to help us keep these integrations up-to-date. Nuxt isn't going anywhere, but we hope that the work we do collaboratively will last long beyond us. ### A welcoming community Although it is easy to focus on technical excellence or the quality of developer experience, I am more encouraged to see the continued growth of the Nuxt ecosystem as a welcoming and friendly community. I am incredibly grateful for the kind tone that prevails across [our community Discord](https://chat.nuxt.dev), on [Twitter](https://x.com/nuxt_js), and on [GitHub](https://github.com/nuxt/nuxt). ### What about Nuxt 4? The JavaScript space is known for producing a new framework every week, which means there is a natural hype cycle. But we are not aiming for hype with the continued development of Nuxt, which is part of why we have committed to regular release cycles: - **major** framework releases every year - **minor** releases every month or so - **patch** releases every week or so You can expect to see breaking changes shipped in major releases, with features shipped in our minor releases. That means we aren't holding features back for Nuxt 4; we'll ship those as regularly as we can in our 6-weekly minor release cycle. Our aim is that Nuxt 4 is an opportunity for *thoughtful* breaking changes with either a straightforward migration path (ideally with automated processes) or the ability to opt-in to previous behaviour. There are definitely lessons we as a team have learned form the transition from Nuxt 2 to 3, and I see Nuxt 4 as an opportunity for us to prove that major releases can be a good experience for users. You can preview the changes we have in mind by browsing [our roadmap](https://github.com/orgs/nuxt/projects/8/views/4) or checking through [the issues we've tagged for v4](https://github.com/nuxt/nuxt/issues?q=is:issue+label:4.x). Feedback and wishes are very welcome as always! #### Timetable Our roadmap for Nuxt 4 is a little complex as we are also planning on major releases across the UnJS ecosystem. Roughly speaking, we are aiming to have one more minor release for Nuxt v3 and Nitro v2, before beginning a [raft of major releases across the UnJS ecosystem of packages](https://github.com/unjs/community/discussions/4) - culminating in Nuxt v4. We are aiming to release **Nuxt v4 on or before June 14** (though obviously this is dependent on having enough time after Nitro's major release to be properly tested in the community, so be aware that this is not an exact date). #### Ongoing Support After the release of Nuxt v4, we will be providing **six months** of ongoing support and bug-fixes for Nuxt v3, which we expect will be more than ample to upgrade to v4 given our aim of a gentle upgrade path. We hope you are as excited as we are about the pending release of Nuxt v4! 🎉 --- Finally, thank you so much for all your trust and support as we've been building Nuxt. I know I speak for the whole team when I say that it's such a privilege to be doing this alongside everyone in the Nuxt community! ❤️ Daniel (on behalf of the whole Nuxt team) # Refreshed Nuxt ESLint Integrations > We revamped our ESLint integrations to support ESLint v9 with the flat config, as well as a new module with many more capabilities. ## TL;DR We revamped our ESLint integrations to support ESLint v9 with the new flat config. Along the way, we have explored many new possibilities to make it more personalized, powerful, and with better developer experience. You can run the following command to install the new [`@nuxt/eslint`](https://eslint.nuxt.com/packages/module) module: ```bash [Terminal] npx nuxi module add eslint ``` Continue reading the story or learn more with [the documentation](https://eslint.nuxt.com/packages/module). ## Background [ESLint](https://eslint.org/) has become an essential tool for today's web development. It helps you to catch errors and enforce a consistent coding style in your project. At Nuxt, we do our best to provide an out-of-the-box experience for ESLint, making it easy to use, configure and follow the best practices we recommend. Since, both Nuxt and ESLint have evolved a lot. Historically, we ended up with [a few different packages and integrations for ESLint in Nuxt](https://eslint.nuxt.com/guide/faq#package-disambiguation), and it wasn't always obvious which one to use for what purpose. We have received a lot of feedback from our community. To improve the situation and also make it future-proof, we recently refreshed our ESLint integrations to support [ESLint v9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/) with the [flat config](https://eslint.org/docs/latest/use/configure/configuration-files). It opens up many more capabilities for customizing your ESLint setup, providing a more straightforward and unified experience. ## Nuxt ESLint Monorepo We moved ESLint-related packages scattered across different repositories into a single monorepo: [`nuxt/eslint`](https://github.com/nuxt/eslint), with a dedicated new documentation site: [eslint.nuxt.com](https://eslint.nuxt.com/). To help understand the differences between each package and what to use, we also have a [FAQ](https://eslint.nuxt.com/guide/faq) page comparing them and explaining their scopes. This monorepo now includes: - `@nuxt/eslint` - The new, all-in-one ESLint module for Nuxt 3, supporting project-aware ESLint flat config and more. - `@nuxt/eslint-config` - The unopinionated but customizable shared ESLint config for Nuxt 3. Supports both [the flat config format](https://eslint.org/docs/latest/use/configure/configuration-files) and [the legacy format](https://eslint.org/docs/latest/use/configure/configuration-files-deprecated). - `@nuxt/eslint-plugin` - The ESLint plugin for Nuxt 3 provides Nuxt-specific rules and configurations. - Two packages for Nuxt 2 in maintenance mode. ## ESLint Flat Config Before diving into new Nuxt integrations, let me introduce you to the concept of [ESLint flat config](https://eslint.org/docs/latest/use/configure/configuration-files). Flat config is a configuration format introduced in ESLint `v8.21.0` as experimental, and became the default format in [ESLint v9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/). A quick reference to differentiate: - **Flat config**: `eslint.config.js` `eslint.config.mjs` etc. - **Legacy config**: `.eslintrc` `.eslintrc.json` `.eslintrc.js` etc. ### Why Flat Config? [This blog post from ESLint](https://eslint.org/blog/2022/08/new-config-system-part-1/) explains the motivation behind the flat config system in detail. In short, the legacy `eslintrc` format was designed in the early time of JavaScript, when ES modules and modern JavaScript features were not yet standardized. Many implicit conventions involved, and the `extends` feature makes the final config result hard to understand and predict. Which also makes shared configs hard to maintain and debug. ```jsonc [.eslintrc] { "extends": [ // Solve from `import("@nuxtjs/eslint-config").then(mod => mod.default)` "@nuxtjs", // Solve from `import("eslint-config-vue").then(mod => mod.default.configs["vue3-recommended"])` "plugin:vue/vue3-recommended", ], "rules": { // ... } } ``` The new flat config moves the plugins and configs resolution from ESLint's internal convention to the native ES module resolution. This in turn makes it more explicit and transparent, allowing you to even import it from other modules. Since the flat config is just a JavaScript module, it also opens the door for much more customization. ## Nuxt Presets for Flat Config In the latest [`@nuxt/eslint-config` package](https://eslint.nuxt.com/packages/config#flat-config-format), we leverage the flexibility we have to provide a factory function that allows you to customize the config presets easily in a more high-level way. Here is an example of how you can use it: ```ts [eslint.config.js] import { createConfigForNuxt } from '@nuxt/eslint-config/flat' export default createConfigForNuxt() ``` `@nuxt/eslint-config` starts with an unopinionated base config, which means we only include rules for best practices of TypeScript, Vue and Nuxt, and leave the rest like code style, formatting, etc for you to decide. You can also run [Prettier](https://prettier.io/) alongside for formatting with the defaults. The config also allows you to opt-in to more opinionated features as needed. For example, if you want ESLint to take care of the formatting as well, you can turn it on by passing `features.stylistic` to the factory function (powered by [ESLint Stylistic](https://eslint.style/)): ```ts [eslint.config.js] import { createConfigForNuxt } from '@nuxt/eslint-config/flat' export default createConfigForNuxt({ features: { stylistic: true } }) ``` Or tweak your preferences with an options object ([learn more with the options here](https://eslint.style/guide/config-presets#configuration-factory)): ```ts [eslint.config.js] import { createConfigForNuxt } from '@nuxt/eslint-config/flat' export default createConfigForNuxt({ features: { stylistic: { semi: false, indent: 2, // 4 or 'tab' quotes: 'single', // ... and more } } }) ``` And if you are [authoring a Nuxt Module](https://nuxt.com/docs/guide/going-further/modules), you can turn on `features.tooling` to enable the rules for the Nuxt module development: ```ts [eslint.config.js] import { createConfigForNuxt } from '@nuxt/eslint-config/flat' export default createConfigForNuxt({ features: { tooling: true } }) ``` ...and so on. The factory function in flat config allows the presets to cover the complexity of the underlying ESLint configuration, and provide high-level and user-friendly abstractions for end users to customize. All this without requiring users to worry about the internal details. While this approach offers you a Prettier-like experience with minimal configurations (because it's powered by ESLint), you still get the full flexibility to customize and override fine-grained rules and plugins as needed. We also made a [`FlatConfigComposer`](https://github.com/antfu/eslint-flat-config-utils#composer) utility from [`eslint-flat-config-utils`](https://github.com/antfu/eslint-flat-config-utils) to make it even easier to override and extend the flat config. The factory function in `@nuxt/eslint-config/flat` returns a `FlatConfigComposer` instance: ```ts [eslint.config.js] import { createConfigForNuxt } from '@nuxt/eslint-config/flat' export default createConfigForNuxt({ // ...options for Nuxt integration }) .append( // ...append other flat config items ) .prepend( // ...prepend other flat config items before the base config ) // override a specific config item based on their name .override( 'nuxt/typescript', // specify the name of the target config, or index { rules: { // ...override the rules '@typescript-eslint/no-unsafe-assignment': 'off' } } ) // an so on, operations are chainable ``` With this approach, we get the best of both worlds: the simplicity and high-level abstraction for easy to use, and the power for customization and fine-tuning. ## Nuxt ESLint Module Taking this even further, we made the new, all-in-one [`@nuxt/eslint` module](https://eslint.nuxt.com/packages/module) for Nuxt 3. It leverages Nuxt's context to generate project-aware and type-safe ESLint configurations for your project. ### Project-aware Rules We know that Vue's Style Guide suggests the use of [multi-word names for components](https://vuejs.org/style-guide/rules-essential.html#use-multi-word-component-names) to avoid conflicts with existing and future HTML elements. Thus, in [`eslint-plugin-vue`](https://github.com/vuejs/eslint-plugin-vue), we have the rule [`vue/multi-word-component-names`](https://eslint.vuejs.org/rules/multi-word-component-names.html) enabled by default. It's a good practice to follow, but we know that in a Nuxt project, not all `.vue` files are registered as components. Files like `app.vue`, `pages/index.vue`, `layouts/default.vue`, etc. are not available as components in other Vue files, and the rule will be irrelevant to them. Usually, we could turn off the rule for those files like: ```js [eslint.config.js] export default [ { files: ['*.vue'], rules: { 'vue/multi-word-component-names': 'error' } }, { files: ['app.vue', 'error.vue', 'pages/**/*.vue', 'layouts/**/*.vue'], rules: { // disable the rule for these files 'vue/multi-word-component-names': 'off' } } ] ``` It should work for the majority of the cases. However, we know that in Nuxt you can [customize the path for each directory](https://nuxt.com/docs/api/nuxt-config#dir), and [layers](https://nuxt.com/docs/getting-started/layers) allow you to have multiple sources for each directory. This means the linter rules will be less accurate and potentially cause users extra effort to keep them aligned **manually**. Similarly, we want to have [`vue/no-multiple-template-root`](https://eslint.vuejs.org/rules/no-multiple-template-root.html) enabled only for `pages` and `layouts`, etc. As the cases grow, it becomes unrealistic to ask users to maintain the rules manually. That's where the magic of `@nuxt/eslint` comes in! It leverages Nuxt's context to generate the configurations and rules specific to your project structure. Very similar to the [`.nuxt/tsconfig.json`](http://nuxt.com/docs/guide/concepts/typescript#auto-generated-types) Nuxt provides, you now also have the project-aware `.nuxt/eslint.config.mjs` to extend from. To use it, you can add the module to your Nuxt project: ```bash [Terminal] npx nuxi module add eslint ``` ### Config Inspector DevTools Integrations During the migrations and research for the new flat config, I came up with the idea to make an interactive UI inspector for the flat config and make the configurations more transparent and easier to understand. We have integrated it into [Nuxt DevTools](https://github.com/nuxt/devtools) when you have the `@nuxt/eslint` module installed so you easily access it whenever you need it. ![Screenshot of ESLint Config Inspector running as a tab in Nuxt DevTools](https://nuxt.com/assets/blog/nuxt-eslint-inspector.png) The inspector allows you to see the final resolved configurations, rules and plugins that have been enabled, and do quick matches to see how rules and configurations have applied to specific files. It's great for debugging and learning how ESLint works in your project. We are delighted that the ESLint team also finds it useful and is interested in having it for the broader ESLint community. We later joined the effort and made it [the official ESLint Config Inspector](https://github.com/eslint/config-inspector) (it's built with Nuxt, by the way). You can read [this announcement post](https://eslint.org/blog/2024/04/eslint-config-inspector/) for more details. ### Type Generation for Rules One of the main pain points of configuring ESLint was the leak of type information for the rules and configurations. It's hard to know what options are available for a specific rule, and it would require you to jump around the documentation for every rule to figure that out. Thanks again for the new flat config being dynamic with so many possibilities. We figured out a new tool, [`eslint-typegen`](https://github.com/antfu/eslint-typegen), that we could generate the corresponding types from rules configuration schema for each rule **based on the actual plugins you are using**. This means it's a universal solution that works for any ESLint plugins, and the types are always accurate and up-to-date. In the `@nuxt/eslint` module, this feature is integrated out-of-box, so that you will get this awesome experience right away: ![Screenshot of VS Code that showcases the type check and autocomplete with ESLint rules config](https://nuxt.com/assets/blog/nuxt-eslint-typegen.png) ### Dev Server Checker With the new module, we took the chance to merge the [`@nuxtjs/eslint-module`](https://github.com/nuxt-modules/eslint) and the dev server checker for ESLint into the new `@nuxt/eslint` module as an opt-in feature. ::note You might not need this feature most of the time, as your editor integration should already provide ESLint diagnostics right in your editor. However, for some teams that work with different editors and want to ensure ESLint is always running, being able to run ESLint within the dev server might be helpful in some cases. :: To enable it, you can set the `checker` option to `true` in the module options: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { checker: true // <--- } }) ``` Whenever you get some ESLint errors, you will see a warning in the console and the browser. To learn more about this feature, you can check the [documentation](https://eslint.nuxt.com/packages/module#dev-server-checker). ### Module Hooks Since we are now in the Nuxt module with the codegen capabilities and the project-aware configurations, we can actually do a lot more interesting things with this. One is that we can allow modules to contribute to the ESLint configurations as well. Imagine that in the future, when you install a Nuxt module like `@nuxtjs/i18n` it can automatically enable specific ESLint rules for i18n-related files, or when you install something like `@pinia/nuxt` it can install the Pinia ESLint plugin to enforce the best practices for Pinia, etc. As an experiment, we made a module [`nuxt-eslint-auto-explicit-import`](https://github.com/antfu/nuxt-eslint-auto-explicit-import) that can do auto inserts for the auto-imports registered in your Nuxt project with a preconfigured ESLint preset. So that you can get the same nice developer experience with auto-imports on using APIs, but still have the auto-inserted explicit imports in your codebase. This is still in the early stages, and we are still exploring the possibilities and best practices. But we are very excited about the potential and the opportunities it opens up. We will collaborate with the community to see how we can make the most out of it. If you have any ideas or feedback, please do not hesitate to share them with us! ## Ecosystem At Nuxt, we care a lot about the ecosystem and the community as always. During our explorations to adopt the new flat config and improve the developer experience, we made quite a few tools to reach that goal. All of them are general-purposed and can be used outside of Nuxt: - [`@eslint/config-inspector`](https://github.com/eslint/config-inspector) - The official ESLint Config Inspector, provides interactive UI for your configs. - [`eslint-typegen`](https://github.com/antfu/eslint-typegen) - Generate TypeScript types for ESLint rules based on the actual plugins you are using. - [`eslint-flat-config-utils`](https://github.com/antfu/eslint-flat-config-utils) - Utilities for managing and composing ESLint flat configs. We are committed to supporting the broader community and collaborating with developers to improve these tools and expand their possibilities. We are excited to see how these tools can benefit the ESLint ecosystem and contribute to the overall developer experience. ## The Future ::tip The flat config format is still fairly new, and ESLint v9 was just released a couple of weeks ago. Plugins and the community are gradually catching up to the new format. It's still in the phase of exploration and experimentation. :: Looking ahead, we are eager to see how the ESLint ecosystem will continue to evolve and how we can leverage new capabilities and possibilities to further enhance Nuxt's developer experience. We are dedicated to providing a seamless and powerful development environment for Nuxt users, and we will continue to explore new ideas and collaborate with the community to achieve this goal. # Nuxt 3.12 > Nuxt 3.12 is out - full of improvements and preparing the way for Nuxt 4! We're on the road to the release of Nuxt 4, but we've not held back in Nuxt v3.12. ## 🚀 Testing Nuxt 4 changes Nuxt 4 is on the horizon, and it's now possible to test out the behaviour changes that will be coming in the next major release ([#26925](https://github.com/nuxt/nuxt/pull/26925)) by setting an option in your `nuxt.config.ts` file: ```ts [nuxt.config.ts] export default defineNuxtConfig({ future: { compatibilityVersion: 4, }, }) ``` As we've been merging PRs for Nuxt 4, we've been enabling them behind this flag. As much as possible we're aiming for backwards compatibility - our test matrix is running the same fixtures in both v3 and v4 compatibility mode. There is a lot to say here, with 10+ different PRs and behaviour changes documented and testable, but for full details, including migration steps, see [the v4 upgrade documentation](https://nuxt.com/docs/getting-started/upgrade#testing-nuxt-4). We'd be very grateful for early testing of what's coming in Nuxt 4! 🙏 ## 📜 Nuxt Scripts auto-install We've been gradually working to release [Nuxt Scripts](https://scripts.nuxt.com/). It's currently in public preview, but we're near a public release, so we've added some stubs for composables that (when used) will prompt installing the `@nuxt/scripts` module. 👉 Watch out for the launch - and an article explaining more! ## 🌈 Layer auto-registration and bugfixes Just like `~/modules`, any layers within your project in the `~/layers` directory will now be automatically registered as layers in your project ([#27221](https://github.com/nuxt/nuxt/pull/27221)). We also now correctly load layer dependencies, which should resolve a range of issues with monorepos and git installations ([#27338](https://github.com/nuxt/nuxt/pull/27338)). ## 🌐 Built-in accessibility improvements We now have a built-in [`<NuxtRouteAnnouncer>`](https://nuxt.com/docs/api/components/nuxt-route-announcer) component and corresponding [`useRouteAnnouncer`](https://nuxt.com/docs/api/composables/use-route-announcer) composable, which will be added by default to new Nuxt templates going forward. For full details, see [the original PR (#25741)](https://github.com/nuxt/nuxt/pull/25741) and [documentation](https://nuxt.com/docs/api/components/nuxt-route-announcer). We're continuing to work on `nuxt/a11y` - expect to hear more on that in future! ## 🔥 Performance improvements We've landed some performance improvements as well, many of which are behind the `compatibilityVersion: 4` flag, such as a move away from deeply reactive asyncData payloads. Significant improvements include deduplicating modules ([#27475](https://github.com/nuxt/nuxt/pull/27475)) - which will apply mostly to layer users who specify modules in their layers. In one project, we saw 30s+ improvement in starting Nuxt. We've also improved Vite dev server start up time by excluding common ESM dependencies from pre-bundling, and would suggest module authors consider doing the same ([#27372](https://github.com/nuxt/nuxt/pull/27372)). We improved chunk determinism, so sequential builds should be less likely to have *completely* different chunk hashes ([#27258](https://github.com/nuxt/nuxt/pull/27258)). And we tree shake more client-only composables from your server builds ([#27044](https://github.com/nuxt/nuxt/pull/27044)), and have reduced the size of server component payloads ([#26863](https://github.com/nuxt/nuxt/pull/26863)). ## 👨‍👩‍👧‍👦 Multi-app support We've landed a couple of changes that take us toward a place of supporting multi-app natively in Nuxt, including a `multiApp` experimental flag ([#27291](https://github.com/nuxt/nuxt/pull/27291)) and the ability to have multiple Nuxt app instances running in parallel at runtime ([#27068](https://github.com/nuxt/nuxt/pull/27068)). While it's not yet ready, please do follow along on [the tracker issue](https://github.com/nuxt/nuxt/issues/21635), and feel free to pitch in if this is interesting to you. ## ⛑️ DX wins We now serialise more things in your dev server logs, including VNodes ([#27309](https://github.com/nuxt/nuxt/pull/27309)) and [URLs](https://github.com/nuxt/nuxt/commit/a549b46e9). We also addressed a bug that could lead to a frozen dev server. When accessing private runtime config in the browser, we now let you know with a more informative error message ([#26441](https://github.com/nuxt/nuxt/pull/26441)). ## 🪨 Stabilising features We've removed some experimental options that have been stabilised and which we feel no longer need to be configurable: - `experimental.treeshakeClientOnly` (enabled by default since v3.0.0) - `experimental.configSchema` (enabled by default since v3.3.0) - `experimental.polyfillVueUseHead` (disabled since v3.4.0) - implementable in user-land with [plugin](https://github.com/nuxt/nuxt/blob/f209158352b09d1986aa320e29ff36353b91c358/packages/nuxt/src/head/runtime/plugins/vueuse-head-polyfill.ts#L10-L11) - `experimental.respectNoSSRHeader` (disabled since v3.4.0) - implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9) We've also enabled `scanPageMeta` by default ([#27134](https://github.com/nuxt/nuxt/pull/27134)). This pulls out any page metadata in your `definePageMeta` macro, and makes it available to modules (like `@nuxtjs/i18n`) so they can augment it. This unlocks much better module/typed routing integration, but has a potential performance cost - so please file an issue if you experience any problems. ## 💪 Type improvements We now have support for typed `#fallback` slots in server components ([#27097](https://github.com/nuxt/nuxt/pull/27097)). We've also improved some defaults in your generated `tsconfig.json`, including setting `module: 'preserve'` if you have a locally installed TypeScript v5.4 version ([see docs](https://www.typescriptlang.org/tsconfig/#preserve)) - see [#26667](https://github.com/nuxt/nuxt/pull/26667), [#27485](https://github.com/nuxt/nuxt/pull/27485). ## 📦 Module author/power user improvements We have shipped a range of type improvements for module authors, including: - support for typed module options in `installModule` ([#26744](https://github.com/nuxt/nuxt/pull/26744)) - the option to specify compatibility with certain builders (vite/webpack) in module options ([#27022](https://github.com/nuxt/nuxt/pull/27022)) - a new `onPrehydrate` hook for hooking into the browser hydration cycle ([#27037](https://github.com/nuxt/nuxt/pull/27037)) - the ability to access and update *resolved* runtime configuration within modules, with new build-time `useRuntimeConfig` and `updateRuntimeConfig` utils ([#27117](https://github.com/nuxt/nuxt/pull/27117)) ## 🎨 Inlined UI templates If you previously used `@nuxt/ui-templates` then it may be worth knowing that we have moved them from [a separate repository](https://github.com/nuxt/ui-templates) into the [nuxt/nuxt](https://github.com/nuxt/nuxt) monorepo. (This is purely a refactor rather than a change, although you can expect some new designs for Nuxt v4.) ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi@latest upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.12.0"} Read the full release notes of Nuxt `v3.12.0`. :: A huge thank you to the 75+ Nuxt contributors and community members who have been part of this release. ❤️ Finally, thank you for reading this far! We hope you enjoy v3.12, and please do let us know if you have any feedback or issues. 🙏 **Happy Nuxting ✨** # Introducing Nuxt Scripts > Nuxt Scripts provides better performance, privacy, security, and developer experience for third-party scripts. The Nuxt team, in collaboration with the [Chrome Aurora](https://developer.chrome.com/aurora) team at Google, is excited to announce the public beta release of [Nuxt Scripts](https://scripts.nuxt.com). Nuxt Scripts is a better way to work with third-party scripts, providing improved performance, privacy, security, and developer experience. ![Nuxt Scripts Banner](https://nuxt.com/assets/blog/nuxt-scripts/banner.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} ## Getting to Nuxt Scripts Over a year ago, Daniel published the initial [Nuxt Scripts RFC](https://github.com/nuxt/nuxt/discussions/22016). The RFC proposed a module that would "allow third-party scripts to be managed and optimized, following best practices for performant and compliant websites". Having [personal experience](https://github.com/harlan-zw/nuxt-delay-hydration) with solving performance issues related to third-party scripts, I knew how difficult these performance optimizations could be. Nonetheless, I was keen to tackle the problem and took over the project. With the RFC as the seed of the idea, I started prototyping what it could [look like](https://github.com/nuxt/nuxt/discussions/22016#discussioncomment-6527001) using [Unhead](https://unhead.unjs.io/). Thinking about what I wanted to build exactly, I found that the real issue wasn't just how to load "optimized" third-party scripts but how to make working with third-party scripts a better experience overall. ## Why Build a Third-Party Script Module? [94% of sites use at least one third-party provider](https://almanac.httparchive.org/en/2022/third-parties#prevalence), with the average site having [five third-party providers](https://docs.google.com/spreadsheets/d/1YqoRRsyiNsrEabVLu2nRU98JIG_0zLLuoQhC2nX8xbM/edit?gid=1428106498#gid=1428106498). We know that third-party scripts aren't perfect; they [slow down the web](https://web.dev/articles/optimizing-content-efficiency-loading-third-party-javascript#), cause privacy and security issues, and are a pain to work with. However, they are fundamentally useful and aren't going anywhere soon. By exploring the issues with third-party scripts, we can see where improvements can be made. ### 😒 Developer Experience: A Full-Stack Headache Let's walk through adding a third-party script to your Nuxt app using a fictional `tracker.js` script that adds a `track` function to the window. We start by loading the script using `useHead`. ```ts useHead({ script: [{ src: '/tracker.js', defer: true }] }) ``` However, let's now try to get the script functionality working in our app. The following steps are common when working with third-party scripts in Nuxt: - Everything has to be wrapped for SSR safety. - Flaky checks for if the script has loaded. - Augmenting the window object for types. ::code-group ```vue [1: SSR Safety] <script setup> // ❌ Oops, window is not defined! // 💡 The window can't be directly accessed if we use SSR in Nuxt. // 👉 We need to make this SSR safe window.track('page_view', useRoute().path) </script> ``` ```vue [2: Script Timing] <script setup> if (import.meta.client) { // ❌ Oops, the script hasn't finished loading yet! // 💡 A `defer` script may not be available while our Nuxt app hydrates. // 👉 We need to wait for the script to be loaded window.track('page_view', useRoute().path) } </script> ``` ```vue [3: Broken types] <script lang="ts" setup> if (import.meta.client) { useTimeoutFn(() => { // ✅ It's working! // ❌ Oops, types are broken. // 💡 The `window` has strict types and nothing is defined yet. // 👉 We need to manually augment the window window.track('page_view', useRoute().path) }, 1000 /* should be loaded in 1 second!? */) } </script> ``` ```vue [4: It works?] <script lang="ts" setup> declare global { interface Window { track: (e: string, p: string) => void } } if (import.meta.client) { useTimeoutFn(() => { // ✅ It's working and types are valid! // ❌ Oops, ad-blockers, GDPR and duplicate scripts // 💡 There's a lot of hidden complexity in third-party scripts // 👉 We need a better API window.track('page_view', useRoute().path) }, 1000) } </script> ``` :: ### 🐌 Performance: "Why can't I get 100 on Lighthouse?" For a visitor to start interacting with your Nuxt site, the app bundle needs to be downloaded and Vue needs to hydrate the app instance. Loading third-party scripts can interfere with this hydration process, even when using `async` or `defer`. This slows down the network and blocks the main thread, leading to a degraded user experience and poor [Core Web Vitals](https://web.dev/vitals/). The [Chrome User Experience Report](https://developer.chrome.com/docs/crux) shows Nuxt sites with numerous third-party resources typically have poorer [Interaction to Next Paint (INP)](https://web.dev/articles/inp) and [Largest Contentful Paint (LCP)](https://web.dev/articles/lcp) scores. To see how third-party scripts degrade performance, we can look at the [Web Almanac 2022](https://almanac.httparchive.org/en/2022/third-parties#impact-on-performance). The report shows that the top 10 third-party scripts **average median blocking time is 1.4 seconds**. ### 🛡️ Privacy & Security: Do *no* evil? Of the top 10,000 sites, 58% of them have third-party scripts that [exchange tracking IDs stored in external cookies](https://www3.cs.stonybrook.edu/~mikepo/papers/firstparty.www21.pdf), meaning they can track users across sites even with third-party cookies disabled. While in many cases our hands are tied with the providers we use, we should try to minimize the amount of our end-users' data that we're leaking where possible. When we do take on the privacy implications, it can then be difficult to accurately convey these in our privacy policies and build the consent management required to comply with regulations such as GDPR. Security when using third-party scripts is also a concern. Third-party scripts are common attack vectors for malicious actors, most do not provide `integrity` hashes for their scripts, meaning they can be compromised and inject malicious code into your app at any time. ## What does Nuxt Scripts do about these issues? ### Composable: useScript This composable sits between the `<script>` tag and the functionality added to `window.{thirdPartyKey}`. For the `<script>` tag, the composable: - Gives full visibility into the script's loading and error states - Loads scripts as Nuxt is hydrating the app by default, for slightly better performance. - Restricts `crossorigin` and `referrerpolicy` to improve privacy and security. - Provides a way to [delay loading the script](https://scripts.nuxt.com/docs/guides/script-triggers) until you need it. For the scripts API, it: - Provides full type-safety around the script's functions - Adds a proxy layer allowing your app to run when the script functions in unsafe contexts (SSR, before the script is loaded, the script is blocked) ::code-group ```ts [hello.ts] const { proxy, onLoaded } = useScript('/hello.js', { trigger: 'onNuxtReady', use() { return window.helloWorld } }) onLoaded(({ greeting }) => { // ✅ script is loaded! Hooks into Vue lifecycle }) // ✅ OR use the proxy API - SSR friendly, called when script is loaded proxy.greeting() // Hello, World! declare global { interface Window { helloWorld: { greeting: () => 'Hello World!' } } } ``` ```ts [hello.js] window.helloWorld = { greeting() { console.log('Hello, World!') } } ``` :: ### Script Registry The [script registry](https://scripts.nuxt.com/scripts) is a collection of first-party integrations for common third-party scripts. As of release, we support 21 scripts, with more to come. ![Nuxt Scripts Registry](https://nuxt.com/assets/blog/nuxt-scripts/registry.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} These registry scripts are fine-tuned wrappers around `useScript` with full type-safety, runtime validation of the script options (dev only) and environment variable support For example, we can look at the [Fathom Analytics](https://scripts.nuxt.com/scripts/analytics/fathom-analytics) script. ```ts const { proxy } = useScriptFathomAnalytics({ // ✅ options are validated at runtime site: undefined }) // ✅ typed proxy.trackPageview() ``` ### Facade Components The registry includes several [facade components](https://scripts.nuxt.com/docs/guides/facade-components), such as [Google Maps](https://scripts.nuxt.com/scripts/content/google-maps), [YouTube](https://scripts.nuxt.com/scripts/content/youtube-player) and [Intercom](https://scripts.nuxt.com/scripts/support/intercom). Facade components are "fake" components that get hydrated when the third-party script loads. Facade components have trade-offs but can drastically improve your performance. See the [What are Facade Components?](https://scripts.nuxt.com/docs/guides/facade-components#what-are-facade-components) guide for more information. Nuxt Scripts provides facade components as accessible but headless, meaning they are not styled by default but add the necessary a16y data. ::tabs :::tabs-item{label="Output"} ::::youtube-demo :::: ::: :::tabs-item{label="Input"} ```vue <script setup lang="ts"> const isLoaded = ref(false) const isPlaying = ref(false) const video = ref() function play() { video.value?.player.playVideo() } function stateChange(state) { isPlaying.value = state.data === 1 } </script> <template> <ScriptYouTubePlayer ref="video" video-id="d_IFKP1Ofq0" @ready="isLoaded = true" @state-change="stateChange"> <template #awaitingLoad> <div class="absolute left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 h-[48px] w-[68px]"> <svg height="100%" version="1.1" viewBox="0 0 68 48" width="100%"><path d="M66.52,7.74c-0.78-2.93-2.49-5.41-5.42-6.19C55.79,.13,34,0,34,0S12.21,.13,6.9,1.55 C3.97,2.33,2.27,4.81,1.48,7.74C0.06,13.05,0,24,0,24s0.06,10.95,1.48,16.26c0.78,2.93,2.49,5.41,5.42,6.19 C12.21,47.87,34,48,34,48s21.79-0.13,27.1-1.55c2.93-0.78,4.64-3.26,5.42-6.19C67.94,34.95,68,24,68,24S67.94,13.05,66.52,7.74z" fill="#f00" /><path d="M 45,24 27,14 27,34" fill="#fff" /></svg> </div> </template> </ScriptYouTubePlayer> </template> ``` ::: :: ### Consent Management & Element Event Triggers The `useScript` composable gives you full control over how and when your scripts are loaded, by either providing a custom `trigger` or manually calling the `load()` function. Building on top of this, Nuxt Scripts provides advanced triggers to make it even easier. - [Consent Management](https://scripts.nuxt.com/docs/guides/consent) - Load scripts only after the user has given consent such as with a cookie banner. - [Element Event Triggers](https://scripts.nuxt.com/docs/guides/script-triggers#element-event-triggers) - Load scripts based on user interactions such as scrolling, clicking, or form submissions. ```ts const cookieConsentTrigger = useScriptTriggerConsent() const { proxy } = useScript<{ greeting: () => void }>('/hello.js', { // script will only be loaded once the consent has been accepted trigger: cookieConsentTrigger }) // ... function acceptCookies() { cookieConsentTrigger.accept() } // greeting() is queued until the user accepts cookies proxy.greeting() ``` ### Bundling Scripts In many cases, we're loading third-party scripts from a domain that we don't control. This can lead to a number of issues: - Privacy: The third-party script can track users across sites. - Security: The third-party script can be compromised and inject malicious code. - Performance: Extra DNS lookups will slow down the page load. - Developer Experience: Consented scripts may be blocked by ad blockers. To mitigate this, Nuxt Scripts provides a way to bundle third-party scripts into your public directory without any extra work. ```ts useScript('https://cdn.jsdelivr.net/npm/js-confetti@latest/dist/js-confetti.browser.js', { bundle: true, }) ``` The script will now be served from `/_scripts/{hash}` on your own domain. ## To be continued As we saw, there are many opportunities to improve third-party scripts for developers and end-users. The initial release of Nuxt Scripts has solved *some* of these issues, but there's still a lot of work ahead of us. The next items on the roadmap are: - [Add web worker support (Partytown)](https://github.com/nuxt/scripts/issues/182) - [More Live Chat Facade Components](https://github.com/nuxt/scripts/issues/44) - [Offload Scripts To Nuxt Server Proxy](https://github.com/nuxt/scripts/issues/87) - [Iframe Script Sandboxing](https://github.com/nuxt/scripts/issues/131) We'd love to have your contribution and support. ## Getting started To get started with Nuxt Scripts, we've created a [tutorial](https://scripts.nuxt.com/docs/getting-started/confetti-tutorial) to help you get up and running. ## Credits - [Harlan Wilton - Nuxt](https://github.com/harlan-zw) (author) - [Julien Huang - Nuxt](https://github.com/huang-julien) (contributor) - [Daniel Roe - Nuxt](https://github.com/danielroe) (contributor) - [Chrome Aurora - Google](https://developer.chrome.com/aurora) (contributor) And a big thank you to the early contributors. ![Nuxt Scripts Contributors](https://nuxt.com/assets/blog/nuxt-scripts/contributors.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} # Nuxt 3.13 > Nuxt 3.13 is out - porting back some of the new features we're building for Nuxt 4! ## 🏘️ Route Groups We now support naming directories with parentheses/brackets to organise your routes without affecting the path. For example: ```bash [Directory structure] -| pages/ ---| index.vue ---| (marketing)/ -----| about.vue -----| contact.vue ``` This will produce `/`, `/about` and `/contact` pages in your app. The `marketing` group is ignored for purposes of your URL structure. Read more in [the original PR](https://github.com/nuxt/nuxt/pull/28276). ## 🏝️ Islands and Head Metadata It's now possible for server component islands to manipulate the head, such as by adding SEO metadata when rendering. Read more in [#27987](https://github.com/nuxt/nuxt/pull/27987). ## 🪝 Custom Prefetch Triggers We now support custom prefetch triggers for `NuxtLink` ([#27846](https://github.com/nuxt/nuxt/pull/27846)). For example: ```vue [pages/index.vue] <template> <div> <NuxtLink prefetch-on="interaction"> This will prefetch when hovered or when it gains focus </NuxtLink> <!-- note that you probably don't want both enabled! --> <NuxtLink :prefetch-on="{ visibility: true, interaction: true }"> This will prefetch when hovered/focus - or when it becomes visible </NuxtLink> </div> </template> ``` It's also possible to enable/disable these globally for your app and override them per link. For example: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { prefetch: true, prefetchOn: { visibility: false, interaction: true } } } } }) ``` ## 🗺️ Better Server Source Maps When running with `node --enable-source-maps`, you may have noticed that the source maps for the Vue files in your server build pointed to the Vite build output (something like `.nuxt/dist/server/_nuxt/index-O15BBwZ3.js`). Now, even after your Nitro build, your server source maps will reference your original source files ([#28521](https://github.com/nuxt/nuxt/pull/28521)). Note that one of the easiest ways of improving your build performance is to turn off source maps if you aren't using them, which you can do easily in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ sourcemap: { server: false, client: true, }, }) ``` ## 🎁 New Features for Module Authors In the run-up to Nuxt v4, we're working on adding some key functionality for module authors, including a new `isNuxtMajorVersion` utility where required ([#27579](https://github.com/nuxt/nuxt/pull/27579)) and better inferred typing for merged module options using the new `defineNuxtModule().with()` method ([#27520](https://github.com/nuxt/nuxt/pull/27520)). ## ✨ Improved Dev Warnings We no longer warn when using data fetching composables in middleware ([#28604](https://github.com/nuxt/nuxt/pull/28604)) and we warn when user components' names begin with Lazy ([#27838](https://github.com/nuxt/nuxt/pull/27838)). ## 🚨 Vue TypeScript Changes For a while, in the Vue ecosystem, we've been augmenting `@vue/runtime-core` to add custom properties and more to `vue`. However, this inadvertently breaks the types for projects that augment `vue` - which is now the officially recommended and documented way to augment these interfaces (for example, [ComponentCustomProperties](https://vuejs.org/api/utility-types.html#componentcustomproperties), [GlobalComponents](https://vuejs.org/guide/extras/web-components.html#web-components-and-typescript) and [so on](https://vuejs.org/guide/typescript/options-api.html#augmenting-global-properties)). This means *all* libraries must update their code (or it will break the types of libraries that augment `vue` instead). We've updated our types in Nuxt along these lines but you may experience issues with the latest `vue-router` when used with libraries which haven't yet done so. Please create an issue with a reproduction - I'll happily help create a PR to resolve in the upstream library in question. Or you may be able to work around the issue by creating a `declarations.d.ts` in the root of your project with the following code ([credit](https://github.com/nuxt/nuxt/pull/28542#issuecomment-2293282891) to [@BobbieGoede](https://github.com/BobbieGoede)): ```ts [declarations.d.ts] import type { ComponentCustomOptions as _ComponentCustomOptions, ComponentCustomProperties as _ComponentCustomProperties, } from 'vue'; declare module '@vue/runtime-core' { interface ComponentCustomProperties extends _ComponentCustomProperties {} interface ComponentCustomOptions extends _ComponentCustomOptions {} } ``` ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi@latest upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.13.0"} Read the full release notes of Nuxt `v3.13.0`. :: A huge thank you to everyone who's been a part of this release - you are the ones who make Nuxt possible. ❤️ Don't hesitate to let us know if you have any feedback or issues! 🙏 # Nuxt 3.14 > Nuxt 3.14 is out - with a new rspack builder, shared folder, and performance enhancements! Behind the scenes, a lot has been going on in preparation for the release of Nuxt v4 (particularly on the `unjs` side with preparations for Nitro v3!) ### ⚡️ Faster starts powered by `jiti` Loading the nuxt config file, as well as modules and other build-time code, is now powered by `jiti` v2. You can see more about the release in the [jiti v2 release notes](https://github.com/unjs/jiti/releases/tag/v2.0.0), but one of the most important pieces is native node esm import (where possible), which should mean a faster start. ✨ ### 📂 Shared folder for code and types shared with client/server You should never import Vue app code in your nitro code (or the other way around). But this has meant a friction point when it comes to sharing types or utilities that *don't* rely on the nitro/vue contexts. For this, we have a new `shared/` folder ([#28682](https://github.com/nuxt/nuxt/pull/28682)). You can't import Vue or nitro code *into* files in this folder, but it produces auto-imports (if you're using `compatibilityVersion: 4`) which you can consume throughout the rest of your app. If needed you can use the new `#shared` alias which points to this folder. The shared folder is alongside your `server/` folder. (If you're using `compatibilityVersion: 4`, this means it's not inside your `app/` folder.) ### 🦀 `rspack` builder We're excited to announce a new first-class Nuxt builder for `rspack`. It's still experimental but we've refactored the internal Nuxt virtual file system to use `unplugin` to make this possible. Let us know if you like it - and feel free to raise any issues you experience with it. 👉 To try it out, you can use [this starter](https://github.com/danielroe/nuxt-rspack-starter) - or just install `@nuxt/rspack-builder` and set `builder: 'rspack'` in your nuxt config file. ### ✨ New composables We have new `useResponseHeader` and `useRuntimeHook` composables ([#27131](https://github.com/nuxt/nuxt/pull/27131) and [#29741](https://github.com/nuxt/nuxt/pull/29741)). ### 🔧 New module utilities We now have a new `addServerTemplate` utility ([#29320](https://github.com/nuxt/nuxt/pull/29320)) for adding virtual files for access inside nitro runtime routes. ### 🚧 v4 changes We've merged some changes which only take effect with `compatibilityVersion: 4`, but which [you can opt-into earlier](https://nuxt.com/docs/getting-started/upgrade#testing-nuxt-4). 1. previously, if you had a component like `~/components/App/Header.vue` this would be visible in your devtools as `<Header>`. From v4 we ensure this is `<AppHeader>`, but it's opt-in to avoid breaking any manual `<KeepAlive>` you might have implemented. ([#28745](https://github.com/nuxt/nuxt/pull/28745)). 2. Nuxt scans page metadata from your files, before calling `pages:extend`. But this has led to some confusing behaviour, as pages added at this point do not end up having their page metadata respected. So we now do not scan metadata before calling `pages:extend`. Instead, we have a new `pages:resolved` hook, which is called after `pages:extend`, after all pages have been augmented with their metadata. I'd recommend opting into this by setting `experimental.scanPageMeta` to `after-resolve`, as it solves a number of bugs. ## 🗺️ Roadmap to v3.15 They didn't quite make it in time for v3.14 but for the next minor release you can expect (among other things): - auto-imported directives from modules ([#29203](https://github.com/nuxt/nuxt/pull/29203)) - 'isolated' page renders ([#29366](https://github.com/nuxt/nuxt/pull/29366)) - delayed hydration ([#26468](https://github.com/nuxt/nuxt/pull/26468)) ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi@latest upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.14.0"} Read the full release notes of Nuxt `v3.14.0`. :: A huge thank you to everyone who's been a part of this release. We have exciting things in store with our next releases! ❤️ Don't hesitate to let us know if you have any feedback or issues! 🙏 # Introducing Nuxt Icon v1 > Discover Nuxt Icon v1 - a modern, versatile, and customizable icon solution for your Nuxt projects. Icons are essential to modern web interfaces. They simplify navigation, clarify functionality, and enhance visual appeal. However, implementing icons efficiently involves challenges like scalability, dynamic loading, and server-side rendering (SSR) compatibility. To address these challenges, we developed **Nuxt Icon v1** — a versatile, modern solution tailored for Nuxt projects. By building on established icon rendering techniques and introducing novel approaches, Nuxt Icon bridges the gap between performance, usability, and flexibility. In this post, we’ll explore the challenges of icon rendering, the evolution of icon solutions, and how Nuxt Icon combines the best aspects of these methods to offer a seamless experience for developers. --- ## Why Are Icons Challenging? At first glance, icons seem simple - they are essentially just tiny image elements that enhance a user interface, providing visual cues and enhancing usability. ![Icons Showcases](https://nuxt.com/assets/blog/nuxt-icon/icons-showcase.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} However, from an engineering perspective, they pose several challenges. Ideal icons should be: - **Colorable**: Adaptable to themes and color schemes. - **Scalable**: Render crisply at various sizes and resolutions. - **Manageable**: Icon sets can contain hundreds or thousands of icons. - **Efficiently Bundled**: Minimize network requests. - **Optimally Loaded**: Affect application performance and user experience. - **Dynamic**: Support dynamic loading for user-generated or runtime-defined icons. ![Engineering Challenges with Icons](https://nuxt.com/assets/blog/nuxt-icon/challenges.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} Meeting all these needs requires a carefully engineered solution that balances trade-offs. Let’s explore the evolution of icon solutions and how they address these challenges. --- ## A Journey Through Icon Solutions Over the years, developers have experimented with various techniques to render icons efficiently. Let’s explore the evolution of these solutions and the challenges they faced. ### 1. `<img>` Tags: The Early Days The most straightforward solution: using the `<img>` tag. This was the go-to method in the early days of the web. You'd host your image assets and use an `<img>` tag to link to that image, specifying its width and height. It's simple, requires no setup or runtime dependencies, and works natively in browsers. ![Solution 1](https://nuxt.com/assets/blog/nuxt-icon/solution-1.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} However, there are drawbacks. Images can become pixelated, lack color control, and don't scale well. Each icon being a separate image file results in many network requests, which could be slow, especially back in the days of HTTP 1.1. Before the image is downloaded, you might see a flash of invisible icons, which can hurt the user experience. Lastly, it's quite verbose to write, as you need to specify the full path of the image and manage the relative paths. It explains why this approach is rarely used on modern websites today. --- ### 2. Web Fonts: Icon Fonts As the next step in icon evolution, web fonts emerged as a popular solution. Fonts are inherently vectorized and colorable, making them a natural fit for icons. Iconset providers typically compile their icons into a special font file, assigning a unique Unicode character to each icon. This is accompanied by a CSS file that maps these Unicode values to specific icon classes. The advantages of this approach are clear: it's easy to use, colorable, scalable, and requires only a single request to load all icons. ![Solution 2](https://nuxt.com/assets/blog/nuxt-icon/solution-2.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} However, there are some downsides. Loading a large font file upfront can be slow, and customizing the icon set is challenging. Additionally, you might experience a flash of invisible icons before the font loads, as there are no fallback fonts available. --- ### 3. Inline SVGs: Component-Based Icons With the advent of modern frontend frameworks, reusing HTML elements has become significantly easier. This led to the idea of directly inlining SVG tags as components. To support this approach, many icon sets provide packages with wrappers tailored for each framework. For instance, MDI icons use a shared component and pass icon data as props, while Tabler icons offer a dedicated component for each icon. Since these are SVGs, they are inherently colorable, scalable, and retain all the features of SVGs. Typically, icons are bundled into the app, eliminating additional network requests and ensuring they are SSR-friendly and visible on the first render. ![Solution 3](https://nuxt.com/assets/blog/nuxt-icon/solution-3.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} However, this method has its downsides. It generates numerous SVG DOM elements, which can impact performance when many icons are used. It also increases the bundle size and requires specific integration support for each icon set and framework combination, leading to a degree of vendor lock-in. This makes it challenging to switch to a different icon set or framework. Despite these trade-offs, this approach is widely adopted today, as switching icon sets or frameworks is not a frequent necessity for most projects. --- ### 4. Iconify Runtime: Dynamic API Access [Iconify](https://iconify.design/) revolutionized icon usage by aggregating over 200,000 icons across 100+ collections. Its runtime solution dynamically fetched icons via an API, enabling dynamic access to any icon without pre-bundling. This is a great fit for rendering icons from user-provided content or other dynamic content that you don't know at build time. And it's super easy to set up, as you can even use it as a CDN without any build tools. ![Solution 4](https://nuxt.com/assets/blog/nuxt-icon/solution-4.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} While this method offers great flexibility, it does come with some trade-offs. It introduces runtime dependencies, meaning icons will only render after JavaScript is loaded and the icon data is fetched. This approach also poses challenges for server-side rendering (SSR) and caching layers, such as those used in Progressive Web Apps (PWAs). --- ### 5. On-Demand Component Icons With the unified interface from Iconify and Vite's on-demand approach, we developed [`unplugin-icons`](https://github.com/unplugin/unplugin-icons). This tool allows you to import any icons as components on-demand. As an [`unplugin`](https://github.com/unjs/unplugin), it supports all popular build tools, including Vite, webpack, and rspack. We provide compilers for popular frameworks like Vue, React, Svelte, and Solid. With Iconify, you can use any icon across any framework, minimizing vendor lock-in. ![Solution 5](https://nuxt.com/assets/blog/nuxt-icon/solution-5.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} While this technique shares the same pros and cons as previous component icon solutions, the integration with build tools allows us to serve the full Iconify collection while only shipping the icons you actually use. However, runtime concerns like DOM element management still persist. --- ### 6. Pure CSS Icons As a side-effect of working on [UnoCSS](https://unocss.dev/), we discovered the potential of embedding icons entirely in CSS, leading to the innovative solution of [Pure CSS Icons](https://antfu.me/posts/icons-in-pure-css). This method involves inlining SVG icons as data URLs and providing a single class to display the icons. With some tweaks, these icons become colorable, scalable, and even capable of displaying SVG animations. Browsers can cache the CSS rules, and each icon requires only **one DOM element** to render. This approach ships the icons in a single CSS file with no additional requests. Since it's pure CSS, the icons display along with the rest of your UI, require zero runtime, and work naturally with SSR—your server doesn't need any extra work on the server side. ![Solution 6](https://nuxt.com/assets/blog/nuxt-icon/solution-6.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} The only downsides are the lack of full customization for elements inside the SVG and the need to bundle icons at build-time, which isn't dynamic. --- ## The Challenges to Integrate in Nuxt While I would say that the [Pure CSS Icons](#_6-pure-css-icons) and [On-demand Component Icons](#_5-on-demand-component-icons) would be pretty sufficient for most of the static usages, Nuxt as a full featured framework, has a bit more requirements to integrate icons efficiently: - **SSR/CSR**: Nuxt works in both server-side rendering (SSR) and client-side rendering (CSR) modes. We care a lot about the end user experience, and we want to ensure that icons are rendered instantly without flickers. - **Dynamic Icons**: In integrations like [Nuxt Content](https://content.nuxt.com/), the content can be provided at runtime or from external sources, which we are not aware at build time. We want to ensure we have the capability to integrate with those cases well. - **Performance**: We want to ensure that the icons are bundled efficiently, and the loading of icons is optimized for the best performance. - **Custom Icons**: While Iconify provides a wide range of icons for selection, we also aware it's pretty common for projects to have their own icon sets, or wanted to use paid icons that are not available in Iconify. Supporting custom icons is crucial for our users. ![Nuxt Integration Challenges and Solutions](https://nuxt.com/assets/blog/nuxt-icon/nuxt-icon-challenges.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} With these requirements in mind, let's revisit the solutions we discussed earlier and see how they stack up. For dynamic icons, the Iconify Runtime stands out as a viable option. It allows for dynamic fetching of icons, making it suitable for content that isn't known at build time. However, it has its drawbacks. The reliance on runtime dependencies means it doesn't integrate seamlessly with SSR, and it doesn't support custom icons since the requests are directed to Iconify's servers, which don't have access to our local icon setup. Conversely, Pure CSS Icons offer excellent performance and SSR compatibility. They ensure icons are rendered instantly without flickers and are bundled efficiently. However, they fall short when it comes to dynamic icons, as they need to be bundled at build time and lack the flexibility to adapt to runtime content changes. Balancing these trade-offs is indeed challenging. So, why not leverage the strengths of both approaches? By understanding these trade-offs, we can better appreciate the balanced solution that Nuxt Icon v1 offers. --- ## Introducing Nuxt Icon v1: The Balance of Both Worlds With the flexibility of the Nuxt Module system, Nuxt Icon combines the best of both worlds: the instant rendering of CSS icons and the dynamic fetching of Iconify icons. This dual approach provides a versatile, modern, and customizable icon solution that seamlessly adapts to your project's needs. ### Dual Rendering Modes To address the trade-offs in rendering approaches, Nuxt Icon introduces a versatile `<Icon>` component that supports both CSS and SVG modes, both of which are SSR-friendly. Depending on your customization needs, you can switch between these modes for each icon. In CSS mode, icons are included in the CSS during SSR, ensuring they render instantly without any runtime cost. In SVG mode, icons are inlined as HTML during SSR, providing the same immediate rendering benefits. Both approaches ensure that icons appear on the initial screen without any delay, offering a seamless user experience. ![Dual rendering mode](https://nuxt.com/assets/blog/nuxt-icon/dual-rendering-modes.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} --- ### Icon Bundles Dynamic icons present unique challenges, especially when it comes to loading them efficiently. To address this, we leverage Iconify's API, which allows us to serve any icon on demand via network requests. However, relying solely on this API can introduce delays, especially if the servers are geographically distant from your users. To mitigate this, we introduced the concept of Icon Bundles. We can bundle frequently used icons directly into the `Client Bundle`. This ensures that these icons render instantly without additional network requests. However, bundling all possible icons isn't feasible due to the potential increase in bundle size. Given that Nuxt is a full-stack framework, we can strike a balance by introducing a `Server Bundle`. On the server side, bundle size is less of an issue, allowing us to include a more extensive set of icons. During SSR, these icons can be fetched quickly and sent to the client as needed. This setup ensures high performance for commonly used icons while still providing the flexibility to serve any icon from Iconify as a fallback. By combining client-side bundling for static icons and server-side bundling for dynamic icons, we achieve an optimal balance between performance and flexibility. ![Icon Bundles in Nuxt Icons](https://nuxt.com/assets/blog/nuxt-icon/bundles.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} --- ### Data Flow Here is a data flow diagram illustrating how Nuxt Icon requests icon data: 1. You use the `<Icon>` component and provide the icon `name`. 2. Nuxt Icon will first check if the icon is available in the `Client Bundle`, or the SSR payload (icons that are known at SSR will be presented in the payload). If so, the icon will be rendered instantly. 3. If the icon is not available on the client side, Nuxt Icon will fetch the icon data from the server API shipped along with your Nuxt app. Inside the server endpoint, it will query from the `Server Bundle` to see if the icon is available. 4. Between that, there are multiple cache systems involved. Server endpoint cache, HTTP cache, and client-side cache to ensure the icon is fetched efficiently and quickly. Since icon data does not change frequently, we use hard cache strategies to ensure the best performance. 5. When the icon is unknown to both the client and server (dynamic icons), the server endpoint will fallback to the Iconify API to fetch the icon data. Since the server endpoint is cached, the Iconify API will be called only once for each unique icon regardless of how many clients are requesting it, to save resources on both sides. ![Nuxt Icon Requesting Data flow](https://nuxt.com/assets/blog/nuxt-icon/dataflow.png){.border.border-gray-200.dark:border-gray-700.rounded-lg} This layered approach ensures efficient icon delivery, balancing speed and flexibility, while being as dynamic as possible. And balance out the trade-offs between each solution. --- ## Try Nuxt Icon Today Nuxt Icon v1 represents the culmination of years of innovation in icon rendering. Whether you’re building a dynamic app, a static website, or anything in between, Nuxt Icon adapts to your needs. It’s easy to add Nuxt Icon to your project by running the following command: ```bash npx nuxi module add icon ``` Then, import the `<Icon>` component in your Vue components, providing icon `name` following [Iconify's conventions](https://iconify.design/docs/icons/icon-basics.html): ```vue <template> <Icon name="i-lucide-activity" /> </template> ``` Explore more with the [documentation](https://github.com/nuxt/icon), experiment with its features, and let us know your thoughts. We’re excited to see how Nuxt Icon transforms your projects! **Happy Nuxting ✨** # Nuxt 3.15 > Nuxt 3.15 is out - with Vite 6, better HMR and faster performance We're continuing to work on the release of Nitro v3, Nuxt v4 and more. But we're delighted to ship Nuxt v3.15 (just) in time for Christmas. ## ❄️ Snowfall! Happy holidays! You'll notice when you start Nuxt that (if you're in the Northern Hemisphere) there's some snow on the loading screen ([#29871](https://github.com/nuxt/nuxt/pull/29871)). ## ⚡️ Vite 6 included Nuxt v3.15 includes [Vite 6](https://vite.dev/blog/announcing-vite6) for the first time. Although this is a major version, we expect that this won't be a breaking change for Nuxt users (see full [migration guide](https://vite.dev/guide/migration.html)). However, please take care if you have dependencies that rely on a particular Vite version. One of the most significant changes with Vite 6 is the new Environment API, which we hope to use in conjunction with Nitro to improve the server dev environment. Watch this space! You can read the full list of changes in the [Vite 6 changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md#600-2024-11-26). ## 🪵 Chromium devtools improvements We talk a lot about the Nuxt DevTools, but v3.15 ships with better integration in dev mode for Chromium-based browser devtools. We now use the [Chrome DevTools extensibility API](https://developer.chrome.com/docs/devtools/performance/extension) to add support for printing nuxt hook timings in the browser devtools performance panel. ![CleanShot 2024-11-14 at 15 05 22@2x](https://github.com/user-attachments/assets/57525027-750a-462f-b713-398302aec0cd) ## 🗺️ Navigation mode for `callOnce` `callOnce` is a built-in Nuxt composable for running code only once. For example, if the code runs on the server it won't run again on the client. But sometimes you do want code to run on *every navigation* - just avoid the initial server/client double load. For this, there's a new `mode: 'navigation'` option that will run the code only once *per navigation*. (See [#30260](https://github.com/nuxt/nuxt/pull/30260) for more info.) ```ts await callOnce(() => counter.value++, { mode: 'navigation' }) ``` ## 🥵 HMR for templates, pages + page metadata We now implement hot module reloading for Nuxt's virtual files (like routes, plugins, generated files) as well as for the content of page metadata (within a `definePageMeta` macro) ([#30113](https://github.com/nuxt/nuxt/pull/30113)). This should mean you have a faster experience in development, as well as not needing to reload the page when making changes to your routes. ## 📋 Page meta enhancements We now support extracting extra page meta keys (likely used by module authors) via `experimental.extraPageMetaExtractionKeys` ([#30015](https://github.com/nuxt/nuxt/pull/30015)). This enables module authors to use this information at build time, in the `pages:resolved` hook. We also now support local functions in `definePageMeta` ([#30241](https://github.com/nuxt/nuxt/pull/30241)). This means you can do something like this: ```ts function validateIdParam(route) { return !!(route.params.id && !isNaN(Number(route.params.id))) } definePageMeta({ validate: validateIdParam, }) ``` ## 🔥 Performance improvements We now preload the app manifest in the browser if it will be used when hydrating the app ([#30017](https://github.com/nuxt/nuxt/pull/30017)). We'll also tree shake vue-router's hash mode history out of your bundle if we can - specifically, if you haven't customised your `app/router.options.ts` ([#30297](https://github.com/nuxt/nuxt/pull/30297)). ## 🐣 v4 updates If A few more changes shipped for the new defaults for v4, including only inlining styles by default for Vue components ([#30305](https://github.com/nuxt/nuxt/pull/30305)). ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi@latest upgrade --force ``` This will refresh your lockfile as well, and ensures that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ## Full release notes ::read-more ```yaml [props] color: neutral icon: i-simple-icons-github target: _blank to: https://github.com/nuxt/nuxt/releases/tag/v3.15.0 ``` Read the full release notes of Nuxt `v3.15.0`. :: A huge thank you to everyone who's been a part of this release. ❤️ Don't hesitate to let us know if you have any feedback or issues! 🙏 # Nuxt 3.16 > Nuxt 3.16 is out - packed with features and performance improvements There's a lot in this one! ## ⚡️ A New New Nuxt Say hello to `create-nuxt`, a new tool for starting Nuxt projects (big thanks to [@devgar](https://github.com/devgar) for donating the package name)! It's a streamlined version of `nuxi init` - just a sixth of the size and bundled as a single file with all dependencies inlined, to get you going as fast as possible. Starting a new project is as simple as: ```bash npm create nuxt ``` ![screenshot of create nuxt app](https://nuxt.com/assets/blog/create-nuxt-ascii.jpeg){.border.border-gray-200.dark:border-gray-700.rounded-lg} Special thanks to [@cmang](https://github.com/cmang) for the [beautiful ASCII-art](https://bsky.app/profile/durdraw.org/post/3liadod3gv22a). ❤️ Want to learn more about where we're headed with the Nuxt CLI? Check out our roadmap [here](https://github.com/nuxt/cli/issues/648), including our plans for an [interactive modules selector](https://github.com/nuxt/cli/issues/754). ## 🚀 Unhead v2 We've upgraded to `unhead` v2, the engine behind Nuxt's `<head>` management. This major version removes deprecations and improves how context works: - For Nuxt 3 users, we're shipping a legacy compatibility build so nothing breaks - The context implementation is now more direct via Nuxt itself ```ts // Nuxt now re-exports composables while properly resolving the context export function useHead(input, options = {}) { const unhead = injectHead(options.nuxt) return head(input, { head: unhead, ...options }) } ``` If you're using Unhead directly in your app, keep in mind: 1. Import from Nuxt's auto-imports or `#app/composables/head` instead of `@unhead/vue` 2. Importing directly from `@unhead/vue` might lose async context Don't worry though - we've maintained backward compatibility in Nuxt 3, so most users won't need to change anything! If you've opted into `compatibilityVersion: 4`, check out [our upgrade guide](https://nuxt.com/docs/getting-started/upgrade#unhead-v2) for additional changes. ## 🔧 Devtools v2 Upgrade Nuxt Devtools has leveled up to v2 ([#30889](https://github.com/nuxt/nuxt/pull/30889))! You'll love the new features like custom editor selection, Discovery.js for inspecting resolved configs (perfect for debugging), the return of the schema generator, and slimmer dependencies. One of our favorite improvements is the ability to track how modules modify your Nuxt configuration - giving you X-ray vision into what's happening under the hood. 👉 Discover all the details in the [Nuxt DevTools release notes](https://github.com/nuxt/devtools/releases). ## ⚡️ Performance Improvements We're continuing to make Nuxt faster, and there are a number of improvements in v3.16: 1. Using [`exsolve`](https://github.com/unjs/exsolve) for module resolution ([#31124](https://github.com/nuxt/nuxt/pull/31124)) along with the rest of the unjs ecosystem (nitro, c12, pkg-types, and more) - which dramatically speeds up module resolution 2. Smarter module resolution paths ([#31037](https://github.com/nuxt/nuxt/pull/31037)) - prioritizes direct imports for better efficiency 3. Eliminated duplicated Nitro alias resolution ([#31088](https://github.com/nuxt/nuxt/pull/31088)) - leaner file handling 4. Streamlined `loadNuxt` by skipping unnecessary resolution steps ([#31176](https://github.com/nuxt/nuxt/pull/31176)) - faster startups 5. Adopt `oxc-parser` for parsing in Nuxt plugins ([#30066](https://github.com/nuxt/nuxt/pull/30066)) All these speed boosts happen automatically - no configuration needed! Shout out to [CodSpeed](https://codspeed.io/) with [Vitest benchmarking](https://vitest.dev/guide/features.html#benchmarking) to measure these improvements in CI - it has been really helpful. To add some anecdotal evidence, my personal site at [roe.dev](https://github.com/danielroe/roe.dev) loads 32% faster with v3.16, and [nuxt.com](https://github.com/nuxt/nuxt.com) is 28% faster. I hope you see similar results! ⚡️ ## 🕰️ Delayed Hydration Support We're very pleased to bring you native delayed/lazy hydration support ([#26468](https://github.com/nuxt/nuxt/pull/26468))! This lets you control exactly when components hydrate, which can improve initial load performance and time-to-interactive. We're leveraging Vue's built-in hydration strategies - [check them out in the Vue docs](https://vuejs.org/guide/components/async.html#lazy-hydration). ```vue <template> <!-- Hydrate when component becomes visible in viewport --> <LazyExpensiveComponent hydrate-on-visible /> <!-- Hydrate when browser is idle --> <LazyHeavyComponent hydrate-on-idle /> <!-- Hydrate on interaction (mouseover in this case) --> <LazyDropdown hydrate-on-interaction="mouseover" /> <!-- Hydrate when media query matches --> <LazyMobileMenu hydrate-on-media-query="(max-width: 768px)" /> <!-- Hydrate after a specific delay in milliseconds --> <LazyFooter :hydrate-after="2000" /> </template> ``` You can also listen for when hydration happens with the `@hydrated` event: ```vue <LazyComponent hydrate-on-visible @hydrated="onComponentHydrated" /> ``` Learn more about lazy hydration in [our components documentation](https://nuxt.com/docs/guide/directory-structure/components#delayed-or-lazy-hydration). ## 🧩 Advanced Pages Configuration You can now fine-tune which files Nuxt scans for pages ([#31090](https://github.com/nuxt/nuxt/pull/31090)), giving you more control over your project structure: ```ts [nuxt.config.ts] export default defineNuxtConfig({ pages: { // Filter specific files or directories pattern: ['**/*.vue'], } }) ``` ## 🔍 Enhanced Debugging We've made debugging with the `debug` option more flexible! Now you can enable just the debug logs you need ([#30578](https://github.com/nuxt/nuxt/pull/30578)): ```ts [nuxt.config.ts] export default defineNuxtConfig({ debug: { // Enable specific debugging features templates: true, modules: true, watchers: true, hooks: { client: true, server: true, }, nitro: true, router: true, hydration: true, } }) ``` Or keep it simple with `debug: true` to enable all these debugging features. ## 🎨 Decorators Support For the decorator fans out there (whoever you are!), we've added experimental support ([#27672](https://github.com/nuxt/nuxt/pull/27672)). As with all experimental features, feedback is much appreciated. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { decorators: true } }) ``` ```ts function something (_method: () => unknown) { return () => 'decorated' } class SomeClass { @something public someMethod () { return 'initial' } } const value = new SomeClass().someMethod() // returns 'decorated' ``` ## 📛 Named Layer Aliases It's been much requested, and it's here! Auto-scanned local layers (from your `~~/layers` directory) now automatically create aliases. You can access your `~~/layers/test` layer via `#layers/test` ([#30948](https://github.com/nuxt/nuxt/pull/30948)) - no configuration needed. If you want named aliases for other layers, you can add a name to your layer configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ $meta: { name: 'example-layer', }, }) ``` This creates the alias `#layers/example-layer` pointing to your layer - making imports cleaner and more intuitive. ## 🧪 Error Handling Improvements We've greatly improved error messages and source tracking ([#31144](https://github.com/nuxt/nuxt/pull/31144)): 1. Better warnings for undefined `useAsyncData` calls with precise file location information 2. Error pages now appear correctly on island page errors ([#31081](https://github.com/nuxt/nuxt/pull/31081)) Plus, we're now using Nitro's beautiful error handling (powered by [youch](https://github.com/poppinss/youch)) to provide more helpful error messages in the terminal, complete with stacktrace support. Nitro now also automatically applies source maps without requiring extra Node options, and we set appropriate security headers when rendering error pages. ## 📦 Module Development Improvements For module authors, we've added the ability to augment Nitro types with `addTypeTemplate` ([#31079](https://github.com/nuxt/nuxt/pull/31079)): ```ts // Inside your Nuxt module export default defineNuxtModule({ setup(options, nuxt) { addTypeTemplate({ filename: 'types/my-module.d.ts', getContents: () => ` declare module 'nitropack' { interface NitroRouteConfig { myCustomOption?: boolean } } ` }, { nitro: true }) } }) ``` ## ⚙️ Nitro v2.11 Upgrade We've upgraded to Nitro v2.11. There are so many improvements - more than I can cover in these brief release notes. 👉 Check out all the details in the [Nitro v2.11.0 release notes](https://github.com/nitrojs/nitro/releases/tag/v2.11.0). ## 📦 New `unjs` Major Versions This release includes several major version upgrades from the unjs ecosystem, focused on performance and smaller bundle sizes through ESM-only distributions: - unenv upgraded to v2 (full rewrite) - db0 upgraded to v0.3 (ESM-only, native node:sql, improvements) - ohash upgraded to v2 (ESM-only, native node:crypto support, much faster) - untyped upgraded to v2 (ESM-only, smaller install size) - unimport upgraded to v4 (improvements) - c12 upgraded to v3 (ESM-only) - pathe upgraded to v2 (ESM-only) - cookie-es upgraded to v2 (ESM-only) - esbuild upgraded to v0.25 - chokidar upgraded to v4 ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi@latest upgrade --dedupe ``` This refreshes your lockfile and pulls in all the latest dependencies that Nuxt relies on, especially from the unjs ecosystem. ## Full release notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.16.0"} Read the full release notes of Nuxt `v3.16.0`. :: A huge thank you to everyone who's been a part of this release. ❤️ I'm aware there have been lots of very significant changes in this release - please don't hesitate to let us know if you have any feedback or issues! 🙏 # Nuxt UI v3 > Nuxt UI v3 is out! After 1500+ commits, this major redesign brings improved accessibility, Tailwind CSS v4 support, and full Vue compatibility We are thrilled to announce the release of Nuxt UI v3, a complete redesign of our UI library that brings significant improvements in accessibility, performance, and developer experience. This major update represents over 1500 commits of hard work, collaboration, and innovation from our team and the community. ## 🚀 Reimagined from the Ground Up Nuxt UI v3 represents a major leap forward in our journey to provide the most comprehensive UI solution for Vue and Nuxt developers. This version has been rebuilt from the ground up with modern technologies and best practices in mind. ### **From HeadlessUI to Reka UI** With Reka UI at its core, Nuxt UI v3 delivers: • Proper keyboard navigation across all interactive components • ARIA attributes automatically handled for you • Focus management that just works • Screen reader friendly components out of the box This means you can build applications that work for everyone without becoming an accessibility expert. ### **Tailwind CSS v4 Integration** The integration with Tailwind CSS v4 brings huge performance improvements: • **5x faster runtime** with optimized component rendering • **100x faster build times** thanks to the new CSS-first engine • Smaller bundle sizes with more efficient styling Your applications will feel snappier, build quicker, and load faster for your users. ## 🎨 A Brand New Design System ```html <!-- Before: Inconsistent color usage with duplicate dark mode classes --> <div class="bg-gray-100 dark:bg-gray-800 p-4 rounded-lg"> <h2 class="text-gray-900 dark:text-white text-xl mb-2">User Profile</h2> <p class="text-gray-600 dark:text-gray-300">Account settings and preferences</p> <button class="bg-blue-500 text-white px-3 py-1 rounded mt-2">Edit Profile</button> </div> ``` ```html <!-- After: Semantic design tokens with automatic dark mode support --> <div class="bg-muted p-4 rounded-lg"> <h2 class="text-highlighted text-xl mb-2">User Profile</h2> <p class="text-muted">Account settings and preferences</p> <UButton color="primary" size="sm" class="mt-2">Edit Profile</UButton> </div> ``` Our new color system includes 7 semantic color aliases: | Color | Default | Description | | ---------------------------- | -------- | ------------------------------------------------ | | `primary`{.text-primary} | `blue` | Primary color to represent the brand. | | `secondary`{.text-secondary} | `blue` | Secondary color to complement the primary color. | | `success`{.text-success} | `green` | Used for success states. | | `info`{.text-info} | `blue` | Used for informational states. | | `warning`{.text-warning} | `yellow` | Used for warning states. | | `error`{.text-error} | `red` | Used for form error validation states. | | `neutral` | `slate` | Neutral color for backgrounds, text, etc. | This approach makes your codebase more maintainable and your UI more consistent—especially when working in teams. With these semantic tokens, light and dark mode transitions become effortless, as the system automatically handles the appropriate color values for each theme without requiring duplicate class definitions. ## 💚 Complete Vue Compatibility We're really happy to expand the scope of Nuxt UI beyond the Nuxt framework. With v3, both Nuxt UI and Nuxt UI Pro now work seamlessly in any Vue project, this means you can: • Use the same components across all your Vue projects • Benefit from Nuxt UI's theming system in any Vue application • Enjoy auto-imports and TypeScript support outside of Nuxt • Leverage both basic components and advanced Pro components in any Vue project ```ts [vite.config.ts] import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' import ui from '@nuxt/ui/vite' export default defineConfig({ plugins: [ vue(), ui() ] }) ``` ## 📦 Components for Every Need With 54 core components, 50 Pro components, and 42 Prose components, Nuxt UI v3 provides solutions for virtually any UI challenge: • **Data Display**: Tables, charts, and visualizations that adapt to your data • **Navigation**: Menus, tabs, and breadcrumbs that guide users intuitively • **Feedback**: Toasts, alerts, and modals that communicate clearly • **Forms**: Inputs, selectors, and validation that simplify data collection • **Layout**: Grids, containers, and responsive systems that organize content beautifully Each component is designed to be both beautiful out of the box and deeply customizable when needed. ## 🔷 Improved TypeScript Integration We've completely revamped our TypeScript integration, with features that make you more productive: - Complete type safety with helpful autocompletion - Generic-based components for flexible APIs - Type-safe theming through a clear, consistent API ```ts export default defineAppConfig({ ui: { button: { // Your IDE will show all available options slots: { base: 'font-bold rounded-lg' }, defaultVariants: { size: 'md', color: 'error' } } } }) ``` ## ⬆️ Upgrading to v3 We've prepared a comprehensive [migration](https://ui.nuxt.com/getting-started/migration) guide to help you upgrade from v2 to v3. While there are breaking changes due to our complete overhaul, we've worked hard to make the transition as smooth as possible. ## 🎯 Getting Started Whether you're starting a new project or upgrading an existing one, getting started with Nuxt UI v3 is easy: ```bash # Create a new Nuxt project with Nuxt UI npx nuxi@latest init my-app -t ui ``` ::code-group{sync="pm"} ```bash [pnpm] pnpm add @nuxt/ui@latest ``` ```bash [yarn] yarn add @nuxt/ui@latest ``` ```bash [npm] npm install @nuxt/ui@latest ``` ```bash [bun] bun add @nuxt/ui@latest ``` :: ::warning If you're using **pnpm**, ensure that you either set [`shamefully-hoist=true`](https://pnpm.io/npmrc#shamefully-hoist) in your `.npmrc` file or install `tailwindcss` in your project's root directory. :: Visit our [documentation](https://ui.nuxt.com/getting-started) to explore all the components and features available in Nuxt UI v3. ## 🙏 Thank You This release represents thousands of hours of work from our team and the community. We'd like to thank everyone who contributed to making Nuxt UI v3 a reality. We're excited to see what you'll build with Nuxt UI v3! # Nuxt 3.17 > Nuxt 3.17 is out - bringing a major reworking of the async data layer, a new built-in component, better warnings, and performance improvements! ## 📊 Data Fetching Improvements A major reorganization of Nuxt's data fetching layer brings significant improvements to `useAsyncData` and `useFetch`. Although we have aimed to maintain backward compatibility and put breaking changes behind the `experimental.granularCachedData` flag (disabled by default), we recommend testing your application thoroughly after upgrading. You can also disable `experimental.purgeCachedData` to revert to the previous behavior if you are relying on cached data being available indefinitely after components using `useAsyncData` are unmounted. ::read-more{to="https://github.com/nuxt/nuxt/pull/31373" target="_blank"} Read the original PR for full details. :: ### Consistent Data Across Components All calls to `useAsyncData` or `useFetch` with the same key now share the underlying refs, ensuring consistency across your application: ```vue <!-- ComponentA.vue --> <script setup> const { data: users, pending } = useAsyncData('users', fetchUsers) </script> <!-- ComponentB.vue --> <script setup> // This will reference the same data state as ComponentA const { data: users, status } = useAsyncData('users', fetchUsers) // When either component refreshes the data, both will update consistently </script> ``` This solves various issues where components could have inconsistent data states. ### Reactive Keys You can now use computed refs, plain refs, or getter functions as keys: ```ts const userId = ref('123') const { data: user } = useAsyncData( computed(() => `user-${userId.value}`), () => fetchUser(userId.value) ) // Changing the userId will automatically trigger a new data fetch // and clean up the old data if no other components are using it userId.value = '456' ``` ### Optimized Data Refetching Multiple components watching the same data source will now trigger only a single data fetch when dependencies change: ```ts // In multiple components: const { data } = useAsyncData( 'users', () => $fetch(`/api/users?page=${route.query.page}`), { watch: [() => route.query.page] } ) // When route.query.page changes, only one fetch operation will occur // All components using this key will update simultaneously ``` ## 🎭 Built-In Nuxt Components ### `<NuxtTime>` - A new component for safe time display We've added a new `<NuxtTime>` component for SSR-safe time display, which resolves hydration mismatches when working with dates ([#31876](https://github.com/nuxt/nuxt/pull/31876)): ```vue <template> <NuxtTime :datetime="Date.now()" /> </template> ``` The component accepts multiple time formats and gracefully handles both client and server rendering. ### Enhanced `<NuxtErrorBoundary>` The `<NuxtErrorBoundary>` component has been converted to a Single File Component and now exposes `error` and `clearError` from the component - as well as in the error slot types, giving you greater ability to handle errors in your templates and via `useTemplateRef` ([#31847](https://github.com/nuxt/nuxt/pull/31847)): ```vue <NuxtErrorBoundary @error="handleError"> <template #error="{ error, clearError }"> <div> <p>{{ error.message }}</p> <button @click="clearError">Try again</button> </div> </template> <!-- Content that might error --> <MyComponent /> </NuxtErrorBoundary> ``` ## 🔗 Router Improvements `<NuxtLink>` now accepts a `trailingSlash` prop, giving you more control over URL formatting ([#31820](https://github.com/nuxt/nuxt/pull/31820)): ```vue <NuxtLink to="/about" trailing-slash>About</NuxtLink> <!-- Will render <a href="/about/"> --> ``` ## 🔄 Loading Indicator Customization You can now customize the loading indicator with new props directly on the component ([#31532](https://github.com/nuxt/nuxt/pull/31532)): - `hideDelay`: Controls how long to wait before hiding the loading bar - `resetDelay`: Controls how long to wait before resetting loading indicator state ```vue <template> <NuxtLoadingIndicator :hide-delay="500" :reset-delay="300" /> </template> ``` ## 📚 Documentation as a Package The Nuxt documentation is now available as an npm package! You can install `@nuxt/docs` to access the raw markdown and YAML content used to build the documentation website ([#31353](https://github.com/nuxt/nuxt/pull/31353)). ## 💻 Developer Experience Improvements We've added several warnings to help catch common mistakes: - Warning when server components don't have a root element [#31365](https://github.com/nuxt/nuxt/pull/31365) - Warning when using the reserved `runtimeConfig.app` namespace [#31774](https://github.com/nuxt/nuxt/pull/31774) - Warning when core auto-import presets are overridden [#29971](https://github.com/nuxt/nuxt/pull/29971) - Error when `definePageMeta` is used more than once in a file [#31634](https://github.com/nuxt/nuxt/pull/31634) ## 🔌 Enhanced Module Development Module authors will be happy to know: - A new `experimental.enforceModuleCompatibility` allows Nuxt to throw an error when a module is loaded that isn't compatible with it ([#31657](https://github.com/nuxt/nuxt/pull/31657)). It will be enabled by default in Nuxt v4. - You can now automatically register every component exported via named exports from a file with `addComponentExports` [#27155](https://github.com/nuxt/nuxt/pull/27155) ## 🔥 Performance Improvements Several performance improvements have been made: - Switched to `tinyglobby` for faster file globbing [#31668](https://github.com/nuxt/nuxt/pull/31668) - Excluded `.data` directory from type-checking for faster builds [#31738](https://github.com/nuxt/nuxt/pull/31738) - Improved tree-shaking by hoisting the `purgeCachedData` check [#31785](https://github.com/nuxt/nuxt/pull/31785) ## ✅ Upgrading Our recommendation for upgrading is to run: ```sh npx nuxi@latest upgrade --dedupe ``` This refreshes your lockfile and pulls in all the latest dependencies that Nuxt relies on, especially from the unjs ecosystem. ## Full release notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.17.0"} Read the full release notes of Nuxt `v3.17.0`. :: A huge thank you to everyone who's been a part of this release. ❤️ # Roadmap to v4 > We have some exciting news about the roadmap to Nuxt 4, including a new timeline and what to expect in the next few weeks. We originally planned Nuxt 4 for June 2024, but things don't always go according to plan. I think it's appropriate to take a different approach: 👉 **Nuxt 4 entered Release Candidate (RC) stage on July 8, 2025, with a stable release coming soon. Nuxt 5 will come later once Nitro v3 is ready.** ## Why Two Releases? Honestly, in hindsight I think we should have shipped Nuxt v4 last year. It would have aligned better with our once-a-year plan for careful major releases. On the other hand, it has allowed time for testing across countless projects, by opting in to Nuxt 4 breaking changes with a single flag. I think that we should go ahead and ship these changes as Nuxt v4 for two reasons: 1. **People are already using it**. I frequently hear of teams who are using `compatibilityVersion: 4` in production. This option was *intended* only for testing, but it has proven solid enough for production use. We want to make it official, and give these teams the protection of a stable release. 2. **It will improve the migration**. Having a two-stage migration from v3 -> v4, and then v4 -> v5 will make for a smoother migration. We don't yet have a final list of breaking changes for Nitro v3, and this means we can spend enough time to ensure that the Nitro upgrade is smooth, while not delaying adoption of the Nuxt changes we've planned for the last year. What's more, going forward we're going to do our best to decouple Nuxt releases from our key dependencies, like Vite or Nitro. ## What's Included? Nuxt 4 includes all the features you've been testing with `compatibilityVersion: 4`: - **🗂️ New Directory Structure** - code goes in `app/` for cleaner organization and better IDE performance - **🔄 Improved Data Fetching** - smarter `useAsyncData` and `useFetch` with better caching and cleanup - **🏷️ Consistent Component Names** - Vue DevTools and `<KeepAlive>` now see the same names as Nuxt's auto-imports - **📄 Enhanced Head Management** - dropping deprecated features from Unhead v2 with better performance and tag optimization ... as well as many other improvements documented in the [upgrade guide](https://nuxt.com/docs/getting-started/upgrade). There are also a handful of further changes we will shipping, including: - preparations for adopting the Vite Environment API (with a single dev server) - improvements to type 'environment' handling (for server, client, and shared code) ## What About Nitro? **We're not delaying Nitro v3 adoption**. We aim for Nuxt 5 to arrive with Nitro v3 at the same time we originally planned, even if that's only a few months after the release of Nuxt v4. Despite the delays over the past year, we've seen phenomenal progress on Nitro. As a whole team, we're very excited to unveil what we have planned in Nitro v3 and h3 v2. While Nuxt 4 won't initially include these upgrades, releasing Nuxt v4 and Nitro v3 in parallel will mean we can test both Nuxt and Nitro more thoroughly across the ecosystem by the time Nuxt 5 arrives. ## What About Nuxt 3? We'll provide ongoing maintenance and support for Nuxt 3 after the first stable release of Nuxt 4 — and continue supporting both Nuxt 3 and Nuxt 4 after Nuxt 5 has been released. We've intentionally chosen a slightly shorter timetable of *six months* support for each of these releases because we believe that it will be a straightforward upgrade. (My main aim for the Nuxt 4 upgrade is to ensure that it is as smooth as possible.) Nevertheless, I'll be closely monitoring to see how successfully and quickly the ecosystem migrates. If there are issues I will absolutely extend that six month ongoing maintenance window. We don't want to leave anyone behind. For a while, this will mean active backports of features and bugfixes across three versions. But I think it's worth it. And we have — after all — been doing this for the last year in preparation for Nuxt 4. | | | | ---------- | ------------------------------------------------- | | **Nuxt 3** | Continues receiving updates until the end of 2025 | | **Nuxt 4** | Supported until mid-2026 (estimated) | | **Nuxt 5** | Long-term support following our usual pattern | ## What's Next Nuxt v4 is now in **Release Candidate (RC)** stage! We'd love early adopters to test it. Please do report issues to Nuxt or any modules that you may be using. ::note We are currently in the **release candidate stage**: no more breaking changes are planned — only bug fixes before the stable release. Release stages for Nuxt 4: - **Alpha**: experimental features and breaking changes - **RC (now)**: stable feature set, final testing before release :: Here's what you can expect over the next few weeks: - We plan to open **upstream PRs for community modules** in the [nuxt/modules](https://github.com/nuxt/modules) registry, and create a migration guide for module authors. - We'll create a full **upgrade guide** for Nuxt 3 users, including a list of breaking changes and how to migrate. (The current [upgrade guide](https://nuxt.com/docs/getting-started/upgrade) explains how to enable compatibility mode, but there are some differences with Nuxt 4.) - We'll **only release bugfixes for v3** this month, deferring backporting new features until after the release of v4. - We'll **update the docs on [nuxt.com](https://nuxt.com/)** to allow switching between `3.x`, `4.x` and (soon) `5.x` documentation. - With the **release candidate now live**, we're focused exclusively on bug fixes. No new features or breaking changes are expected. - Once v4 is released, we'll separate the `main` branch to `4.x` to adopt edge releases of `h3` and `nitro` and begin development of Nuxt 5. ::note You can follow the progress of the remaining work by checking [these remaining tasks](https://github.com/nuxt/nuxt/issues/27027) and [the Nuxt 4 milestone](https://github.com/nuxt/nuxt/milestone/8) on GitHub. :: I'm really excited with this timeline — and thank you for your patience and trust over the last year! # Building a Privacy-First Feedback Widget > A lightweight, privacy-focused widget to gather your feedback on Nuxt documentation, built with Drizzle, NuxtHub database and Motion Vue. Documentation is at the heart of the Nuxt developer experience. To continuously improve it, we needed a simple and effective way to collect user feedback directly on each page. Here's how we designed and implemented our feedback widget, drawing inspiration from Plausible's privacy-first approach. ## Why a feedback widget? Currently, users can provide feedback on our documentation by creating GitHub issues or contacting us directly. While these channels are valuable and remain important, they require users to leave their current context and take several steps to share their thoughts. We wanted something different: - **Contextual**: Directly integrated into each documentation page - **Frictionless**: Maximum 2 clicks to provide feedback - **Privacy-respecting**: No personal tracking, GDPR compliant by design ::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1749746517/nuxt/nuxt-feedback_lh6zyg.jpg" controls .rounded-lg} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1749746517/nuxt/nuxt-feedback_lh6zyg.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1749746517/nuxt/nuxt-feedback_lh6zyg.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1749746517/nuxt/nuxt-feedback_lh6zyg.ogg" type="video/ogg"} ::: :: ## Technical architecture Our solution consists of three main components: ### 1. Frontend with Motion animations The interface combines Vue 3's Composition API with [Motion for Vue](https://motion.dev/docs/vue) to create an engaging user experience. The widget uses layout animations for smooth state transitions and spring physics for natural feedback. The `useFeedback` composable handles all state management and automatically resets when users navigate between pages. Here's the success state animation, for example: ```vue <template> <!-- ... --> <motion.div v-if="isSubmitted" key="success" :initial="{ opacity: 0, scale: 0.95 }" :animate="{ opacity: 1, scale: 1 }" :transition="{ duration: 0.3 }" class="flex items-center gap-3 py-2" role="status" aria-live="polite" aria-label="Feedback submitted successfully" > <motion.div :initial="{ scale: 0 }" :animate="{ scale: 1 }" :transition="{ delay: 0.1, type: 'spring', visualDuration: 0.4 }" class="text-xl" aria-hidden="true" > ✨ </motion.div> <motion.div :initial="{ opacity: 0, x: 10 }" :animate="{ opacity: 1, x: 0 }" :transition="{ delay: 0.2, duration: 0.3 }" > <div class="text-sm font-medium text-highlighted"> Thank you for your feedback! </div> <div class="text-xs text-muted mt-1"> Your input helps us improve the documentation. </div> </motion.div> </motion.div> <!-- ... --> </template> ``` You can find the source code of the feedback widget [here](https://github.com/nuxt/nuxt.com/tree/main/app/components/Feedback.vue). ### 2. Plausible-inspired anonymization The challenge was detecting duplicates (a user changing their mind) while preserving privacy. We took inspiration from [Plausible](https://plausible.io/)'s approach to [counting unique visitors without cookies](https://plausible.io/data-policy). ```typescript export async function generateHash( today: string, ip: string, domain: string, userAgent: string ): Promise<string> { const data = `${today}+${domain}+${ip}+${userAgent}` const buffer = await crypto.subtle.digest( 'SHA-1', new TextEncoder().encode(data) ) return [...new Uint8Array(buffer)] .map(b => b.toString(16).padStart(2, '0')) .join('') } ``` This method generates a unique daily identifier by combining: - **IP + User-Agent**: Naturally sent with every HTTP request - **Domain**: Enables environment isolation - **Current date**: Forces daily rotation of identifiers **Why is this secure?** - IP and User-Agent are never stored in the database - The hash changes daily, preventing long-term tracking - Very difficult to reverse engineer original data from the hash - GDPR compliant by design (no persistent personal data) ### 3. Database persistence with conflict handling First, we define the schema for the feedback table and add a unique constraint on the `path` and `fingerprint` columns. ```typescript export const feedback = sqliteTable('feedback', { id: integer('id').primaryKey({ autoIncrement: true }), rating: text('rating').notNull(), feedback: text('feedback'), path: text('path').notNull(), title: text('title').notNull(), stem: text('stem').notNull(), country: text('country').notNull(), fingerprint: text('fingerprint').notNull(), createdAt: integer({ mode: 'timestamp' }).notNull(), updatedAt: integer({ mode: 'timestamp' }).notNull() }, table => [uniqueIndex('path_fingerprint_idx').on(table.path, table.fingerprint)]) ``` Then, in the server, we use [Drizzle](https://orm.drizzle.team/docs/get-started) with an `UPSERT` strategy: ```typescript await drizzle.insert(tables.feedback).values({ rating: data.rating, feedback: data.feedback || null, path: data.path, title: data.title, stem: data.stem, country: event.context.cf?.country || 'unknown', fingerprint, createdAt: new Date(), updatedAt: new Date() }).onConflictDoUpdate({ target: [tables.feedback.path, tables.feedback.fingerprint], set: { rating: data.rating, feedback: data.feedback || null, country, updatedAt: new Date() } }) ``` This approach enables updates if the user changes their mind within the day, creation for new feedback, and automatic deduplication per page and user. You can find the source code of the server side [here](https://github.com/nuxt/nuxt.com/tree/main/server). ## Shared types for consistency We use Zod for runtime validation and type generation: ```typescript export const FEEDBACK_RATINGS = [ 'very-helpful', 'helpful', 'not-helpful', 'confusing' ] as const export const feedbackSchema = z.object({ rating: z.enum(FEEDBACK_RATINGS), feedback: z.string().optional(), path: z.string(), title: z.string(), stem: z.string() }) export type FeedbackInput = z.infer<typeof feedbackSchema> ``` This approach ensures consistency across frontend, API, and database. ## What's next The widget is now live across all documentation pages. Our next step is building an admin interface within [nuxt.com](http://nuxt.com) to analyze feedback patterns and identify pages that need improvement. This will help us continuously enhance the documentation quality based on real user feedback. The complete source code is available on [GitHub](https://github.com/nuxt/nuxt.com) for inspiration and contributions! # Announcing Nuxt 4.0 > Nuxt 4.0 is here! A thoughtful evolution focused on developer experience, with better project organization, smarter data fetching, and improved type safety. **Nuxt 4.0 is here!** 🎉 After a year of real-world testing, we're excited to announce the official release of Nuxt 4. This is a stability-focused major release, introducing a few thoughtful breaking changes in order to improve development experience. If you've been following along, you'll recognize many of these features and changes — and if you're new to them, we hope you'll welcome them. ## 🔥 What's new? Nuxt 4 is all about making your development experience smoother: - **Cleaner project organization** with the new `app/` directory structure - **Smarter data fetching** - we've taken the opportunity to address some inconsistencies and improve performance with the data layer - **Better TypeScript support** with project-based separation between the different contexts in your project - app code, server code, `shared/` folder, and configuration - **Faster CLI and development** with adoption of internal sockets and a faster CLI Why these features in particular? Mostly because these kind of improvements have required making changes that are technically breaking. In general, we aim for a hype-free approach to releases. Rather than save up features for a big release, we've been shipping improvements in Nuxt 3 minor releases. We've also spent a lot of time figuring out how to implement these changes in a backwards-compatible way, and I hope that means that most Nuxt 3 projects can upgrade with a minimum of effort. I'd advise reading through the [upgrade guide](https://nuxt.com/docs/4.x/getting-started/upgrade) before you start, to understand what areas of your app might be affected. ### 🗂️ New project structure The biggest visible change is how projects are organized. Your application code now lives in an `app/` directory by default: ```bash my-nuxt-app/ ├─ app/ │ ├─ assets/ │ ├─ components/ │ ├─ composables/ │ ├─ layouts/ │ ├─ middleware/ │ ├─ pages/ │ ├─ plugins/ │ ├─ utils/ │ ├─ app.vue │ ├─ app.config.ts │ └─ error.vue ├─ content/ ├─ public/ ├─ shared/ ├─ server/ └─ nuxt.config.ts ``` This helps keep your code separate from `node_modules/` and `.git/`, which makes file watchers faster (especially on Windows and Linux). It also gives your IDE better context about whether you're working with client or server code. ::tip **Don't want to migrate?** That's totally fine! Nuxt will detect your existing structure and keep working exactly as before. :: #### 🎨 Updated UI templates Nuxt’s starter templates have an all new look, with improved accessibility, default titles, and template polish ([#27843](https://github.com/nuxt/nuxt/pull/27843)). ### 🔄 Smarter data fetching We've made `useAsyncData` and `useFetch` work better. Multiple components using the same key now share their data automatically. There's also automatic cleanup when components unmount, and you can use reactive keys to refetch data when needed. Plus, we've given you more control over when cached data gets used. Some of these features have already been made available in Nuxt v3 minor releases, because we've been rolling this out gradually. Nuxt v4 brings different defaults, and we expect to continue to work on this data layer in the days to come. ### 🔧 Better TypeScript experience Nuxt now creates separate TypeScript projects for your app code, server code, `shared/` folder, and builder code. This should mean better autocompletion, more accurate type inference and fewer confusing errors when you're working in different contexts. ::tip With Nuxt 4, you will only need one `tsconfig.json` file in your project root! :: This is probably the single issue that is most likely to cause surprises when upgrading, but it should also make your TypeScript experience much smoother in the long run. Please report any issues you encounter. 🙏 ### ⚡ Faster CLI and development In parallel with the release of v4, we've been working on speeding up `@nuxt/cli`. - **Faster cold starts** - Development server startup is noticeably faster - **Node.js compile cache** - Automatic reuse of the v8 compile cache - **Native file watching** - Uses `fs.watch` APIs for fewer system resources - **Socket-based communication** - The CLI and Vite dev server now communicate via internal sockets instead of network ports, reducing overhead — particularly on Windows These improvements combined can make a really noticeable difference in your day-to-day development experience, and we have more planned. ## 🚀 How to upgrade Although any major release brings breaking changes, one of our main aims for this release is to ensure that the upgrade path is as smooth as possible. Most of the breaking changes have been testable with a compatibility flag for over a year. Most projects should upgrade smoothly, but there are a few things to be aware of: - Nuxt 2 compatibility has been removed from `@nuxt/kit`. (This will particularly affect module authors.) - Some legacy utilities and deprecated features have been cleaned up. - The new TypeScript setup might surface some type issues that were hidden before. - A few modules might need further updates for full Nuxt 4 compatibility. Don't worry though — for most breaking changes, there are configuration options to revert to the old behavior while you adjust. ### 1. Update Nuxt Our recommendation for upgrading is to run: ```sh npx nuxt upgrade --dedupe ``` This will deduplicate your lockfile as well, and help ensure that you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ### 2. Optional: use migration tools We’ve also partnered with [Codemod](https://github.com/codemod-com/codemod) to automate many, though not all, migration steps: ::code-group ```bash [npm] npx codemod@latest nuxt/4/migration-recipe ``` ```bash [yarn] yarn dlx codemod@latest nuxt/4/migration-recipe ``` ```bash [pnpm] pnpm dlx codemod@latest nuxt/4/migration-recipe ``` ```bash [bun] bun x codemod@latest nuxt/4/migration-recipe ``` :: ### 3. Test and adjust Run your tests, check that everything builds correctly, and fix any issues that come up. The [upgrade guide](https://nuxt.com/docs/4.x/getting-started/upgrade) has detailed migration steps for specific scenarios. We'd recommend reading through it in full before starting your upgrade, to understand what areas of your app might be affected. ## 🗺️ What's next? We're planning quick patch releases to address any issues that come up. Nuxt 3 will continue to receive maintenance updates (both bug fixes and backports of features from Nuxt 4) until the end of January 2026, so there's no rush if you need time to migrate. Looking ahead, we plan to release Nuxt 5 on the sooner side, which will bring Nitro v3 and h3 v2 for even better performance, as well as adopting the Vite Environment API for an improved (and faster!) development experience. And there's a lot more in the works too! And, quite apart from major releases, we have a lot of exciting features planned to make their way into Nuxt 3.x and 4.x release branches, including support for SSR streaming ([#4753](https://github.com/nuxt/nuxt/issues/4753)), a first-party accessibility module ([#23255](https://github.com/nuxt/nuxt/issues/23255)), built-in fetch caching strategies ([#26017](https://github.com/nuxt/nuxt/issues/26017)), more strongly typed fetch calls (landing in Nitro v3), dynamic route discovery ([#32196](https://github.com/nuxt/nuxt/issues/32196)), multi-app support ([#21635](https://github.com/nuxt/nuxt/issues/21635)) and more. ## ❤️ Thank you This release is credit to so many people, particularly those who have been testing v4 compatibility mode over the past year. I'm really grateful — thank you for all your help! Happy coding with Nuxt 4! 🚀 ## 📑 Full release notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v4.0.0"} Read the full release notes of Nuxt `v4.0.0`. :: A huge thank you to everyone who's been a part of this release. ❤️ # Nuxt 3.18 > Nuxt 3.18 is out - bringing v4 features to v3, improved accessibility, better browser dev tooling integration, and performance enhancements! ## 🧪 Lazy Hydration Macros Building on the delayed hydration support from v3.16, we now support **lazy hydration macros** ([#31192](https://github.com/nuxt/nuxt/pull/31192))! These provide a more ergonomic way to control component hydration: ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'visible', () => import('./components/MyComponent.vue') ) </script> <template> <div> <!-- Hydration will be triggered when the element(s) is 100px away from entering the viewport. --> <LazyHydrationMyComponent :hydrate-on-visible="{ rootMargin: '100px' }" /> </div> </template> ``` These macros make it possible to use Nuxt's lazy hydration utilities alongside explicit component imports. ## ♿️ Accessibility Improvements We've enhanced accessibility by including `<NuxtRouteAnnouncer>` in the built-in `app.vue` ([#32621](https://github.com/nuxt/nuxt/pull/32621)). This means page changes will be announced to screen readers, making navigation more accessible for users with visual impairments. (This only applies if you do not have an `app.vue` in your project. If you do, please keep `<NuxtRouteAnnouncer>` in your `app.vue`!) ## 🛠️ Enhanced Development Experience ### Chrome DevTools Workspace Integration We've added **Chrome DevTools workspace integration** ([#32084](https://github.com/nuxt/nuxt/pull/32084)), allowing you to edit your Nuxt source files directly from Chrome DevTools. This creates a better debugging experience where changes made in DevTools are reflected in your actual source files. ### Better Component Type Safety Component type safety has been improved with: - **Typed slots for `<ClientOnly>` and `<DevOnly>`** ([#32707](https://github.com/nuxt/nuxt/pull/32707)) - better IntelliSense and error checking - **Exported `<NuxtTime>` prop types** ([#32547](https://github.com/nuxt/nuxt/pull/32547)) - easier to extend and customize ### New Auto-Import: `onWatcherCleanup` The `onWatcherCleanup` function from `vue` is now available as an auto-import ([#32396](https://github.com/nuxt/nuxt/pull/32396)), making it easier to clean up watchers and prevent memory leaks: ```ts const { data } = useAsyncData('users', fetchUsers) watch(data, (newData) => { const interval = setInterval(() => { // Some periodic task }, 1000) // Clean up when the watcher is stopped onWatcherCleanup(() => { clearInterval(interval) }) }) ``` ## 📊 Observability Enhancements Page routes are now **exposed to Nitro for observability** ([#32617](https://github.com/nuxt/nuxt/pull/32617)), enabling better monitoring and analytics integration with supported platforms. This allows observability tools to track page-level metrics more effectively. ## 🔧 Module Development Improvements Module authors get several quality-of-life improvements: ### Simplified Server Imports The `addServerImports` kit utility now **supports single imports** ([#32289](https://github.com/nuxt/nuxt/pull/32289)), making it easier to add individual server utilities: ```ts // Before: had to wrap in array addServerImports([{ from: 'my-package', name: 'myUtility' }]) // Now: can pass directly addServerImports({ from: 'my-package', name: 'myUtility' }) ``` ### TypeScript Configuration Modules can now **add to `typescript.hoist`** ([#32601](https://github.com/nuxt/nuxt/pull/32601)), giving them more control over TypeScript configuration and type generation. ## ⚡️ Performance Improvements We've made several performance optimizations: - **Improved Vite-node communication** via internal socket ([#32417](https://github.com/nuxt/nuxt/pull/32417)) for faster development builds - **Migration to `oxc-walker`** ([#32250](https://github.com/nuxt/nuxt/pull/32250)) and **oxc for `onPrehydrate` transforms** ([#32045](https://github.com/nuxt/nuxt/pull/32045)) for faster code transformations ## 🐛 Bug Fixes This release also includes several important fixes: - **Improved data fetching**: When computed keys change, old data is now properly retained ([#32616](https://github.com/nuxt/nuxt/pull/32616)) - **Better scroll behavior**: `scrollBehaviorType` is now only used for hash scrolling ([#32622](https://github.com/nuxt/nuxt/pull/32622)) - **Fixed directory aliases**: Added trailing slashes to some directory aliases for better consistency ([#32755](https://github.com/nuxt/nuxt/pull/32755)) ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxi@latest upgrade --dedupe ``` This refreshes your lockfile and pulls in all the latest dependencies that Nuxt relies on, especially from the unjs ecosystem. ## Full release notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.18.0"} Read the full release notes of Nuxt `v3.18.0`. :: A huge thank you to everyone who's been a part of this release. Over the next six months, we'll continue backporting compatible v4 features and bug fixes, so please keep the feedback coming! ❤️ # Nuxt 4.1 > Nuxt 4.1 is out - bringing enhanced build stability, better development experience, and powerful new features for module authors! ## 🔥 Build and Performance Improvements ### 🍫 Enhanced Chunk Stability Build stability has been significantly improved with import maps ([#33075](https://github.com/nuxt/nuxt/pull/33075)). This prevents cascading hash changes that could invalidate large portions of your build when small changes are made: ```html <!-- Automatically injected import map --> <script type="importmap">{"imports":{"#entry":"/_nuxt/DC5HVSK5.js"}}</script> ``` By default, JS chunks emitted in a Vite build are hashed, which means they can be cached immutably. However, this can cause a significant issue: a change to a single component can cause *every* hash to be invalidated, massively increasing the chance of 404s. In short: 1. a component is changed slightly - the hash of its JS chunk changes 2. the page which uses the component has to be updated to reference the new file name 3. the *entry* now has its hash changed because it dynamically imports the page 4. ***every other file*** which imports the entry has its hash changed because the entry file name is changed Obviously this wasn't optimal. With this new feature, the hash of (otherwise) unchanged files which import the entry won't be affected. This feature is automatically enabled and helps maintain better cache efficiency in production. It does require [native import map support](https://caniuse.com/import-maps), but Nuxt will automatically disable it if you have configured `vite.build.target` to include a browser that doesn't support import maps. And of course you can disable it if needed: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { entryImportMap: false } }) ``` ### 🦀 Experimental Rolldown Support Nuxt now includes experimental support for `rolldown-vite` ([#31812](https://github.com/nuxt/nuxt/pull/31812)), bringing Rust-powered bundling for potentially faster builds. To try Rolldown in your Nuxt project, you need to override Vite with the rolldown-powered version since Vite is a dependency of Nuxt. Add the following to your `package.json`: ::code-group{sync="pm"} ```json [npm] { "overrides": { "vite": "npm:rolldown-vite@latest" } } ``` ```json [pnpm] { "pnpm": { "overrides": { "vite": "npm:rolldown-vite@latest" } } } ``` ```json [yarn] { "resolutions": { "vite": "npm:rolldown-vite@latest" } } ``` ```json [bun] { "overrides": { "vite": "npm:rolldown-vite@latest" } } ``` :: After adding the override, reinstall your dependencies. Nuxt will automatically detect when Rolldown is available and adjust its build configuration accordingly. For more details on Rolldown integration, see the [Vite Rolldown guide](https://vite.dev/guide/rolldown). ::note This is experimental and may have some limitations, but offers a glimpse into the future of high-performance bundling in Nuxt. :: ## 🧪 Improved Lazy Hydration Lazy hydration macros now work without auto-imports ([#33037](https://github.com/nuxt/nuxt/pull/33037)), making them more reliable when component auto-discovery is disabled: ```vue <script setup> // Works even with components: false const LazyComponent = defineLazyHydrationComponent( 'visible', () => import('./MyComponent.vue') ) </script> ``` This ensures that components that are not "discovered" through Nuxt (e.g., because `components` is set to `false` in the config) can still be used in lazy hydration macros. ## 📄 Enhanced Page Rules If you have enabled experimental extraction of route rules, these are now exposed on a dedicated `rules` property on `NuxtPage` objects ([#32897](https://github.com/nuxt/nuxt/pull/32897)), making them more accessible to modules and improving the overall architecture: ```ts // In your module nuxt.hook('pages:extend', pages => { pages.push({ path: '/api-docs', rules: { prerender: true, cors: true, headers: { 'Cache-Control': 's-maxage=31536000' } } }) }) ``` The `defineRouteRules` function continues to work exactly as before, but now provides better integration possibilities for modules. ## 🚀 Module Development Enhancements ### 🪾 Module Dependencies and Integration Modules can now specify dependencies and modify options for other modules ([#33063](https://github.com/nuxt/nuxt/pull/33063)). This enables better module integration and ensures proper setup order: ```ts export default defineNuxtModule({ meta: { name: 'my-module', }, moduleDependencies: { 'some-module': { // You can specify a version constraint for the module version: '>=2', // By default moduleDependencies will be added to the list of modules // to be installed by Nuxt unless `optional` is set. optional: true, // Any configuration that should override `nuxt.options`. overrides: {}, // Any configuration that should be set. It will override module defaults but // will not override any configuration set in `nuxt.options`. defaults: {} } }, setup (options, nuxt) { // Your module setup logic } }) ``` This replaces the deprecated `installModule` function and provides a more robust way to handle module dependencies with version constraints and configuration merging. ### 🪝 Module Lifecycle Hooks Module authors now have access to two new lifecycle hooks: `onInstall` and `onUpgrade` ([#32397](https://github.com/nuxt/nuxt/pull/32397)). These hooks allow modules to perform additional setup steps when first installed or when upgraded to a new version: ```ts export default defineNuxtModule({ meta: { name: 'my-module', version: '1.0.0', }, onInstall(nuxt) { // This will be run when the module is first installed console.log('Setting up my-module for the first time!') }, onUpgrade(inlineOptions, nuxt, previousVersion) { // This will be run when the module is upgraded console.log(`Upgrading my-module from v${previousVersion}`) } }) ``` The hooks are only triggered when both `name` and `version` are provided in the module metadata. Nuxt uses the `.nuxtrc` file internally to track module versions and trigger the appropriate hooks. (If you haven't come across it before, the `.nuxtrc` file should be committed to version control.) ::tip This means module authors can begin implementing their own 'setup wizards' to provide a better experience when some setup is required after installing a module. :: ### 🙈 Enhanced File Resolution The new `ignore` option for `resolveFiles` ([#32858](https://github.com/nuxt/nuxt/pull/32858)) allows module authors to exclude specific files based on glob patterns: ```ts // Resolve all .vue files except test files const files = await resolveFiles(srcDir, '**/*.vue', { ignore: ['**/*.test.vue', '**/__tests__/**'] }) ``` ### 📂 Layer Directories Utility A new `getLayerDirectories` utility ([#33098](https://github.com/nuxt/nuxt/pull/33098)) provides a clean interface for accessing layer directories without directly accessing private APIs: ```ts import { getLayerDirectories } from '@nuxt/kit' const layerDirs = await getLayerDirectories(nuxt) // Access key directories: // layerDirs.app - /app/ by default // layerDirs.appPages - /app/pages by default // layerDirs.server - /server by default // layerDirs.public - /public by default ``` ## ✨ Developer Experience Improvements ### 🎱 Simplified Kit Utilities Several kit utilities have been improved for better developer experience: - `addServerImports` now supports single imports ([#32289](https://github.com/nuxt/nuxt/pull/32289)): ```ts // Before: required array addServerImports([{ from: 'my-package', name: 'myUtility' }]) // Now: can pass directly addServerImports({ from: 'my-package', name: 'myUtility' }) ``` ### 🔥 Performance Optimizations This release includes several internal performance optimizations: - Improved route rules cache management ([#32877](https://github.com/nuxt/nuxt/pull/32877)) - Optimized app manifest watching ([#32880](https://github.com/nuxt/nuxt/pull/32880)) - Better TypeScript processing for page metadata ([#32920](https://github.com/nuxt/nuxt/pull/32920)) ## 🐛 Notable Fixes - Improved `useFetch` hook typing ([#32891](https://github.com/nuxt/nuxt/pull/32891)) - Better handling of TypeScript expressions in page metadata ([#32902](https://github.com/nuxt/nuxt/pull/32902), [#32914](https://github.com/nuxt/nuxt/pull/32914)) - Enhanced route matching and synchronization ([#32899](https://github.com/nuxt/nuxt/pull/32899)) - Reduced verbosity of Vue server warnings in development ([#33018](https://github.com/nuxt/nuxt/pull/33018)) - Better handling of relative time calculations in `<NuxtTime>` ([#32893](https://github.com/nuxt/nuxt/pull/32893)) ## ✅ Upgrading As usual, our recommendation for upgrading is to run: ```sh npx nuxt upgrade --dedupe ``` This will refresh your lockfile and pull in all the latest dependencies that Nuxt relies on, especially from the unjs ecosystem. ## 📦 Nuxt 3.19 All of these features are also available in **Nuxt 3.19**, which has been released alongside v4.1. As part of our commitment to the v3 branch, we continue to backport compatible v4 features to ensure v3 users can benefit from the latest improvements. If you're still using Nuxt 3, you can upgrade to v3.19 to get access to all these features while staying on the stable v3 release line. ## Full release notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v4.1.0"} Read the full release notes of Nuxt `v4.1.0`. :: ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.19.0"} Read the full release notes of Nuxt `v3.19.0`. :: Thank you to everyone who contributed! We're excited to see what you build with these new features. ❤️ # Nuxt UI v4 > Nuxt UI v4 unifies Nuxt UI and Nuxt UI Pro into one powerful, completely free library. With over 110 components, 12 templates, and a comprehensive Figma kit, all of this is available for free. Today, we’re releasing [**Nuxt UI v4**](https://ui.nuxt.com), a major milestone that sets a new standard for our component library. With this release, we are unifying Nuxt UI and Nuxt UI Pro into a single, powerful, and completely free open-source library. This marks an exciting new chapter for the Vue and Nuxt ecosystems, made possible by [NuxtLabs joining Vercel](https://nuxtlabs.com). Our shared commitment to the open-source community has allowed us to make every component, from a [simple button](https://ui.nuxt.com/docs/components/button) to the most advanced [dashboard sidebar](https://ui.nuxt.com/docs/components/dashboard-sidebar), accessible to everyone. What was previously a premium offering is now available to all. Over 100 components, advanced sections, and [production-ready templates](https://ui.nuxt.com/templates) are now yours to build with, unified in one place. ## Build anything, faster than ever Nuxt UI v4 makes it easy to build modern, polished apps quickly. You can create complex interfaces like landing pages, pricing pages, docs, blogs, portfolios, etc. without starting from scratch. This is possible because Nuxt UI v4 unifies everything you need into a single `@nuxt/ui` package: - **110+ components:** An extensive library to build anything from simple websites to complex applications. - **12 free templates:** Start your next project in minutes with a production-ready template for a [Landing Page](https://landing-template.nuxt.dev), [SaaS](https://saas-template.nuxt.dev), [Dashboard](https://dashboard-template.nuxt.dev), [Docs site](https://docs-template.nuxt.dev), [Portfolio](https://portfolio-template.nuxt.dev), [Chat app](https://chat-template.nuxt.dev), or [Changelog](https://changelog-template.nuxt.dev). - **Rich Content & Typography:** Beautifully render Markdown and build content-rich sites with our advanced prose components, fully integrated with Nuxt Content. - **Vue and Nuxt Compatibility:** Works in any Vue or Nuxt project, as well as [Adonis](https://github.com/nuxt-ui-templates/starter-adonis) and [Laravel](https://github.com/nuxt-ui-templates/starter-laravel). The entire Pro suite is now yours. Build with powerful components previously exclusive to our paid users, now free for everyone. ::tabs{.gap-0} :::div{label="index.vue"} ```vue <template> <UApp> <UHeader> <UNavigationMenu :items="navigation" /> <template #right> <UColorModeButton /> <UButton icon="i-simple-icons-github" /> </template> </UHeader> <UPageHero title="Nuxt UI - Starter" description="Nuxt UI is a free and open-source UI library for Nuxt applications. Create beautiful & responsive applications in minutes." :links="heroLinks" /> <UPageSection title="The freedom to build anything" description="Nuxt UI ships with a comprehensive set of components that cover a wide range of use-cases. Carefully crafted to reduce boilerplate code without sacrificing flexibility." :features="features" /> <UPageSection title="Pricing"> <UPricingPlans :plans="plans" /> </UPageSection> <UPageSection> <UPageCTA title="Start with Nuxt UI today!" description="Nuxt UI is a free and open-source UI library for Nuxt applications." variant="subtle" :links="ctaLinks" /> </UPageSection> <UFooter :items="footerItems" /> </UApp> </template> ``` ::: :::code-preview ```yaml [props] label: Preview class: '[&>div]:*:my-0 w-full' ui: preview: p-0 ``` ::::index-example :::: ::: :: ## From design to code, seamlessly A successful project starts with a solid design system. In v4, we are releasing the **complete Figma Kit** for free, mirroring the entire component library. With over **2,000 component variants and design tokens**, you now have a single Figma entry point that contains every component along with detailed explanations about structure and usage. Designers and developers work from the same comprehensive source, making collaboration seamless and ensuring a perfect match between design and implementation. ::u-button ```yaml [props] to: https://go.nuxt.com/figma-ui target: _blank icon: i-simple-icons-figma label: Get the Figma Kit → class: mb-4 ``` :: ::carousel ```yaml [props] items: - /assets/blog/figma-kit/1.png - /assets/blog/figma-kit/2.png - /assets/blog/figma-kit/3.png ``` :: ## An upgraded developer experience Beyond new components, v4 brings significant improvements to your workflow. ### Effortless migration Unlike the major overhaul from v2 to v3, the migration to v4 is simple. This release focuses on unification, not breaking changes. Most components work identically, and your existing code will remain largely intact. Check out our [migration guide](https://ui.nuxt.com/getting-started/migration/v4) for a complete walkthrough. ### A refined documentation We've completely overhauled our documentation to make it clearer and more intuitive. We’ve restructured the layout and split complex topics into dedicated pages, ensuring you can find the information you need, faster. Our documentation is also now fully AI-ready. It's powered by our new **[Model Context Protocol (MCP) server](https://ui.nuxt.com/docs/getting-started/ai/mcp)**, which allows AI tools like Cursor to access component documentation and metadata directly. In addition, we provide **[`LLMs.txt` files](https://ui.nuxt.com/docs/getting-started/ai/llms-txt)**, a structured format that enables any AI assistant to understand our components, theming, and best practices. Your AI tools now have first-class access to our entire library, right inside your editor. ### Ready for the next wave of AI Our AI chat components now support **Vercel's AI SDK v5**. The new `Chat` class and message format (with `parts`) ensure compatibility with the latest AI SDK improvements, keeping you at the forefront of AI development. ## A thank you to our pro users We want to extend a special thanks to everyone who supported Nuxt UI Pro. Your early adoption and feedback were instrumental in shaping Nuxt UI. You helped us fund, maintain, and improve the project, allowing us to reach this milestone where we can now offer these powerful tools to the entire community. Your support made this moment possible. ## Start building today Get started with Nuxt UI v4 by creating a new project with any of our free templates: ::code-group ```bash [Starter] npm create nuxt@latest -- -t ui ``` ```bash [Landing] npm create nuxt@latest -- -t github:nuxt-ui-templates/landing ``` ```bash [Docs] npm create nuxt@latest -- -t github:nuxt-ui-templates/docs ``` ```bash [SaaS] npm create nuxt@latest -- -t github:nuxt-ui-templates/saas ``` ```bash [Dashboard] npm create nuxt@latest -- -t github:nuxt-ui-templates/dashboard ``` ```bash [Chat] npm create nuxt@latest -- -t github:nuxt-ui-templates/chat ``` ```bash [Portfolio] npm create nuxt@latest -- -t github:nuxt-ui-templates/portfolio ``` ```bash [Changelog] npm create nuxt@latest -- -t github:nuxt-ui-templates/changelog ``` :: Or add it to your existing project: ```bash npm install @nuxt/ui@latest ``` ## The future is open With a unified codebase and the backing of Vercel, we're more excited than ever to push the boundaries of component development. This release wouldn't have been possible without the support of our amazing community, and every contributor who has helped shape Nuxt UI. We can't wait to see what you build. The future of UI development is free, open, and more powerful than ever. --- *Ready to get started? Check out the [documentation](https://ui.nuxt.com) and join our [Discord community](https://discord.nuxt.com) to connect with other developers building amazing things with Nuxt UI.* # Building an MCP Server for Nuxt > How we built the Nuxt MCP server to enable AI assistants to access our documentation through structured data and composable tools. AI assistants are becoming an increasingly important part of the developer experience. To help them provide accurate, up-to-date information about Nuxt, we built an MCP server that exposes our documentation, blog posts, and deployment guides in a structured way. Here's how we did it with the [Nuxt MCP Toolkit](https://mcp-toolkit.nuxt.dev), and how you can build your own. ::callout{icon="i-lucide-bot"} Want to test the Nuxt MCP server? Jump to the [Nuxt MCP Server Documentation](https://nuxt.com/docs/4.x/guide/ai/mcp). :: ## What is MCP and why did we build it? The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open standard that enables AI assistants to securely access data and tools. Think of it as an API specifically designed for AI assistants: rather than returning HTML or generic JSON, it provides structured, semantic data that LLMs can easily understand and use. MCP defines three main primitives: - **Resources**: Allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information. Each resource is uniquely identified by a URI. - **Tools**: Enable AI models to interact with external systems and perform operations (like searching or API calls) - **Prompts**: Reusable prompt templates with arguments that can be invoked by users ### Why MCP over RAG? We've observed that AI assistants using MCP servers provide **significantly better responses** than traditional RAG (Retrieval-Augmented Generation) approaches: - **Structured data in, structured data out**: Tools accept well-defined parameters and return typed data that prevents hallucinations - **Composable tools**: AI assistants can chain tools together, using the output of one tool as input for another (e.g., search for a topic, then fetch the full content) - **Faster and more accurate**: No need to process and chunk large documents at query time - **Always up-to-date**: Direct access to your content layer without reindexing - **Context-aware navigation**: The AI can intelligently navigate relationships between content Both [Nuxt](https://nuxt.com/mcp) and [Nuxt UI](https://ui.nuxt.com/mcp) now have MCP servers with similar architectures, making it easier for AI assistants to help developers with these frameworks. ## Technical architecture Our MCP server is built directly into [nuxt.com](http://nuxt.com) using the [Nuxt MCP Toolkit](https://mcp-toolkit.nuxt.dev) module. The module provides automatic discovery of tools, resources, and prompts from your server directory: ``` nuxt.com/ ├── server/ │ └── mcp/ │ ├── tools/ │ │ ├── docs/ │ │ │ ├── list-documentation-pages.ts │ │ │ ├── get-documentation-page.ts │ │ │ └── ... │ │ ├── blog/ │ │ ├── modules/ │ │ └── ... │ ├── resources/ │ │ ├── docs/ │ │ │ └── nuxt-documentation-pages.ts │ │ └── ... │ └── prompts/ │ ├── docs/ │ │ └── find-documentation-for-topic.ts │ └── ... └── nuxt.config.ts ``` The architecture is straightforward: you define your tools, resources, and prompts as individual files, and the module automatically registers them and exposes an HTTP endpoint for MCP clients to connect. No manual server setup, no transport configuration, just create files in the right directories and they're ready to use. Subdirectories like `tools/docs/` are optional and only help you organize files: the tool name still comes from the filename. ## Implementation deep dive ### Module setup Getting started is as simple as adding the module to your Nuxt config: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxtjs/mcp-toolkit'], mcp: { name: 'Nuxt', } }) ``` That's it. The module will automatically scan your `server/mcp/` directory and register everything it finds. ### Tools: Operations for AI models Tools enable language models to interact with external systems by accepting parameters and performing operations. Here's a simplified version of our `list_documentation_pages` tool: ```ts [server/mcp/tools/docs/list-documentation-pages.ts] import { z } from 'zod' import { queryCollection } from '@nuxt/content/server' export default defineMcpTool({ description: `Lists Nuxt documentation pages, optionally filtered by search term. WHEN TO USE: When you need to find documentation about a topic but don't know the exact page path. WHEN NOT TO USE: If you already know the page path, use get_documentation_page directly.`, inputSchema: { version: z.enum(['3.x', '4.x']).optional().default('4.x').describe('Documentation version to fetch'), search: z.string().optional().describe('Filter pages by keyword (matches title, path, and description)') }, annotations: { readOnlyHint: true, openWorldHint: false }, cache: '1h', async handler({ version, search }) { const event = useEvent() let docs = await queryCollection(event, version === '3.x' ? 'docsv3' : 'docsv4') .select('title', 'path', 'description') .all() if (search) { const terms = search.toLowerCase().split(/\s+/) docs = docs.filter((doc) => { const haystack = `${doc.title} ${doc.path} ${doc.description}`.toLowerCase() return terms.every(term => haystack.includes(term)) }) } return docs.map(doc => ({ title: doc.title, path: doc.path, description: doc.description, url: `https://nuxt.com${doc.path}` })) } }) ``` Notice a few key things: - **`defineMcpTool`** is auto-imported, no need to import it manually - **`inputSchema`** uses Zod for parameter validation - **`annotations`** tell clients the tool only reads data, so they can call it without asking for confirmation - **`cache: '1h'`** enables built-in response caching - **The handler returns plain data**: arrays and objects are serialized to JSON for you, and strings are sent as text. To fail, throw an error (for example with `createError()`) instead of returning one. The [live tool](https://github.com/nuxt/nuxt.com/blob/main/server/mcp/tools/docs/list-documentation-pages.ts) does more: it also covers Nuxt 5, can search every version at once, includes CLI and example pages, and only returns descriptions when a search term is given, to keep responses small. The tool name is automatically derived from the filename (`list-documentation-pages.ts` becomes `list_documentation_pages`). ### Resources: Context for language models Resources allow servers to share data that provides context to language models, such as files, database schemas, or application-specific information. Each resource is uniquely identified by a URI. The simplest way to expose a file is using the `file` property, which automatically handles URI generation, MIME type detection, and file reading: ```ts [server/mcp/resources/readme.ts] export default defineMcpResource({ name: 'readme', description: 'Project README file', file: 'README.md' // Relative to project root }) ``` For dynamic resources, you can use a custom handler: ```ts [server/mcp/resources/docs/nuxt-documentation-pages.ts] import { queryCollection } from '@nuxt/content/server' export default defineMcpResource({ uri: 'resource://nuxt-com/documentation-pages', description: 'Complete list of available Nuxt documentation pages (defaults to v4.x)', cache: '1h', async handler(uri: URL) { const event = useEvent() const allDocs = await queryCollection(event, 'docsv4') .select('title', 'path', 'description') .all() const result = allDocs.map(doc => ({ title: doc.title, path: doc.path, description: doc.description, version: '4.x', url: `https://nuxt.com${doc.path}` })) return { contents: [{ uri: uri.href, mimeType: 'application/json', text: JSON.stringify(result, null, 2) }] } } }) ``` Unlike tools, which are model-controlled, resources are application-driven, the host application determines how to incorporate them based on user needs, such as through UI elements for explicit selection or automatic context inclusion. ### Prompts: Reusable templates Prompts are reusable templates with arguments that users can invoke. They return a conversation format that guides the AI through specific workflows: ```ts [server/mcp/prompts/docs/find-documentation-for-topic.ts] import { z } from 'zod' import { queryCollection } from '@nuxt/content/server' export default defineMcpPrompt({ description: 'Find the best Nuxt documentation for a specific topic or feature', inputSchema: { topic: z.string().describe('Describe what you want to learn about'), version: z.enum(['3.x', '4.x']).optional().describe('Documentation version to search') }, async handler({ topic, version = '4.x' }) { const event = useEvent() const docsVersion = version === '4.x' ? 'docsv4' : 'docsv3' const allDocs = await queryCollection(event, docsVersion) .select('title', 'path', 'description') .all() const allPages = allDocs?.map(doc => ({ title: doc.title, path: doc.path, description: doc.description, url: `https://nuxt.com${doc.path}` })) || [] return { messages: [{ role: 'user' as const, content: { type: 'text' as const, text: `Help me find the best Nuxt documentation for this topic: "${topic}". Here are all available documentation pages: ${JSON.stringify(allPages, null, 2)}` } }] } } }) ``` Prompts differ from tools in that they are user-invoked and return conversation messages, while tools are model-controlled and return structured data. ### Built-in helpers The module provides several auto-imported helpers to simplify common patterns: - **`defineMcpTool`**, **`defineMcpResource`**, **`defineMcpPrompt`**: Define your MCP primitives - **`imageResult(data, mimeType)`** and **`audioResult(data, mimeType)`**: Return base64-encoded media from tools ::note Earlier versions of the module used `jsonResult()`, `textResult()` and `errorResult()` to shape tool responses. They still work but are deprecated: return the value directly from your handler, and throw errors instead of returning them. :: ### Using Nuxt server utilities To use Nuxt server utilities like `useEvent()` in your handlers, enable `asyncContext` in your Nuxt config: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { asyncContext: true } }) ``` Then you can access the H3 event and use Nuxt server composables like `queryCollection` from [Nuxt Content](https://content.nuxt.com). ## Building your own MCP server Ready to build an MCP server for your own application? With the Nuxt MCP Toolkit, it takes just a few minutes. ### 1. Install the module ```bash [Terminal] npx nuxt add mcp ``` Or install it manually along with Zod, which the module uses for input validation: ```bash [Terminal] npm install -D @nuxtjs/mcp-toolkit zod ``` ### 2. Configure the module Add basic configuration to your Nuxt config: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxtjs/mcp-toolkit'], mcp: { name: 'my-app', version: '1.0.0' } }) ``` ### 3. Create your first tool Create a file in `server/mcp/tools/`: ```ts [server/mcp/tools/search.ts] import { z } from 'zod' export default defineMcpTool({ description: 'Search through my content', inputSchema: { query: z.string().describe('Search query') }, async handler({ query }) { // Your search logic here return await searchContent(query) } }) ``` That's it! Your MCP server is now accessible at `https://your-domain.com/mcp`. ### 4. Add resources and prompts (optional) You can also add resources and prompts following the same pattern: ```ts [server/mcp/resources/readme.ts] export default defineMcpResource({ name: 'readme', description: 'Project README file', file: 'README.md' }) ``` For more advanced configuration options, check out the [Nuxt MCP Toolkit documentation](https://mcp-toolkit.nuxt.dev). ## Get started with the Nuxt MCP server Ready to experience the power of MCP with Nuxt? Our server is already live at `https://nuxt.com/mcp`. It gives AI assistants access to the Nuxt documentation (v3, v4 and v5), blog posts, the changelog, deployment providers, modules, and Nuxt contributors. ### Quick install for Cursor The easiest way to get started is with Cursor's one-click installation: ::u-button ```yaml [props] to: cursor://anysphere.cursor-deeplink/mcp/install?name=nuxt&config=eyJ0eXBlIjoiaHR0cCIsInVybCI6Imh0dHBzOi8vbnV4dC5jb20vbWNwIn0%3D label: Install Nuxt MCP Server in Cursor color: neutral icon: i-custom-cursor ``` :: ### Other AI assistants The Nuxt MCP server works with Claude Desktop, Windsurf, Visual Studio Code, ChatGPT, and many other MCP-compatible AI assistants. For complete setup instructions for all platforms, check out our [MCP documentation](https://nuxt.com/docs/4.x/guide/ai/mcp). ### Expose only the tools you need Every tool an assistant can see takes up room in its context. If you only need part of the server, send an `X-MCP-Tools` header with a comma-separated list of tool names, and the server will only expose those tools: ```json [.cursor/mcp.json] { "mcpServers": { "nuxt": { "type": "http", "url": "https://nuxt.com/mcp", "headers": { "X-MCP-Tools": "list_documentation_pages,get_documentation_page" } } } } ``` The server rejects unknown tool names, so a typo fails loudly instead of silently hiding a tool. The [MCP documentation](https://nuxt.com/docs/4.x/guide/ai/mcp) lists every available tool. This header is a feature of the Nuxt MCP Toolkit, so any server built with the module supports it too. We encourage you to build MCP servers for your own applications. Whether it's documentation, API references, or domain-specific knowledge, MCP makes it easy for AI assistants to provide accurate, helpful information to your users. The complete source code for our MCP server is available on [GitHub](https://github.com/nuxt/nuxt.com) in the [`server/mcp/`](https://github.com/nuxt/nuxt.com/tree/main/server/mcp) directory. Feel free to use it as inspiration for your own implementation! # Nuxt 4.2 > Nuxt 4.2 is out - with experimental TypeScript plugin support, better error handling in development, abort control for data fetching, and more! We're excited to announce Nuxt 4.2, bringing new capabilities for better TypeScript DX, enhanced error handling, and improved control over data fetching! 🎉 ## 🎯 Abort Control for Data Fetching You can now use `AbortController` signals directly within `useAsyncData`, giving you fine-grained control over request cancellation ([#32531](https://github.com/nuxt/nuxt/pull/32531)). This works by passing an internal signal to your `useAsyncData` `handler` to cancel any promise that can be canceled, such as `$fetch`. ```vue <script setup lang="ts"> const { data, error, clear, refresh } = await useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal })) refresh() // will actually cancel the $fetch request (if dedupe: cancel) clear() // will cancel the latest pending handler </script> ``` You also pass an `AbortController` signal directly to `refresh`/`execute`, giving you fine-grained control over request cancellation. This is particularly useful when you need to abort requests based on user actions or component lifecycle events. ```ts const { data, refresh } = await useAsyncData('posts', fetchPosts) // Abort an ongoing refresh const abortController = new AbortController() refresh({ signal: abortController.signal }) // Later... abortController.abort() ``` ## 🎨 Better Error Pages in Development When an error occurs during development, Nuxt will now display both your custom error page *and* a detailed technical error overlay ([#33359](https://github.com/nuxt/nuxt/pull/33359)). This gives you the best of both worlds – you can see what your users will experience while also having immediate access to stack traces and debugging information. ![Screenshot of the new development error page](https://nuxt.com/assets/blog/nuxt-error-page.png) The technical overlay appears as a toggleable panel that doesn't interfere with your custom error page, making it easier to debug issues while maintaining a realistic preview of your error handling. ## 🔮 Opt-in Vite Environment API For those wanting to experiment with cutting-edge features, you can now opt into the [Vite Environment API](https://vite.dev/guide/api-environment) ([#33492](https://github.com/nuxt/nuxt/pull/33492)). The Vite Environment API is a major architectural improvement in Vite 6. It closes the gap between development and production by allowing the Vite dev server to handle multiple environments concurrently (rather than requiring multiple Vite dev servers, as we have done previously in Nuxt). This should improve performance when developing and eliminate some edge case bugs. ... and it is the foundation for implementing Nitro as a Vite environment, which should speed up the dev server still further, as well as allowing more greater alignment in development with your Nitro preset. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { viteEnvironmentApi: true } }) ``` This is also the first breaking change for Nuxt v5. You can opt in to these breaking changes by setting `compatibilityVersion` to `5`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ future: { compatibilityVersion: 5 }, }) ``` Please only use this for testing, as this opts in to unlimited future breaking changes, including updating to Nitro v3 once we ship the Nuxt integration. ::callout{type="warning"} This is highly experimental and the API may change. Only enable if you're prepared for potential breaking changes and want to help shape the future of Nuxt! :: ## 📦 New `@nuxt/nitro-server` Package We've extracted Nitro server integration into its own package: `@nuxt/nitro-server` ([#33462](https://github.com/nuxt/nuxt/pull/33462)). This architectural change allows for different Nitro integration patterns and paves the way for future innovations in server-side rendering. While this change is mostly internal, it's part of our ongoing effort to make Nuxt more modular and flexible. The new package provides standalone Nitro integration and sets the foundation for alternative integration approaches (such as using Nitro as a Vite plugin in Nuxt v5+). ::callout{type="info"} This is an internal refactor – no changes should be required in your code. :: ## ⚡ Performance Improvements We've also shipped several performance enhancements: - **Precomputed renderer dependencies** – We now compute renderer dependencies at build time rather than runtime, improving cold start and initial render performance ([#33361](https://github.com/nuxt/nuxt/pull/33361)) - **Reduced dependencies** – Removed unnecessary dependencies from kit and schema packages ([7ae2cf563](https://github.com/nuxt/nuxt/commit/7ae2cf563)) ### 📉 Async Data Handler Extraction One of the most exciting performance improvements is the new experimental async data handler extraction ([#33131](https://github.com/nuxt/nuxt/pull/33131)). When enabled, handler functions passed to `useAsyncData` and `useLazyAsyncData` are automatically extracted into separate chunks and dynamically imported. This is **particularly effective for prerendered static sites**, as the data fetching logic is only needed at build time and can be completely excluded from the client bundle. ::callout{type="info"} In testing with a previous version of [nuxt.com](http://nuxt.com), this feature **reduced JavaScript bundle size by 39%**! Of course, your mileage may vary depending on how much data fetching logic you have. :: ```vue [pages/blog/[slug\\].vue] <script setup lang="ts"> // This handler will be extracted into a separate chunk // and only loaded when needed const { data: post } = await useAsyncData('post', async () => { const content = await queryContent(`/blog/${route.params.slug}`).findOne() // Complex data processing that you don't want in the client bundle const processed = await processMarkdown(content) const related = await findRelatedPosts(content.tags) return { ...processed, related } }) </script> ``` For static/prerendered sites, enable it in your config: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { extractAsyncDataHandlers: true } }) ``` The extracted handlers are then tree-shaken from your client bundle when prerendering, as the data is already available in the payload. This results in significantly smaller JavaScript files shipped to your users. ## 🔧 Experimental TypeScript Plugin Support We're introducing experimental support for enhanced TypeScript developer experience through the [`@dxup/nuxt`](https://github.com/KazariEX/dxup) module. This module adds a number of TypeScript plugins that aim to improve your experience when using Nuxt-specific features: - **Smart component renaming**: Automatically updates all references when you rename auto-imported component files - **Go to definition for dynamic imports**: Navigate directly to files when using glob patterns like <code>import(\`\~/assets/${name}.webp\`)</code> - **Nitro route navigation**: Jump to server route handlers from data fetching functions (`$fetch`, `useFetch`, `useLazyFetch`) - **Runtime config navigation**: Go to definition works seamlessly with runtime config properties - **Enhanced auto-import support**: Includes the [`@dxup/unimport`](https://github.com/KazariEX/dxup/tree/main/packages/unimport) plugin for better navigation with auto-imported composables and utilities ::note{to="https://nuxt.com/docs/guide/going-further/experimental-features#typescriptplugin"} Read more in **[the documentation](https://nuxt.com/docs/guide/going-further/experimental-features#typescriptplugin)**. :: To enable this feature, set `experimental.typescriptPlugin` to `true` in your Nuxt configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { typescriptPlugin: true } }) ``` Once enabled, the module will be automatically installed and configured by Nuxt. ::warning This feature also requires selecting the workspace TypeScript version in VS Code. Run the "TypeScript: Select TypeScript Version" command and choose "Use Workspace Version". :: ## 🎁 Other Improvements - **Component `declarationPath`** – You can now specify a custom declaration path for components ([#33419](https://github.com/nuxt/nuxt/pull/33419)) - **Module resolution extensions** – Kit's `resolveModule` now accepts an `extensions` option ([#33328](https://github.com/nuxt/nuxt/pull/33328)) - **Global head utility** – New `setGlobalHead` utility in kit for easier head management ([#33512](https://github.com/nuxt/nuxt/pull/33512)) ## 🩹 Important Fixes - Route hash is now preserved when redirecting based on `routeRules` ([#33222](https://github.com/nuxt/nuxt/pull/33222)) - Fixed concurrent calls to `loadNuxtConfig` with proper cleanup ([#33420](https://github.com/nuxt/nuxt/pull/33420)) - Object-format `href` now works correctly in `<NuxtLink>` ([c69e4c30d](https://github.com/nuxt/nuxt/commit/c69e4c30d)) - Component auto-imports now work as arguments to Vue's `h()` function ([#33509](https://github.com/nuxt/nuxt/pull/33509)) - Fixed app config array handling during HMR ([#33555](https://github.com/nuxt/nuxt/pull/33555)) ## ✅ Upgrading Our recommendation for upgrading is to run: ```sh npx nuxt upgrade --dedupe ``` This will refresh your lockfile and pull in all the latest dependencies that Nuxt relies on, especially from the unjs ecosystem. ## 👉 Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v4.2.0"} Read the full release notes of Nuxt `v4.2.0`. :: Thank you for reading this far! We hope you enjoy the new release. Please do let us know if you have any feedback or issues. **Happy Nuxting ✨** # Nuxt Image v2 > Nuxt Image v2 is out - with full TypeScript support, IPX v3, and new providers! We're excited to announce **Nuxt Image v2**! 🎉 This release focuses on TypeScript support, performance improvements, and better developer experience. ::note Nuxt Image v2 works with Nuxt 3.1+. If you're on Nuxt 3.0.x, you'll need to upgrade to at least 3.1 first. :: ## 🎯 TypeScript support The biggest change in v2 is full TypeScript support throughout the module ([#1802](https://github.com/nuxt/image/pull/1802)). ### Typed composables The `$img` helper and `useImage()` composable have full type inference ([#1844](https://github.com/nuxt/image/pull/1844)): ```ts const img = useImage() // Full autocomplete for modifiers const url = img('/image.jpg', { width: 300, height: 200, fit: 'cover' // TypeScript knows the valid values! }) ``` ### Type-safe configuration Module options are now fully typed. For example, providers that require a `baseURL` will enforce it at the type level in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ image: { provider: 'bunny', bunny: { baseURL: '...' // TypeScript error if missing! } } }) ``` ### Typed providers Finally, if you are using custom image providers, you should use the new `defineProvider` for type-safe configuration: ```ts // Before (v1) export const getImage = (src, { modifiers, baseURL }) => { // ... return { url } } // After (v2) import { defineProvider } from '@nuxt/image/runtime' export default defineProvider({ getImage(src, { modifiers, baseURL }) { // Fully typed modifiers // ... return { url } } }) ``` ## 🚀 IPX v3 We've upgraded to IPX v3 ([#1799](https://github.com/nuxt/image/pull/1799)) for better performance and better `sharp` binary handling. The upgrade includes automatic detection of the correct `sharp` binaries for your deployment architecture. ## 🔌 Server-side utilities You can now use image helpers directly in Nitro server endpoints ([#1473](https://github.com/nuxt/image/pull/1473)). ```ts [server/api/og-image.ts] export default defineEventHandler((event) => { const img = useImage() return { url: img('/hero.jpg', { width: 1200, height: 630, fit: 'cover' }) } }) ``` ## 🎨 Component improvements ### Template refs `<NuxtImg>` now exposes the underlying `<img>` element via template refs: ```vue <script setup> const img = useTemplateRef('img') onMounted(() => { // Direct access to the native img element console.log(img.value.imgEl) }) </script> <template> <NuxtImg ref="img" src="/image.jpg" /> </template> ``` ### Typed slots Both `<NuxtImg>` and `<NuxtPicture>` now have properly typed default slots. ```vue <template> <NuxtImg src="/image.jpg" custom> <template #default="{ imgAttrs, isLoaded, src }"> <img v-bind="imgAttrs" :src="src"> <span v-if="!isLoaded">Loading...</span> </template> </NuxtImg> </template> ``` The slot provides: - `imgAttrs` - All computed image attributes (sizes, srcset, etc.) - `isLoaded` - Whether the placeholder has loaded - `src` - The computed image source URL ## 🌐 New providers ### Shopify You can now configure the Shopify provider ([#1890](https://github.com/nuxt/image/pull/1890)): ```ts [nuxt.config.ts] export default defineNuxtConfig({ image: { provider: 'shopify', shopify: { baseURL: 'https://your-store.myshopify.com' } } }) ``` ### GitHub This provider lets you inject GitHub avatars and user content ([#1990](https://github.com/nuxt/image/pull/1990)): ```vue <!-- Width and height --> <NuxtImg provider="github" src="nuxt" height="50" width="50" /> <!-- Width only --> <NuxtImg provider="github" src="unjs" width="512" /> <!-- Default size --> <NuxtImg provider="github" src="npm" /> ``` ## ⚡ Performance We've made several optimizations to reduce bundle size and improve runtime performance: - **Better URL encoding** ([#1813](https://github.com/nuxt/image/pull/1813)) - Switched to `URLSearchParams` for more reliable parameter handling - **Reduced runtime utilities** ([#1816](https://github.com/nuxt/image/pull/1816)) - Removed unused code and simplified implementations - **Streamlined screen sizes** ([#1931](https://github.com/nuxt/image/pull/1931)) - Aligned default breakpoints with Tailwind CSS ## 🎯 Better layer support Nuxt Image now properly supports custom image directories within Nuxt layers ([#1880](https://github.com/nuxt/image/pull/1880)), making it easier to organize images in modular projects. ## ⚠️ Breaking changes ### Provider API The biggest breaking change is how providers are defined. All providers now use a default export with the `defineProvider` wrapper: ```diff - export const getImage = (src, { modifiers }) => { ... } + export default defineProvider({ + getImage(src, { modifiers }) { ... } + }) ``` If you maintain a custom provider, you'll need to update it. But you get full TypeScript support in return! ### Removed providers The deprecated `layer0` and `edgio` providers have been removed. ### URL formatters If you have custom providers using `joinWith` for parameter formatting, you'll need to update them to use the `formatter` function with `createOperationsGenerator`. See the [migration guide](https://image.nuxt.com/get-started/migration#url-formatter-changes) for details. ### Screen sizes Default screen sizes now match Tailwind CSS. We've removed `xs` (320px) and `xxl` (2560px). See the [migration guide](https://image.nuxt.com/get-started/migration#screen-size-changes) for how to add them back if needed. ### Removed utilities We've removed several unused runtime utilities. If you were importing internal utilities directly, check if they still exist. ## ✅ Upgrading Check out our comprehensive [migration guide](https://image.nuxt.com/get-started/migration) for step-by-step upgrade instructions. The quick version: ```bash [Terminal] npm install @nuxt/image@latest ``` Most apps can upgrade with no code changes. If you have custom providers, you'll need to update them to use `defineProvider` - see the [migration guide](https://image.nuxt.com/get-started/migration#custom-provider-updates) for examples. ## 🐛 Bug fixes This release includes several fixes: - **Preload links**: Fixed preload for multiple densities with single size ([#1851](https://github.com/nuxt/image/pull/1851)) - **Crossorigin attributes**: Correct crossorigin on preload links ([#1836](https://github.com/nuxt/image/pull/1836)) - **Provider-specific formats**: AWS Amplify and Vercel providers now have proper format allow lists ([#1996](https://github.com/nuxt/image/pull/1996)) - **Hygraph**: Prevented broken image URLs ([#1999](https://github.com/nuxt/image/pull/1999)) - **Preset sizes**: Fixed preset size application when component sizes prop is undefined ([#1919](https://github.com/nuxt/image/pull/1919)) - **Cloudflare**: Don't add baseURL if there are no operations ([#1790](https://github.com/nuxt/image/pull/1790)) - **IPX**: Always use IPX provider if external baseURL is provided ([#1800](https://github.com/nuxt/image/pull/1800)) ## 🙏 Thank you Thank you to all the contributors who made this release possible! This includes contributions from dozens of community members who helped with features, bug fixes, documentation improvements, and feedback. ## 📚 Resources - [Documentation](https://image.nuxt.com) - [GitHub Repository](https://github.com/nuxt/image) - [Migration Guide](https://image.nuxt.com/get-started/migration) ## 👉 Full release notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/image/releases/tag/v2.0.0"} Read the full release notes of Nuxt Image `v2.0.0`. :: Happy optimizing! 🖼️✨ # Nuxt 4.3 > Nuxt 4.3 is out – route rule layouts, ISR payload extraction, draggable error overlay, and more! Nuxt 4.3 brings powerful new features for layouts, caching, and developer experience – plus significant performance improvements under the hood. ## 📣 Some News ### Extended v3 Support Early this month, I [opened a discussion](https://github.com/nuxt/nuxt/discussions/33918) to find out how the upgrade had gone from v3 to v4. I was really pleased to hear how well it had gone for most people. Having said that, we're committed to making sure no one gets left behind. And so we will **continue to provide security updates and critical bug fix releases** beyond the previously announced end-of-life date of January 31, 2026, meaning Nuxt v3 will meet its end-of-life on July 31, 2026. ::tip As usual, today also brings a minor release for v3, with many of the same improvements backported from v4.3. :: ### Preparing for Nuxt 5 We're closer than ever to the releases of Nuxt v5 and Nitro v3. In the coming weeks, the `main` branch of the Nuxt repository will begin receiving initial commits for Nuxt 5. However, it's still **business as usual**. - Continue making pull requests to the `main` branch - We'll backport changes to the `4.x` and `3.x` branches Keep an eye out on the [Upgrade Guide](https://nuxt.com/docs/4.x/getting-started/upgrade) – we'll be adding details about how you can already start migrating your projects to prepare for Nuxt v4 with `future.compatibilityVersion: 5`. ## 🗂️ Route Rule Layouts But that's enough about the future. We have a lot of good things for you today! First, you can now set layouts directly in route rules using the new `appLayout` property ([#31092](https://github.com/nuxt/nuxt/pull/31092)). This provides a centralized, declarative way to manage layouts across your application without scattering `definePageMeta` calls throughout your pages. ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/admin/**': { appLayout: 'admin' }, '/dashboard/**': { appLayout: 'dashboard' }, '/auth/**': { appLayout: 'minimal' } } }) ``` This might be useful for: - Admin panels with a shared layout across many routes - Marketing pages that need a different layout from the app ::tip Plus, you can pass props to layouts now! See [the `setPageLayout` improvements below](#layout-props-with-setpagelayout). :: ## 📦 ISR/SWR Payload Extraction Payload extraction now works with ISR (incremental static regeneration), SWR (stale-while-revalidate) and cache `routeRules` ([#33467](https://github.com/nuxt/nuxt/pull/33467)). Previously, only pre-rendered pages could generate `_payload.json` files. This means: - Client-side navigation to ISR/SWR pages can use cached payloads - CDNs (Vercel, Netlify, Cloudflare) can cache payload files alongside HTML - Fewer API calls during navigation – data can be prefetched and served from the cached payload ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/products/**': { isr: 3600, // Revalidate every hour } } }) ``` ## 🧹 Dev Mode Payload Extraction Related to the above, payload extraction now also works in development mode ([#30784](https://github.com/nuxt/nuxt/pull/30784)). This makes it easier to test and debug payload behavior without needing to run a production build. ::important Payload extraction works in dev mode with `nitro.static` set to `true`, or for individual pages which have `isr`, `swr`, `prerender` or `cache` route rules. :: ## 🚫 Disable Modules from Layers When extending Nuxt layers, you can now disable specific modules that you don't need ([#33883](https://github.com/nuxt/nuxt/pull/33883)). Just pass `false` to the module's options: ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: ['../shared-layer'], // disable @nuxt/image from layer image: false, }) ``` ## 🏷️ Route Groups in Page Meta [Route groups](https://nuxt.com/docs/4.x/directory-structure/app/pages#route-groups) (folders wrapped in parentheses like `(protected)/`) are now exposed in page meta ([#33460](https://github.com/nuxt/nuxt/pull/33460)). This makes it easy to check which groups a route belongs to in middleware or anywhere you have access to the route. ```vue [pages/(protected)/dashboard.vue] <script setup lang="ts"> // This page's meta will include: { groups: ['protected'] } useRoute().meta.groups </script> ``` ```ts [middleware/auth.ts] export default defineNuxtRouteMiddleware((to) => { if (to.meta.groups?.includes('protected') && !isAuthenticated()) { return navigateTo('/login') } }) ``` This provides a clean, convention-based approach to route-level authorization without needing to add `definePageMeta` to every protected page. ## 🎨 Layout Props with `setPageLayout` The `setPageLayout` composable now accepts a second parameter to pass props to your layout ([#33805](https://github.com/nuxt/nuxt/pull/33805)): ```ts [middleware/admin.ts] export default defineNuxtRouteMiddleware((to) => { setPageLayout('admin', { sidebar: true, theme: 'dark' }) }) ``` ```vue [layouts/admin.vue] <script setup lang="ts"> defineProps<{ sidebar?: boolean theme?: 'light' | 'dark' }>() </script> ``` ## 🔧 `#server` Alias A new `#server` alias provides clean imports within your server directory ([#33870](https://github.com/nuxt/nuxt/pull/33870)), similar to how `#shared` works: ```ts [server/api/users/[id\\]/profile.ts] // Before: relative path hell import { helper } from '../../../../utils/helper' // After: clean and predictable import { helper } from '#server/utils/helper' ``` The alias includes import protection – you can't accidentally import `#server` code from client or shared contexts. ## 🪟 Draggable Error Overlay The development error overlay introduced in Nuxt 4.2 is now draggable and can be minimized ([#33695](https://github.com/nuxt/nuxt/pull/33695)). You can: - Drag it to any corner of the screen (it snaps to edges) - Minimize it to a small pill button when you want to keep working - Your position and minimized state persist across page reloads This is a quality-of-life improvement when you're iterating on fixes and don't want the overlay blocking your view. ::video{poster="https://res.cloudinary.com/dchoja2nb/video/upload/v1769103044/nuxt_4-3_error_caqkhk.jpg" controls .rounded.dark:border.dark:border-gray-700} :::source{src="https://res.cloudinary.com/dchoja2nb/video/upload/v1769103044/nuxt_4-3_error_caqkhk.webm" type="video/webm"} ::: :::source{src="https://res.cloudinary.com/dchoja2nb/video/upload/v1769103044/nuxt_4-3_error_caqkhk.mp4" type="video/mp4"} ::: :::source{src="https://res.cloudinary.com/dchoja2nb/video/upload/v1769103044/nuxt_4-3_error_caqkhk.ogg" type="video/ogg"} ::: :: ## ⚙️ Async Plugin Constructors Module authors can now use async functions when adding build plugins ([#33619](https://github.com/nuxt/nuxt/pull/33619)): ```ts [modules/my-module.ts] export default defineNuxtModule({ async setup() { // Lazy load only when actually needed addVitePlugin(() => import('my-cool-plugin').then(r => r.default())) // No need to load webpack plugin if using Vite addWebpackPlugin(() => import('my-cool-plugin/webpack').then(r => r.default())) } }) ``` This enables true lazy loading of build plugins, avoiding unnecessary code loading when plugins aren't needed. ## 🚀 Performance Improvements This release includes several performance optimizations for faster builds: - **Hook filters** - Internal plugins now use filters to avoid running hooks unnecessarily ([#33898](https://github.com/nuxt/nuxt/pull/33898)) - **SSR styles optimization** - The `nuxt:ssr-styles` plugin is now significantly faster ([#33862](https://github.com/nuxt/nuxt/pull/33862), [#33865](https://github.com/nuxt/nuxt/pull/33865)) - **Layer alias transform** - Skipped when using Vite (it handles this natively) ([#33864](https://github.com/nuxt/nuxt/pull/33864)) - **Route rules compilation** - Route rules are now compiled into a client chunk using `rou3`, removing the need for `radix3` in the client bundle and eliminating app manifest fetches ([#33920](https://github.com/nuxt/nuxt/pull/33920)) ## 🎨 Inline Styles for Webpack/Rspack The `inlineStyles` feature now works with webpack and rspack builders ([#33966](https://github.com/nuxt/nuxt/pull/33966)), not just Vite. This enables critical CSS inlining for better Core Web Vitals regardless of your bundler choice. ## ⚠️ Deprecations ### `statusCode` → `status`, `statusMessage` → `statusText` In preparation for Nitro v3 and H3 v2, we're moving to use Web API naming conventions ([#33912](https://github.com/nuxt/nuxt/pull/33912)). The old properties still work but are deprecated in advance of v5: ```diff - throw createError({ statusCode: 404, statusMessage: 'Not Found' }) + throw createError({ status: 404, statusText: 'Not Found' }) ``` ## 🐛 Bug Fixes Notable fixes in this release: - Fixed head component deduplication using `key` attribute ([#33958](https://github.com/nuxt/nuxt/pull/33958), [#33963](https://github.com/nuxt/nuxt/pull/33963)) - Fixed async data properties not being reactive in Options API ([#34119](https://github.com/nuxt/nuxt/pull/34119)) - Fixed `useCookie` unsafe number parsing during decode ([#34007](https://github.com/nuxt/nuxt/pull/34007)) - Fixed `NuxtPage` not re-rendering when nested `NuxtLayout` has layouts disabled ([#34078](https://github.com/nuxt/nuxt/pull/34078)) - Fixed client-side pathname decoding for non-ASCII route aliases ([#34043](https://github.com/nuxt/nuxt/pull/34043)) - Fixed suspense remounting when navigating after pending state ([#33991](https://github.com/nuxt/nuxt/pull/33991)) - Fixed clipboard copy in error overlay ([#33873](https://github.com/nuxt/nuxt/pull/33873)) - Enabled `allowArbitraryExtensions` by default in TypeScript config ([#34084](https://github.com/nuxt/nuxt/pull/34084)) - Added `noUncheckedIndexedAccess` to server tsconfig for safer typing ([#33985](https://github.com/nuxt/nuxt/pull/33985)) ::important Enabling `noUncheckedIndexedAccess` in the Nitro server TypeScript config improves type safety but may surface new type errors in your server code. This change was necessary because Nuxt's app context performs type checks on server routes ([learn more](https://nuxt.com/docs/4.x/guide/modules/recipes-advanced#known-limitations)). While we recommend keeping this enabled for better type safety, you can disable it if needed: ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { typescript: { tsConfig: { compilerOptions: { noUncheckedIndexedAccess: false } } } } }) ``` Note that disabling this may allow type errors to slip through that could cause runtime issues with indexed access. :: ## 📚 Documentation - Improved module author guides with clearer structure ([#33803](https://github.com/nuxt/nuxt/pull/33803)) - Added MCP setup instructions for Claude Desktop ([#33914](https://github.com/nuxt/nuxt/pull/33914)) - Added layers directory documentation ([#33967](https://github.com/nuxt/nuxt/pull/33967)) - Added Deno package manager examples ([#34070](https://github.com/nuxt/nuxt/pull/34070)) - Clarified type-checking context limitations for server routes ([#33964](https://github.com/nuxt/nuxt/pull/33964)) ## 🎉 Nuxt 3.21.0 Alongside v4.3.0, we're releasing **Nuxt v3.21.0** with many of the same improvements backported to the 3.x branch. This release includes: - **All the same features**: Route rule layouts, ISR payload extraction, layout props with `setPageLayout`, `#server` alias, draggable error overlay, and more - **All performance improvements**: SSR styles optimization, hook filters, and route rules compilation - **Module disabling**: Disable layer modules by setting options to `false` - **Critical bug fixes**: Async data reactivity in Options API, `useCookie` number parsing, head component deduplication, and more ## ✅ Upgrading Our recommendation for upgrading is to run: ```sh npx nuxt upgrade --dedupe # or, if you are upgrading to v3.21 npx nuxt@latest upgrade --dedupe --channel=v3 ``` This will deduplicate your lockfile and help ensure you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ::tip Check out our [upgrade guide](https://nuxt.com/docs/getting-started/upgrade) if upgrading from an older version. :: ## 👉 Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v4.3.0"} Read the full release notes of Nuxt `v4.3.0`. :: ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.21.0"} Read the full release notes of Nuxt `v3.21.0`. :: Thank you to all of the many contributors to this release! 💚 # Nuxt 4.4 > Nuxt 4.4 brings custom useFetch/useAsyncData factories, vue-router v5, a new accessibility announcer, typed layout props, build profiling, smarter payload handling, and much more. ## 🏭 `createUseFetch` and `createUseAsyncData` You can now create **custom instances** of `useFetch` and `useAsyncData` with your own default options ([#32300](https://github.com/nuxt/nuxt/pull/32300)). ```ts [composables/api.ts] // Simple defaults export const useClientFetch = createUseFetch({ server: false, }) // Dynamic defaults with full control over merging export const useApiFetch = createUseFetch((currentOptions) => { const runtimeConfig = useRuntimeConfig() return { ...currentOptions, baseURL: currentOptions.baseURL ?? runtimeConfig.public.baseApiUrl, } }) ``` Then use them exactly like `useFetch` – they're fully typed and support all the same options: ```vue [pages/dashboard.vue] <script setup lang="ts"> // Uses your baseURL from runtimeConfig automatically const { data: users } = await useApiFetch('/users') </script> ``` When you pass a plain object, your usage options automatically override the defaults. When you pass a function, you get full control over how options are merged – which means you can compose interceptors, headers, and other complex options however you need. Under the hood, this is powered by a new Nuxt ad-hoc module that scans your composables directory and automatically registers your custom instances for key injection – so they work seamlessly with SSR, just like `useAsyncData` and `useFetch`. There's also `createUseAsyncData` for the same pattern with `useAsyncData`. ::read-more{to="https://nuxt.com/docs/api/composables/create-use-async-data"} :: ## 🗺️ Vue Router v5 We've upgraded to [vue-router v5](https://github.com/vuejs/router) ([#34181](https://github.com/nuxt/nuxt/pull/34181)), which removes the dependency on `unplugin-vue-router`. This is the first major vue-router upgrade since Nuxt 3, and it comes with a bunch of improvements under the hood. For most apps, this should be a transparent upgrade. If you're using `unplugin-vue-router` directly, you can remove it from your dependencies. The next step will be taking typed routes out of experimental status. 👀 ## 💪 Typed Layout Props in `definePageMeta` You can now pass props to your layouts directly from `definePageMeta` ([#34262](https://github.com/nuxt/nuxt/pull/34262)). This means your layouts can be parameterised per-page without needing to use `provide`/`inject` or other workarounds. Check out the [updated docs](https://nuxt.com/docs/guide/directory-structure/layouts#layout-props) for the full details. ```ts [pages/dashboard.vue] definePageMeta({ layout: { name: 'panel', props: { sidebar: true, title: 'Dashboard', }, }, }) ``` Even better – the props are **fully typed** ([#34409](https://github.com/nuxt/nuxt/pull/34409)). If your layout defines props, you'll get autocomplete and type-checking in `definePageMeta`. ```vue [layouts/panel.vue] <script setup lang="ts"> defineProps<{ sidebar?: boolean title?: string }>() </script> ``` ::read-more{to="https://nuxt.com/docs/guide/directory-structure/layouts"} :: ## 🗣️ `useAnnouncer` Composable Accessibility got a major boost with the new `useAnnouncer` composable and `<NuxtAnnouncer>` component ([#34318](https://github.com/nuxt/nuxt/pull/34318)). While `useRouteAnnouncer` handles page navigation for screen readers, many apps need to announce **dynamic in-page changes** – form submissions, loading states, search results, and more. ::code-group ```vue [app.vue] <template> <NuxtAnnouncer /> <NuxtRouteAnnouncer /> <NuxtLayout> <NuxtPage /> </NuxtLayout> </template> ``` ```vue [components/ContactForm.vue] <script setup lang="ts"> const { polite, assertive } = useAnnouncer() async function submitForm() { try { await $fetch('/api/contact', { method: 'POST', body: formData }) polite('Message sent successfully') } catch (error) { assertive('Error: Failed to send message') } } </script> ``` :: ::note This is part of our [accessibility roadmap](https://github.com/nuxt/nuxt/issues/23255). You don't need to use it everywhere – for many interactions, moving focus to new content or using native form validation is sufficient. `useAnnouncer` is most useful when content changes dynamically without a corresponding focus change. :: ::read-more{to="https://nuxt.com/docs/api/composables/use-announcer"} :: ## 🚀 Migrate to `unrouting` We've migrated Nuxt's file-system route generation to [`unrouting`](https://github.com/unjs/unrouting) ([#34316](https://github.com/nuxt/nuxt/pull/34316)), which uses a trie data structure for constructing routes. The cold start is roughly the same (\~8ms vs \~6ms for large apps), but dev server changes are **up to 28x faster** when you're not adding/removing pages, and \~15% faster even when you are. This also makes route generation more deterministic – it's no longer sensitive to page file ordering. ## 🍫 Smarter Payload Handling for Cached Routes When a cached route (ISR/SWR) is rendered at runtime with payload extraction enabled, the browser immediately fetches `_payload.json` as a second request – which triggers a full SSR re-render of the same page. In serverless environments, this can spin up a second lambda before the first response has even finished streaming. This release addresses this with two changes ([#34410](https://github.com/nuxt/nuxt/pull/34410)): 1. A new `payloadExtraction: 'client'` mode that inlines the full payload in the initial HTML response while still generating `_payload.json` for client-side navigation 2. A runtime in-memory LRU payload cache so that `_payload.json` requests can be served without a full re-render ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { payloadExtraction: 'client', }, }) ``` ::note `payloadExtraction: 'client'` will become the default with `compatibilityVersion: 5`. The runtime cache is active for all users. :: ::read-more{to="https://nuxt.com/docs/guide/going-further/experimental-features#payloadextraction"} :: ## 🍪 `refresh` Option for `useCookie` If you're using cookies for session management, you've probably run into the problem of needing to extend a cookie's expiration without changing its value. The new `refresh` option makes this simple ([#33814](https://github.com/nuxt/nuxt/pull/33814)): ```ts const session = useCookie('session-id', { maxAge: 60 * 60, refresh: true, }) // Extends expiration each time, even with the same value session.value = session.value ``` ::read-more{to="https://nuxt.com/docs/api/composables/use-cookie"} :: ## ♻️ `useState` Reset to Default `useState` and `clearNuxtState` now support resetting to the initial value instead of clearing to `undefined` ([#33527](https://github.com/nuxt/nuxt/pull/33527)). This aligns with how `useAsyncData` handles resets and is more intuitive for state management. ```ts const count = useState('counter', () => 0) count.value = 42 // Resets to 0 (the init value), not undefined clearNuxtState('counter') ``` ::read-more{to="https://nuxt.com/docs/api/utils/clear-nuxt-state"} :: ## 🕵️‍♂️ Better Import Protection Inspired by features in [TanStack Start](https://tanstack.com/start/latest/docs/framework/react/guide/import-protection), import protection now shows **suggestions** and a full **trace** of where a problematic import originated ([#34454](https://github.com/nuxt/nuxt/pull/34454)). This makes it much easier to debug why a server-only import ended up in your client bundle. For example, if you accidentally import from a server route in a component: ![More useful import protection error](https://nuxt.com/assets/blog/import-protection.png) The trace shows the import chain (the component was imported from a page), the exact line of code, and actionable suggestions for how to fix it. We plan to continue work on improving our error messages. 🪵 ## 🔮 View Transitions Types You can now define [view transition types](https://developer.chrome.com/blog/view-transitions-update-io24#view-transition-types) in Nuxt's experimental view transitions support ([#31982](https://github.com/nuxt/nuxt/pull/31982)). This lets you use different transition styles for different navigation patterns (forwards vs. backwards, tabs vs. pages, etc.). ::read-more{to="https://nuxt.com/docs/getting-started/transitions#view-transitions-api-experimental"} :: ## 💡 Improved `optimizeDeps` Hints When Vite discovers new dependencies at runtime and triggers page reloads, Nuxt now shows a clear, copy-pasteable `nuxt.config.ts` snippet to pre-bundle them ([#34320](https://github.com/nuxt/nuxt/pull/34320)). It also warns about unresolvable entries at startup. ## 🏷️ Normalised Page Component Names (Experimental) A new experimental option normalises page component names to match route names ([#33513](https://github.com/nuxt/nuxt/pull/33513)), which can help with consistency in devtools and debugging. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { normalizeComponentNames: true, }, }) ``` ::read-more{to="https://nuxt.com/docs/guide/going-further/experimental-features#normalizecomponentnames"} :: ## ⚡ Build Profiling Ever wondered where your build time goes? You can now get a detailed performance breakdown of your Nuxt builds ([#34468](https://github.com/nuxt/nuxt/pull/34468), [nuxt/cli#1243](https://github.com/nuxt/cli/pull/1243)): ```bash nuxt build --profile ``` This produces a report showing duration, RSS delta, and heap delta for every build phase, module, and bundler plugin: ![Build timings report printed to console](https://nuxt.com/assets/blog/build-profiler.png) It also profiles individual modules and bundler plugins, making it easy to spot bottlenecks. Three output formats are written: - **Chrome Trace** (`.nuxt/perf-trace.json`) – open in `chrome://tracing` or Perfetto for a visual timeline - **JSON report** (`.nuxt/perf-report.json`) – machine-readable data for tracking over time - **CPU profile** (`nuxt-build.cpuprofile`) – open in Chrome DevTools or VS Code for flame graphs For even more detail, use `--profile=verbose` to print timing breakdowns to the console. ::read-more{to="https://nuxt.com/docs/api/commands/build"} :: We'll be using this feature to make Nuxt even faster – and if performance is something you care about, this might be a good opportunity to contribute! ## 🔥 Performance Improvements - **14,000x faster module ID parsing** – replaced `new URL()` + regex chain with a single `indexOf` + slice ([#34451](https://github.com/nuxt/nuxt/pull/34451)) - **Disabled NuxtLink visibility prefetching in dev** – stops Vite from discovering and reloading deps unnecessarily during development ([#34325](https://github.com/nuxt/nuxt/pull/34325)) ## ⬆︎ Upgrading Our recommendation for upgrading is to run: ```sh npx nuxt upgrade --dedupe ``` This will deduplicate your lockfile and help ensure you pull in updates from other dependencies that Nuxt relies on, particularly in the unjs ecosystem. ::tip Check out our [upgrade guide](https://nuxt.com/docs/getting-started/upgrade) if upgrading from an older version. :: ## 👉 Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v4.4.0"} Read the full release notes of Nuxt `v4.4.0`. :: Thank you to all of the many contributors to this release! 💚 # Introducing the Nuxt Agent > Our own AI agent on nuxt.com, grounded in the official docs and the Nuxt ecosystem. We built it internally using the AI SDK, our MCP server, and Nuxt UI components. ::important{icon="i-lucide-heart"} First, we want to thank [Kapa.ai](https://kapa.ai), who have powered our AI Chat widget over the past few years through their sponsorship and continue to support our community through our [Discord server](https://go.nuxt.com/discord). We decided to build our own to showcase what’s possible with Nuxt and offer a deeper integration with the framework and its ecosystem. :: The **Nuxt Agent** is now in Beta on [nuxt.com](http://nuxt.com). We built it ourselves, plugged it into the site, and connected it to our docs, modules catalog, blog, and deployment guides. ::callout{icon="i-lucide-sparkles"} Open the agent anywhere on [nuxt.com](http://nuxt.com) with **⌘I** (or **Ctrl+I**), or jump straight into the full-screen experience at [/chat](https://nuxt.com/chat). :: ::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1776779145/nuxt/nuxt-agent-built-in-chat_lpaii9.jpg" controls .rounded-lg} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1776779145/nuxt/nuxt-agent-built-in-chat_lpaii9.mp4" type="video/mp4"} ::: :: ## From a docs widget to a real agent Kapa AI served us well as a docs Q&A widget for the past couple of years. It searched the docs and summarized an answer, and that was about it. But Nuxt is more than docs. There are modules, templates, deployment providers, a changelog, GitHub issues, playgrounds, and real navigation across the site. We wanted an agent that could handle all of it, with the same design language as the rest of [nuxt.com](http://nuxt.com) and the same content pipeline (Nuxt Content) we already run. So we built our own on top of the [Nuxt MCP server](https://nuxt.com/blog/building-nuxt-mcp) we shipped last November. Here's what the agent does today: - **Grounds answers** in the official Nuxt docs and ecosystem data through MCP tools, not retrieved text chunks. - **Renders rich UI**. Modules, templates, blog posts, hosting providers, and playground links come back as clickable cards instead of plain links. - **Streams everything** as it runs, including tool call progress. - **Closes the loop**. Feedback, voting, and issue reports flow into our internal tools so we can keep improving it. ## Meet the agent ### Three ways to talk to it You can reach the agent three ways: - A **side panel** pinned to the right on large screens, sliding over on smaller ones. Toggle it from the header or with **⌘I**. - An **ask bar** at the bottom of `/docs` and `/blog` pages, so you can ask a question without leaving the page you're reading. - A **full-screen chat** at [/chat](https://nuxt.com/chat) for longer sessions. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1776779145/nuxt/nuxt-agent-fullscreen-chat_gxxzmh.jpg" controls .rounded-lg} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1776779145/nuxt/nuxt-agent-fullscreen-chat_gxxzmh.mp4" type="video/mp4"} ::: :: ### It knows what page you're on Ask "how do I customize this for my app?" while reading a doc and the agent automatically pulls that page in as context. A small "Agent is using this page" indicator in the footer makes it explicit, and you can dismiss it whenever you want. ### Rich answers, not just text Answers come back as more than text. Ask about a module and you get a module card with metadata pulled live from `api.nuxt.com`. Ask for starter templates and you get a row of clickable template cards. Ask about deployment and you get provider cards that link to the right guide. Need to reproduce a bug? The agent can generate a [StackBlitz](https://stackblitz.com) playground link from the conversation itself. ::callout{icon="i-lucide-lightbulb"} Try asking: *"Show me official starter templates"* — you'll get the full lineup (`nuxt-ui-dashboard`, `nuxt-ui-saas`, `nuxt-ui-landing`, `nuxt-ui-chat`, `nuxt-ui-docs`, `nuxt-ui-portfolio`) as cards you can open in one click. :: ### Feedback built in Every assistant message has a thumbs up/down. If you want to share more (a missing piece, a wrong answer, an idea), the **Report issue** action opens a short form and creates a Linear ticket on our side with the conversation attached, so we have everything we need to follow up. The agent can open that form itself, too. If you ask to "submit feedback" or "report an issue", or if the conversation starts to feel frustrated, the agent calls the `report_issue` tool and the same form opens inline. No button hunting required. Conversations are saved and resume across reloads, so you can step away and pick up where you left off. ## What the agent can actually do The agent's grounding comes from the [Nuxt MCP server](https://nuxt.com/mcp), the same one Cursor, Claude Desktop and ChatGPT connect to. That means the Nuxt Agent and your local AI assistant share the same structured data: the official docs, the modules catalog, the blog, deployment guides, and the Nuxt repositories' changelog. On top of that grounding, the agent has a small set of native tools that render as UI in the chat: module and template cards, hosting providers, blog posts, StackBlitz playground links, and a GitHub issue search across `nuxt`, `nuxt-modules` and `nuxt-content`. The agent reaches for that issue search first whenever you paste an error. The web is available too, through Anthropic's native `web_search`, but only as a fallback for things the model couldn't reasonably know: a brand new Vue release, a freshly published RFC, recent ecosystem news. It's not a general-purpose search engine, and the system prompt is explicit about that. We never use `web_search` for anything that should be answered from the docs or the rest of the Nuxt content exposed through MCP. ## Under the hood ### The stack A single Nitro handler at [`server/api/agent.post.ts`](https://github.com/nuxt/nuxt.com/blob/main/server/api/agent.post.ts) drives everything. On the client, an `@ai-sdk/vue` `Chat` instance points at `/api/agent`. On the server, AI SDK v6 `streamText` calls `claude-sonnet-4.6`, with tools merged from our own MCP server (`/mcp`, same origin) and a few native ones (`show_*`, `open_playground`, `report_issue`). Chat state lives in Drizzle ORM, and [`evlog`](https://evlog.dev) wraps the model for structured telemetry on tokens, cost, and tool calls. ### UIMessage streaming with AI SDK v6 The whole pipeline runs on the [AI SDK v6](https://ai-sdk.dev) `UIMessage` streaming model. The server side looks like this: ::code-collapse ```ts [server/api/agent.post.ts] const stream = createUIMessageStream({ execute: async ({ writer }) => { const result = streamText({ model: ai.wrap(MODEL), maxOutputTokens: 4000, stopWhen: stopWhenResponseComplete, system: systemPrompt, messages: await convertToModelMessages(messages), tools: { ...mcpTools as ToolSet, web_search: anthropic.tools.webSearch_20250305(), search_github_issues: createSearchGitHubIssuesTool(event), show_module: showModuleTool, show_template: createShowTemplateTool(event), show_blog_post: createShowBlogPostTool(event), show_hosting: createShowHostingTool(event), open_playground: openPlaygroundTool, report_issue: reportIssueTool }, experimental_telemetry: { isEnabled: true, integrations: [createEvlogIntegration(ai)] } }) writer.merge(result.toUIMessageStream({ sendSources: true, originalMessages: messages, onFinish: ({ messages: finalizedMessages }) => { event.waitUntil(saveChat(finalizedMessages)) } })) } }) ``` :: Two details are worth pointing out. `stopWhen: stopWhenResponseComplete` is a custom predicate that ends the loop as soon as the model produces text without another tool call, with a hard ceiling at 10 steps. That avoids the classic "model loops forever on tools" failure mode. And `event.waitUntil(saveChat(...))` pushes persistence outside the response lifecycle, so the stream finishes for the user right away while the chat row gets upserted in the background. ### One MCP server, two consumers The agent and external AI assistants talk to the same MCP server. That's the single most important architectural choice we made. The route handler opens an HTTP MCP client pointed at its own `/mcp` endpoint: ```ts [server/api/agent.post.ts] const httpClient = await createMCPClient({ transport: { type: 'http', url: `${getRequestURL(event).origin}${MCP_PATH}` } }) const mcpTools = await httpClient.tools() ``` Those tools then get merged with the native UI tools into a single `tools` object passed to `streamText`. The payoff: any tool we add to the MCP server is immediately available to the Nuxt Agent and to every external assistant pointed at it, with no extra wiring. We wrote a [separate post](https://nuxt.com/blog/building-nuxt-mcp) about how the MCP server itself works back in November. ### Persistence, cost, and rate limiting Chats live in a single `agent_chats` table, keyed by the `x-chat-id` header the client sends on every request. Drizzle's `onConflictDoUpdate` accumulates token usage, estimated cost, duration, and request count across the lifetime of a conversation. That gives us per-chat analytics for free. Every request also goes through a small `consumeAgentRateLimit` helper before streaming starts. The current cap is 20 messages per day per IP fingerprint, which is enough for real use and low enough to prevent runaway costs if something starts looping. ### A tight system prompt A lot of agent quality comes from the prompt. A few rules carried most of the weight: reach for `search_github_issues` first whenever the user pastes an error, prefer `show_module` over `get_module` when the answer should render as a card, never call `web_search` unless the question is genuinely outside what the model could know, and never end a turn with only a tool call. Together those rules cut tool-spam and hallucinations enough that the agent stays focused on the task at hand. ## What's next The agent is launching in Beta. In the short term, we're focused on the basics: better answer quality, richer memory across turns, and cleaner source citations. Further out, we want [nuxt.com](http://nuxt.com) to feel more like an application than a static site. The next step there is user accounts. Each logged-in user gets their own session, their chat history saved and resumable across devices, and the Nuxt Agent becomes the first real building block of that more app-like [nuxt.com](http://nuxt.com). We'd love your help shaping where it goes next. If the agent gets something wrong, or misses something you want, use the **Report issue** button inside the chat. It creates a ticket on our side with the full conversation attached, and we read every one. ::callout{icon="i-lucide-message-square"} Try the Nuxt Agent now: open it with **⌘I**, use the ask bar on any [docs](https://nuxt.com/docs) page, or head to [/chat](https://nuxt.com/chat) for the full-screen experience. :: The full source for [nuxt.com](http://nuxt.com) is on [GitHub](https://github.com/nuxt/nuxt.com), including the agent, the MCP server, and every tool covered above. The agent handler is at [`server/api/agent.post.ts`](https://github.com/nuxt/nuxt.com/blob/main/server/api/agent.post.ts), the native tools at [`server/utils/tools/`](https://github.com/nuxt/nuxt.com/tree/main/server/utils/tools), and the UI components at [`app/components/agent/`](https://github.com/nuxt/nuxt.com/tree/main/app/components/agent). Take any of it as inspiration for your own apps. And if you want to build your own MCP server, the [Nuxt MCP Toolkit](https://mcp-toolkit.nuxt.dev) gets you there in a few minutes. # Meet Nuxi > Today, we're announcing the new Nuxt Agent: Nuxi. We want to make your Nuxt experience less generic and more personalized, with the care that characterizes the Nuxt community. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1779442968/nuxt/introducing-nuxi_vvvoyh.jpg" controls .rounded-lg} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1779442968/nuxt/introducing-nuxi_vvvoyh.mp4" type="video/mp4"} ::: :: Back in April, we shipped AI-powered chat on [nuxt.com](https://nuxt.com/) in [beta](https://nuxt.com/blog/introducing-nuxt-agent). It was full-featured. It knew the docs, searched issues, and even had custom components for modules and templates. It was a great showcase of how to build chat UI on Nuxt. But chat-based interfaces are everywhere, and we wanted Nuxt to feel different. We wanted to find an identity for the Nuxt agent, matching the Nuxt brand. And we wanted it to become much more part of your experience on [nuxt.com](http://nuxt.com). So, today, we're announcing **Nuxi**, your Nuxt companion. ::agent-nuxi-icon{.block.mx-auto.my-6.size-20.text-primary} :: ## Why Nuxi? Our aim with Nuxi is to improve your developer experience and communicate something of the friendliness and care that characterizes the Nuxt community. We want it to feel just like Nuxt. That's why we picked the name **Nuxi**. For a while, it was the command you ran to start a Nuxt dev server (`nuxi dev`). Now, we use `nuxt dev`, but Nuxi has always held a place in our hearts, and we feel it's right to bring it back. ::nuxi-mood-gallery :: ## What we're aiming for **Playful.** We want Nuxi to feel fun. It should react to where you are, what you do, and it has a few things built in that we'd rather you find on your own. Start with typing `hi`. ::try-nuxi{prompt="hi" label="Say hi"} :: ::tip{icon="i-lucide-mouse-pointer-2"} Hover the Nuxi icon in the header and move your mouse around. :: **Context-aware.** When you chat to Nuxi, it will know the page you're on. We don't want you to have to explain yourself unnecessarily. Ask "how do I use this in my app?" while reading a doc and it should already knows what *this* refers to. ::try-nuxi{prompt="How do I use this in my app?"} :: **Grounded.** There are a lot of sources for Nuxt documentation. Nuxi uses [the Nuxt MCP server](https://nuxt.com/mcp) under the hood to find answers from whatever is most relevant, whether that's official Nuxt docs, the modules catalog, templates or changelogs. If it's helping you resolve an error, Nuxi will even search issues across `nuxt`, `nuxt-modules`, and `nuxt-content` before forming an opinion. ::try-nuxi{prompt="How do I fetch data on the server in Nuxt 4?"} :: **Built to grow.** Nuxi isn't a one-shot Q&A widget. We've built in lots of rich components. It renders modules, templates, hosting providers, and blog posts as cards you can click, spins up a [StackBlitz](https://stackblitz.com) playground straight from the conversation, and opens a feedback form if something feels off. If you have any ideas for improving it, adding new functionality is just a PR away. ::try-nuxi{prompt="Show me official starter templates"} :: ## What's new today This is the biggest batch of changes since the April beta. Our focus is making [nuxt.com](https://nuxt.com/) less generic and more personalized. ### Pick up where you left off You can now **sign in to [nuxt.com](http://nuxt.com)** with GitHub. Once you do, your conversations are saved automatically. For example, you can [open a chat](https://nuxt.com/dashboard/chat) from your phone and finish it on your laptop. ::callout{icon="i-lucide-log-in" to="https://nuxt.com/login"} Sign in with GitHub at [/login](https://nuxt.com/login). :: ### Branch your chats Conversations aren't always linear. If you need to explore a tangent, try a different approach, or compare two approaches, you can fork the chat. From any of Nuxi's answers, hit **Branch in new chat**; everything up to that point is copied into a fresh thread, leaving the original untouched. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1779446023/nuxt/nuxi-branch_jidlol.jpg" controls .rounded-lg} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1779446023/nuxt/nuxi-branch_jidlol.mp4" type="video/mp4"} ::: :: ### Share a great answer Conversations are **private by default**. But if you want, you can mark your chat public and share it with a link. Anyone can read it, but nobody else can post in it, and you can switch it back to private whenever you want. ::video{poster="https://res.cloudinary.com/nuxt/video/upload/so_0/v1779446028/nuxt/nuxi-sharing_jwgsxg.jpg" controls .rounded-lg} :::source{src="https://res.cloudinary.com/nuxt/video/upload/v1779446028/nuxt/nuxi-sharing_jwgsxg.mp4" type="video/mp4"} ::: :: ::note Everything from the first release is still here: docs grounding, module and template cards, deployment provider cards, blog cards, GitHub issue search, playground links, page context, and the **Report issue** flow that opens a Linear ticket on our side. The [previous post](https://nuxt.com/blog/introducing-nuxt-agent) covers the full list. :: ## Your thoughts Nuxt is a community, and we want everything we build to be useful for all of us. Please feel free to share your feedback on Nuxi, or any of the new features we're rolling out. ::tip{icon="i-lucide-mouse-pointer-2"} If something feels off when you're using Nuxi, hit **Report issue** inside the chat. It creates a ticket with the full conversation attached — and we'll read every one. :: Over the next few months, we have more exciting plans for [nuxt.com](https://nuxt.com/), and we'd love your thoughts as we build. Watch this space. 👀 ## FAQ ::accordion{type="single"} :::accordion-item{label="Is my chat history private?"} Yes. Every conversation is private by default and only visible to you. You can flip an individual chat to public from the visibility menu in the chat header, and switch it back whenever you want. ::: :::accordion-item{label="Do I need to sign in to use Nuxi?"} No. Nuxi works without an account. You'll just lose the cross-device history, and your rate limit is counted per IP instead of per account. ::: :::accordion-item{label="How many messages can I send per day?"} Twenty per day for now, counted per account when you're signed in and per IP otherwise. We're tuning these limits as usage settles. ::: :::accordion-item{label="What model does Nuxi run on?"} Anthropic's Claude Sonnet 4.6 through the [Vercel AI Gateway](https://vercel.com/ai-gateway), with the [Nuxt MCP server](https://nuxt.com/mcp) as its grounding for docs, modules, templates, deployment guides, and GitHub issues. ::: :::accordion-item{label="Can I delete a conversation?"} Yes. Open [/dashboard/chat](https://nuxt.com/dashboard/chat), hover any chat in the sidebar, and use the menu to delete it. It's removed from our database. ::: :::accordion-item{label="What does Nuxi know about me when I'm signed in?"} Your GitHub username and avatar, plus your conversations on [nuxt.com](http://nuxt.com). Nothing about your repos, your code, or your other Nuxt projects. ::: :: # Nuxt 4.5 > Nuxt 4.5 is our biggest release in a while. Vite 8, Rspack 2 powered by Rsbuild, experimental SSR streaming, a stable error code system, a new useLayout composable, named views, and a lot of groundwork for Nuxt 5. Nuxt 4.5 is a big one. This release ships three major upgrades to the build layer (Vite 8, Rspack 2, and a brand new Rsbuild-powered pipeline for the Rspack builder), an experimental SSR streaming mode, a stable error code system, a handful of new composables and conventions, and a lot of groundwork that brings us closer to Nuxt 5. There's a lot here, so grab a coffee. ☕️ ## 📣 Some News ### Preparing for Nuxt 5 A good chunk of this release is (hopefully) invisible plumbing for Nuxt 5. We've moved onto the latest major versions of several core dependencies ([`unhead` v3](#unhead-v3), [`unctx` v3](#unctx-v3), and [Vite 8](#vite-8)), switched the framework's own build over to [`tsdown`](https://github.com/nuxt/nuxt/pull/35179), and introduced a stable `nuxt/*` build output contract with dev exports so that type-checking in the Nuxt monorepo works without a build step ([#35463](https://github.com/nuxt/nuxt/pull/35463), [#35605](https://github.com/nuxt/nuxt/pull/35605)). Much of this is working to shrink the gap between v4 and v5 internally, so that the migration will be as boring(?) as possible. ::tip If you want to test some of the breaking changes of Nuxt v4, you can already opt in with `future.compatibilityVersion: 5`. Keep an eye on the [Upgrade Guide](https://nuxt.com/docs/getting-started/upgrade) for details as they land. :: With the release of Nuxt v4.5, our focus as a team will turn to stabilising Nuxt v5 and creating compatibility utilities to make the upgrade as smooth as possible. ### Nuxt 3 End-of-Life Nuxt 3 reaches end-of-life on **July 31, 2026**, so this is one of the last few 3.x releases we'll ship. If you're still on v3, now is a great time to move across. Most people told us the v3 to v4 upgrade was smooth, and we've kept the [upgrade guide](https://nuxt.com/docs/getting-started/upgrade) up to date. Alongside v4.5.0 we're publishing a maintenance patch for the 3.x line (**v3.21.9**) with the compatible bug fixes and smaller improvements from this release backported. The headline items here (Vite 8, Rspack 2, `unhead` v3, `unctx` v3) are major upgrades and stay v4-only, so 3.x remains stable as it approaches end-of-life. ## ⚡️ Vite 8 Nuxt now runs on [Vite 8](https://vite.dev) ([#34256](https://github.com/nuxt/nuxt/pull/34256)). This brings faster cold starts, the latest Rolldown-powered internals, and many upstream improvements from the Vite team. For most apps this is a transparent upgrade. If you have custom Vite plugins or config, it's worth skimming the [Vite migration guide](https://vite.dev/guide/migration) to check for anything that affects you. ::warning Vite 8 is a major version bump. If you depend on Vite directly (custom plugins, `vite.config` tweaks, or ecosystem plugins that pin a Vite version), make sure those are compatible before upgrading in production. :: ## 🦀 Rspack 2 and Rsbuild If you use the Rspack builder, this release is a substantial upgrade. We've moved to [Rspack 2](https://www.rspack.rs/blog/announcing-2-0) ([#34929](https://github.com/nuxt/nuxt/pull/34929)), which is faster and lighter, and rebuilt the builder on top of [`@rsbuild/core`](https://rsbuild.dev) ([#35489](https://github.com/nuxt/nuxt/pull/35489)). The public surface stays the same. You still opt in with `builder: 'rspack'` and the existing `rspack:*` hooks continue to work: ```ts [nuxt.config.ts] export default defineNuxtConfig({ builder: 'rspack', }) ``` Under the hood, though, a lot has changed for the better: - The dev server now runs in middleware mode via Rsbuild, replacing `webpack-dev-middleware` and `webpack-hot-middleware` ([#35575](https://github.com/nuxt/nuxt/pull/35575)). - We use an Rspack-specific Vue loader for correct SSR scoped-style ids and stricter ESM resolution ([#35566](https://github.com/nuxt/nuxt/pull/35566)). ::note This is the foundation for first-class Rsbuild support. We kept the builder named `rspack` for now so nothing breaks, but the internals are now Rsbuild all the way down. :: ## 🌊 Experimental SSR Streaming This is one I'm particularly excited about. You can now enable **SSR streaming** to dramatically improve Time to First Byte ([#34411](https://github.com/nuxt/nuxt/pull/34411)). Instead of buffering the whole rendered page and sending it in one go, Nuxt flushes the HTML shell (your `<head>`, styles, preload hints, and entry scripts) immediately, then streams the body as Vue renders it. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { ssrStreaming: true, }, }) ``` Streaming is automatically disabled for bots and crawlers so search engines still receive fully-rendered HTML. You can tune which user agents count as crawlers, and you can opt individual routes out: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { ssrStreaming: { botRegex: /googlebot|bingbot|my-internal-crawler/i, }, }, routeRules: { '/no-stream/**': { streaming: false }, }, }) ``` There's one thing worth understanding before you turn it on. Because streaming commits the HTTP status and headers with the very first byte, anything that mutates the response *after* rendering has begun (a `setResponseStatus()` in a `<script setup>`, a cookie write during middleware, and so on) can't reach the client. Nuxt handles the common cases for you: routes with `redirect`, `cache`, `isr`, `swr`, `noScripts`, or `ssr: false` rules automatically fall back to the buffered renderer, and in development we log a warning naming any dropped mutations so nothing fails silently. ::warning SSR streaming is experimental and off by default. It could be great for content-heavy routes where TTFB matters, but test it against your app's response-mutating logic (status codes, headers, cookies) before shipping it widely. The [experimental features docs](https://nuxt.com/docs/guide/going-further/experimental-features#ssrstreaming) cover the fallback rules and caveats in detail. :: ::read-more{to="https://nuxt.com/docs/guide/going-further/experimental-features#ssrstreaming"} :: ## 🩺 Stable Error Codes This one I'm really happy about. Nuxt now has a **stable error code system** ([#35429](https://github.com/nuxt/nuxt/pull/35429)) using [nostics](https://nostics.dev/). Warnings and errors raised during build and at runtime now carry a stable code (like `NUXT_E1001` or `NUXT_B5001`), a short explanation of **why** it happened, and a concrete **fix** to try. Every code is greppable and bookmarkable, and the ones that need more than a one-line fix link straight to a dedicated docs page. For example, the classic "a composable was called outside a Nuxt context" now surfaces as `NUXT_E1001` with the why/fix inline and a [docs page](https://nuxt.com/docs/errors/e1001) explaining the context rules and how to use `runWithContext()`. To keep production output lean, the verbose why/fix text is stripped from production builds, leaving just the stable code. This is the foundation for much better error messages across Nuxt, and we'll keep migrating existing warnings and errors onto it over the coming releases. 🔥 ## 🎨 `useLayout` Composable There's a new `useLayout` composable for reading the layout that's been resolved for the current route ([#35623](https://github.com/nuxt/nuxt/pull/35623)). Previously there was no clean, reactive way to ask "which layout is this page using?" from within a component. ```vue [app/components/LayoutBadge.vue] <script setup lang="ts"> const layout = useLayout() </script> <template> <span>Current layout: {{ layout }}</span> </template> ``` It returns a read-only computed ref, so it stays in sync as you navigate or as route rules and `definePageMeta` change the resolved layout. ::read-more{to="https://nuxt.com/docs/api/composables/use-layout"} :: ## 🪟 Named Views Nuxt now supports **named views** through a filename convention ([#35123](https://github.com/nuxt/nuxt/pull/35123)). If a parent page renders more than one `<NuxtPage>` outlet, you can give each outlet a name and provide a sibling page file for it using the `name@view.vue` convention: ```bash [Directory Structure] -| pages/ ---| parent/ -----| child.vue -----| child@sidebar.vue ---| parent.vue ``` ```vue [pages/parent.vue] <template> <div> <NuxtPage /> <aside> <NuxtPage name="sidebar" /> </aside> </div> </template> ``` Navigating to `/parent/child` renders `child.vue` into the default outlet and `child@sidebar.vue` into the `sidebar` outlet. This has actually been possible in Vue Router for a long time; this release wires it up to Nuxt's file-based routing. ::note `definePageMeta` is read from the default route file only, and per-view rendering modes aren't supported (the parent page's mode applies to the default view). :: ::read-more{to="https://nuxt.com/docs/guide/directory-structure/app/pages#named-views-v45"} :: ## 🚦 `enabled` Option for `useFetch` and `useAsyncData` You can now gate data fetching with a reactive `enabled` option ([#33260](https://github.com/nuxt/nuxt/pull/33260)). While `enabled` is `false`, every execution is blocked (the initial fetch, `execute`/`refresh`, and watch triggers), and if you flip it from `true` to `false` mid-flight, the in-flight request is cancelled without clearing your existing `data`. ```vue [app/pages/search.vue] <script setup lang="ts"> const query = ref('') const { data } = await useFetch('/api/search', { query: { q: query }, // Only fetch once the user has typed something enabled: () => query.value.length > 2, }) </script> ``` This is perfect for dependent or conditional queries, where you don't want to fire a request until some precondition is met. It pairs naturally with a getter or a ref, so it stays reactive. ::read-more{to="https://nuxt.com/docs/api/composables/use-async-data"} :: ## 🔗 `NuxtLink` Prefetch Control for Custom Slots When you use `<NuxtLink>` with the `custom` prop, Nuxt no longer attaches prefetch handlers for you, because it can't know how you've structured your markup. To make that ergonomic, the slot now exposes everything you need to wire prefetching up yourself ([#34539](https://github.com/nuxt/nuxt/pull/34539)): ```vue <template> <NuxtLink v-slot="{ href, navigate, prefetch, prefetched, shouldPrefetch }" to="/about" custom > <a :href="href" :class="{ 'is-prefetched': prefetched }" @click="navigate" @pointerenter="shouldPrefetch('interaction') && prefetch()" @focus="shouldPrefetch('interaction') && prefetch()" > About page </a> </NuxtLink> </template> ``` You get `prefetch` to trigger it, `prefetched` to know whether it's already happened (great for a prefetched class), and `shouldPrefetch` to respect the user's connection and config. ::read-more{to="https://nuxt.com/docs/api/components/nuxt-link"} :: ## ⚡️ Forwarded Preload Hints on Prefetch Here's another one we'd love you to try. When you prefetch a link to a route with payload extraction, Nuxt already primes the destination's data and chunks. With the new opt-in `experimental.prefetchPreloadTags` ([#35144](https://github.com/nuxt/nuxt/pull/35144)), it also forwards the destination's `<link rel="preload">` and `modulepreload` hints (whatever the page sets via `useHead`, or via modules like `@nuxt/image`'s `<NuxtImg preload>`) into the *current* document, downgraded to `rel="prefetch"` so they don't compete with the resources the user is looking at right now. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { prefetchPreloadTags: true, }, }) ``` The practical effect is that heavy above-the-fold assets on the *next* page (a hero image, a critical script) start downloading while the user is still on the current one, so the navigation feels instant. It's off by default while we gather feedback, so please give it a spin and tell us how it behaves on your app. ## 🌐 `import.meta.envName` The resolved Nuxt environment name is now available at runtime as `import.meta.envName` for both Vite and webpack/Rspack builds ([#34844](https://github.com/nuxt/nuxt/pull/34844)). This is the value set by `--envName` (or the resolved default), so you can branch on it in your app code: ```ts if (import.meta.envName === 'staging') { // enable staging-only behaviour } ``` ::read-more{to="https://nuxt.com/docs/api/advanced/import-meta"} :: ## 🔭 Tracing Channels for SSR Events Nuxt now publishes [diagnostics-channel](https://nodejs.org/api/diagnostics_channel.html) traces for its server-side subsystems ([#35191](https://github.com/nuxt/nuxt/pull/35191)). It's unopinionated: we emit `nuxt.render`, `nuxt.island`, `nuxt.data`, and `nuxt.plugin` channels following the [untracing](https://github.com/unjs/untracing) naming convention, and you can build OpenTelemetry (or anything else) on top. It works in Node, Deno, Bun, and Cloudflare Workers. ```ts [nuxt.config.ts] export default defineNuxtConfig({ // Turn on Nuxt's own channels tracingChannel: true, }) ``` You can also enable it granularly. In Nuxt v5, there will be additional Nitro-level channels: ```ts [nuxt.config.ts] export default defineNuxtConfig({ tracingChannel: { nuxt: true, }, }) ``` ::note Channel names, payload shapes, and option keys may still change while the [untracing](https://github.com/unjs/untracing) registry settles. :: ## 📦 Dependency Upgrades ### `unhead` v3 Nuxt's head management now runs on [`unhead` v3](https://unhead.unjs.io/docs/releases/v3) ([#34793](https://github.com/nuxt/nuxt/pull/34793)). It's smaller, uses a synchronous engine internally, and ships better type-safety for `useHead` out of the box. This also unblocks SSR streaming. ::warning `unhead` v3 introduces type-narrowing for `useHead`, which can be a **breaking type change** if you were relying on the looser v2 types. The runtime behaviour is compatible for the vast majority of apps, and promise input (deprecated in v2) is no longer supported. If you hit type errors after upgrading, they're almost always genuine tightening rather than regressions. :: ### `unctx` v3 We've moved to [`unctx` v3](https://github.com/unjs/unctx) ([#35541](https://github.com/nuxt/nuxt/pull/35541)), which resolves a class of long-standing async context issues ([#33644](https://github.com/nuxt/nuxt/issues/33644)). This is part of the composable-context reliability work that continues into v5. ### And more We've also updated `magic-string` to v1, Babel to v8, and pulled in the latest Rolldown across the board. Running `nuxt upgrade --dedupe` (see below) is the easiest way to pull these through cleanly. ## 🛠️ Nuxt CLI This release bundles some improvements from `@nuxt/cli` v3.36 and v3.37: - **`nuxt module remove`** to uninstall a module and clean up its config ([nuxt/cli#1306](https://github.com/nuxt/cli/pull/1306)), the natural companion to `nuxt module add`. - **Non-interactive `nuxt init`** for scripting and CI ([nuxt/cli#1341](https://github.com/nuxt/cli/pull/1341)), plus it now respects the template's own package manager instead of prompting ([nuxt/cli#1330](https://github.com/nuxt/cli/pull/1330)). - **Type-check onboarding**: `nuxt typecheck` now offers to install `vue-tsc` and `typescript` for you if they're missing ([nuxt/cli#1316](https://github.com/nuxt/cli/pull/1316)). - **Golar support for type-checking**: `nuxt typecheck` can now use [Golar](https://golar.dev) as an alternative to `vue-tsc` ([nuxt/cli#1362](https://github.com/nuxt/cli/pull/1362)). It's picked up automatically if it's installed (or a `golar.config.*` file exists), and you can force a checker with `--checker=vue-tsc` or `--checker=golar`: ```sh nuxt typecheck --checker=golar ``` - **Layer-aware dev server**: the dev server now reloads on changes to local layer `nuxt.config` files ([nuxt/cli#1345](https://github.com/nuxt/cli/pull/1345)). ## 🧩 TypeScript Plugin and Named Layout Slots Nuxt's experimental TypeScript plugin is powered by [`@dxup/nuxt`](https://github.com/KazariEX/dxup), and this release bundles a newer version of it. If you haven't tried it, turning on `experimental.typescriptPlugin` gives you a set of editor niceties: renaming an auto-imported component updates every usage, plus go-to-definition for glob imports, Nitro routes, `definePageMeta`, `runtimeConfig`, and typed route names. New in the bundled version is an opt-in **runtime** feature: named layout slots ([KazariEX/dxup#20](https://github.com/KazariEX/dxup/pull/20)). You can write a top-level named-slot template in a page and have it forwarded into the matching named slot of the active layout. That lets a page inject content into its layout's slots, which file-based layouts don't otherwise allow. Enable it alongside the plugin: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { typescriptPlugin: true, }, dxup: { features: { namedLayoutSlots: true, }, }, }) ``` Then a layout can expose named slots: ```vue [layouts/center.vue] <template> <slot /> <slot name="side" one="one" /> </template> ``` And any page using that layout can fill them: ```vue [pages/about.vue] <script setup lang="ts"> definePageMeta({ layout: 'center' }) </script> <template> <template #side="{ one }"> This "{{ one }}" comes from the layout slot. </template> <div>About page</div> </template> ``` ::read-more{to="https://nuxt.com/docs/guide/going-further/experimental-features#typescriptplugin"} :: ## 🔥 Performance and Reliability As always, a lot of work has gone into making Nuxt faster and steadier. One you can opt into today is the **shared file watcher** ([#35143](https://github.com/nuxt/nuxt/pull/35143)). Vite already runs a chokidar-based watcher, so Nuxt can piggy-back on it instead of spinning up a second one – less memory, fewer file handles. This becomes the default with `compatibilityVersion: 5`, but you can turn it on now and let us know how it goes: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { watcher: 'builder', }, }) ``` There are also some other good performance improvements that don't need to be opted into: - **Faster dev startup** – dev-only Nitro and pages work is now deferred and streamlined ([#35381](https://github.com/nuxt/nuxt/pull/35381), [#35383](https://github.com/nuxt/nuxt/pull/35383)). - **Leaner production builds** – the island-renderer chunk is skipped entirely when you use no islands ([#35456](https://github.com/nuxt/nuxt/pull/35456)), and plugin handling is tree-shaken out of prod builds ([#35278](https://github.com/nuxt/nuxt/pull/35278)). - **Improved island hashing** – more stable island and key hashing, with `getIslandHash` and `hashKey` now exposed ([#35583](https://github.com/nuxt/nuxt/pull/35583)). - **Concurrency and I/O wins** across Kit path resolution and Nitro inline styles ([#35511](https://github.com/nuxt/nuxt/pull/35511), [#35514](https://github.com/nuxt/nuxt/pull/35514)). A couple of other nice quality-of-life fixes: - `$fetch` is now auto-imported in user code, which fixes edge cases with top-level `$fetch.create` under Rolldown's output format ([#35581](https://github.com/nuxt/nuxt/pull/35581)). - Nuxt now honours system `HTTP_PROXY` / `HTTPS_PROXY` environment variables in the builder environment ([#35183](https://github.com/nuxt/nuxt/pull/35183)). - HMR now works for `defineNuxtComponent` in JSX ([#35620](https://github.com/nuxt/nuxt/pull/35620)). ## ⚠️ Heads-Up Before Upgrading As mentioned above, this release includes three major dependency bumps. For most apps they're transparent, but they're worth a moment of attention: - **Vite 8** – check custom Vite plugins and config against the [Vite migration guide](https://vite.dev/guide/migration). - **Rspack 2** – if you use `builder: 'rspack'`, the internals are now Rsbuild-based, so custom Rspack config may need review. - **`unhead` v3** – possible breaking *type* changes from stricter `useHead` typing. ## ⬆️ Upgrading Our recommendation for upgrading is to run: ```sh npx nuxt upgrade --dedupe # or, if you are staying on the 3.x line npx nuxt@latest upgrade --dedupe --channel=v3 ``` This will deduplicate your lockfile and help ensure you pull in updates from the other dependencies Nuxt relies on, particularly across the unjs ecosystem (which matters more than usual this release, given the major bumps). ::tip Check out our [upgrade guide](https://nuxt.com/docs/getting-started/upgrade) if you're upgrading from an older version. :: ## 👉 Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v4.5.0"} Read the full release notes of Nuxt `v4.5.0`. :: ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v3.21.9"} Read the full release notes of Nuxt `v3.21.9`. :: Thank you to all of the many contributors to this release. This was a big one, and it wouldn't happen without you. 💚 # Nuxt Security Patch Releases > Nuxt 4.5.1 and 3.21.10 are out now, fixing several security issues, alongside a critical fix in @nuxt/devtools 3.3.1. We recommend upgrading as soon as possible. We've released **Nuxt 4.5.1** and **Nuxt 3.21.10**, security patch releases for the 4.x and 3.x release lines. We recommend upgrading now: ```sh npx nuxt upgrade --dedupe ``` Refreshing your lockfile also pulls in `@nuxt/devtools@3.3.1`, which fixes a separate critical development-only issue, so make sure it updates too. ::note If you can't upgrade to the patched releases for some reason (for example, you're pinned to an older release line), Socket has provided [Certified Patches for Nuxt](https://socket.dev/blog/patches-for-nuxt-security-vulnerabilities) which can be applied across release lines as an interim measure. Upgrading remains the recommended fix. :: ::warning If you already upgraded for the earlier route rule advisory ([CVE-2026-53721](https://github.com/nuxt/nuxt/security/advisories/GHSA-mm7m-92g8-7m47)), you still need this release. One of the issues below is a regression introduced by that very fix, so being on 4.4.7+ or 3.21.7+ does not make you safe. :: ## 🔍 What's fixed The advisories linked below have the full details. In brief, with a note on whether you're likely to be affected: ### Server-side remote code execution via server island props (High) [GHSA-9473-5f9j-94wq](https://github.com/nuxt/nuxt/security/advisories/GHSA-9473-5f9j-94wq) You're affected only if you have enabled `vue.runtimeCompiler` (which is off by default) and use server components or islands whose props can reach Vue's dynamic component resolution: `<component :is>`, `resolveDynamicComponent`, `h()`, or a polymorphic `as` / `asChild` prop, such as the one `@nuxt/ui` exposes through `reka-ui`. In that setup, an attacker could inject a `template` key into island props and have Vue's runtime compiler execute arbitrary code in the server (Nitro) process. Because `vue.runtimeCompiler` is off by default, most applications are not affected, but where the preconditions are met the impact is full server-side remote code execution. ### Unauthorized component instantiation via server island props (Medium) [GHSA-48hr-524c-v5w3](https://github.com/nuxt/nuxt/security/advisories/GHSA-48hr-524c-v5w3) This is closely related, but does not require `vue.runtimeCompiler`, so it reaches more configurations. If a server island forwards an undeclared prop onto a polymorphic `as` root (the same `@nuxt/ui` / `reka-ui` pattern, including implicitly through Vue's attribute fallthrough), an attacker could pass a plain string such as `{ "as": "iframe" }`, or the name of a globally-registered component, to instantiate an arbitrary HTML element or component. It does not allow code execution. Declaring the props your island accepts, or setting `inheritAttrs: false` on it, also prevents this. ### Route rule authorization bypass (High) [GHSA-hxvh-4h3w-prp9](https://github.com/nuxt/nuxt/security/advisories/GHSA-hxvh-4h3w-prp9) You're affected if you rely on `routeRules` with `appMiddleware` as an authorization gate and any rule key contains an uppercase character, whether derived from a page file like `pages/Admin.vue` or written explicitly as `routeRules: { '/Admin': ... }`. Case-insensitive routing (the default) served the page while the rule and its middleware were silently skipped. This is the regression mentioned above. Route rules now match case-insensitively, as vue-router does by default. If you intentionally relied on case-sensitive route-rule matching, set `router.options.sensitive: true`. ### Server component denial of service (High) [GHSA-hxcr-hm88-mpq6](https://github.com/nuxt/nuxt/security/advisories/GHSA-hxcr-hm88-mpq6) and [GHSA-9pgf-384g-p7mv](https://github.com/nuxt/nuxt/security/advisories/GHSA-9pgf-384g-p7mv) You're affected if your app uses server components or islands (the `/__nuxt_island` endpoint). An unauthenticated request could crash the server by expanding a `v-for` over a prop, or waste CPU parsing and hashing an oversized request body before it was rejected. ### Cross-user payload disclosure on cached pages (High) [GHSA-wm8w-6qjm-cv43](https://github.com/nuxt/nuxt/security/advisories/GHSA-wm8w-6qjm-cv43), 4.x only (`>= 4.4.0`; the 3.x line is not affected) You're affected if you use the `cache`, `swr` or `isr` route rules on authenticated pages that render user-specific data. The cached `_payload.json` could be served to a different user or an unauthenticated visitor, even though the HTML was correctly varied. ::important After upgrading, purge any CDN or edge cache that may already hold a leaked `_payload.json`. The bad response can already be cached upstream, and upgrading alone won't evict it. :: ### Dev server path disclosure (Low) [GHSA-7c4v-fwgw-9rf7](https://github.com/nuxt/nuxt/security/advisories/GHSA-7c4v-fwgw-9rf7) You're affected only if you run the dev server bound to a network interface (`nuxi dev --host`) on an untrusted network. The Chrome DevTools workspace endpoint disclosed the project's absolute path and a workspace UUID to a LAN client. ### Nuxt DevTools remote code execution (Critical, dev only) [GHSA-279x-mwfv-vcqv](https://github.com/nuxt/nuxt/security/advisories/GHSA-279x-mwfv-vcqv), fixed in `@nuxt/devtools@3.3.1` You're affected if you run `nuxt dev` with DevTools enabled. An unauthenticated RPC method exposed over the Vite HMR socket could run an arbitrary command on your machine, reachable from another process on your host, a LAN peer when using `--host`, or a malicious site you visit while the dev server is running. This never affects production, but please make sure your lockfile picks up the new version. ## 💚 Thanks Huge thanks to the researchers who reported these issues responsibly: [Pig-Tail](https://github.com/Pig-Tail), [sec-reex](https://github.com/sec-reex), [DavidCarliez](https://github.com/DavidCarliez), [manop55555](https://github.com/manop55555), [dinhvaren](https://github.com/dinhvaren), [quantumshiro](https://github.com/quantumshiro), [Saku0512](https://github.com/Saku0512) and [TazmiDev](https://github.com/TazmiDev). These reports came in through GitHub's private advisory process, with one reaching us through the [Vercel OSS Bug Bounty program](https://vercel.com/blog/the-vercel-oss-bug-bounty-program-is-now-available) on HackerOne. For the server-side remote code execution, we also worked with teams at Vercel, Netlify and Cloudflare to provide early notice, so they could put protections in place ahead of this disclosure. If you believe you've found a vulnerability in Nuxt, please [report it privately via the Security tab](https://github.com/nuxt/nuxt/security/advisories/new) on the relevant repository, or email [security@nuxtjs.org](mailto:security@nuxtjs.org) if that's not possible. We really appreciate responsible disclosure. # Nuxt 4.6 > Nuxt 4.6 makes Nuxt server-agnostic with nuxt/server, rebuilds typed $fetch, adds sessions, Vue Vapor support and new dev error pages, and ships alongside Nuxt CLI v4. Nuxt 4.6 is here, and it brings Nuxt CLI v4 with it. It is one of our biggest minor releases, with over 420 commits since v4.5.2, and it marks our first big step towards a server-agnostic Nuxt. ## 📣 Some News ### Nuxt CLI v4 Alongside Nuxt 4.6, today also brings a new major release of the Nuxt CLI: `@nuxt/cli` v4. It ships as a dependency of `nuxt`, so you'll get it automatically when you upgrade. Most of what's new is in `nuxt dev`. ![nuxt dev in Nuxt CLI v4](https://nuxt.com/assets/blog/v4-6/nuxt-dev.svg) There's now an interactive panel pinned to the bottom of your terminal with URLs, startup progress and single-key shortcuts (`r` to restart, `o` to open, `l` for logs, `n` for requests, `p` for pages and server routes). Every request gets an id, so logs and errors are attributed to the request that caused them. If you prefer the classic output, pass `--no-tui`. The dev server also tells you why it reloaded or restarted, which `nuxt.config` keys changed, where the time went during a slow start, and how long each module took to set up. A save that changes nothing no longer restarts the server, and hard restarts keep serving on the same port. Errors go through a single CLI-level channel rendered with `my-bad` ([more below](#better-errors-in-development)). A lock file in `.nuxt/` lets a second `nuxt dev` (say, one started by an agent) take over from or defer to the one you started. The same lock file powers new `nuxt curl` and `nuxt task` commands that talk to the running server, and `nuxt docs "<query>"` searches the docs for your installed Nuxt version. You can also use `nuxt preview --takeover` to replace a running preview server. It's also a lot smaller and starts a lot faster: | | v3.37 | v4.0 | | | ---------------------------------- | ------- | ------ | --------------- | | `@nuxt/cli` install size | 13.1 MB | 3.5 MB | **-73%** | | `@nuxt/cli` dependencies | 70 | 31 | **-56%** | | `nuxt dev`: first paint | 330 ms | 50 ms | **6.6x faster** | | `nuxt dev`: port bound | 338 ms | 104 ms | **3.2x faster** | | `nuxt dev`: memory at rest (Linux) | 630 MB | 440 MB | **-30%** | Although this is a major version, none of the changes should be breaking for Nuxt v4 users: the CLI requires Node.js v22.21+, v24.11+ or v26+, `nuxt init` has been dropped in favour of `npm create nuxt@latest`, and Nuxt 2 and `@nuxt/bridge` are no longer supported. ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/cli/releases/tag/v4.0.0"} Read the full Nuxt CLI v4 release notes. :: ### A Server-Agnostic Nuxt The biggest thing about this release is our move towards making Nuxt server-agnostic. Freedom of choice is a fundamental value of the web, and one that unites the whole Nuxt team. You can use `pages/` (with vue-router) or not. You can use Vite, webpack or Rspack to bundle your code. You can pick from dozens of providers to deploy to, pick any image or font provider, choose any database adapter. In every case, the framework is the same. The server side was different. `#app` composables imported h3 types, server code imported from `h3` and `nitropack`, and every module that touched the server was tied to whichever major version of those packages Nuxt happened to depend on. This has become particularly clear as we have been upgrading to new majors of h3 and Nitro, which ship breaking changes with a cascading effect throughout the whole ecosystem. This release changes that. Alongside explicitly defining our public API in `@nuxt/kit` (which now does not refer to external packages), Nuxt now specifies its own types for the request event, route rules and typed `$fetch`, and exposes an import surface, [`nuxt/server`](#nuxtserver), for the server utilities most apps need. It is the culmination of work we started almost a year ago to make it possible to use any server builder with Nuxt, not just Nitro ([#33462](https://github.com/nuxt/nuxt/pull/33462)). Under the hood, `nuxt/server` is still powered by Nitro by default. We are also announcing a second, experimental implementation, [`@nuxt/vite-server`](#experimental-nuxtvite-server), which allows pure-Vite server builds using the [Vite Environment API](https://vite.dev/guide/api-environment#environment-api). ::important We believe that Nitro is still the right choice for almost everyone. :: We see a number of key benefits for `nuxt/server`: 1. **It smooths the upgrade to Nuxt 5**, which moves to Nitro v3 and h3 v2. Server code written against `nuxt/server` on 4.6 runs unchanged there, so a module can ship one file for both. 2. **It decouples Nuxt from the Nitro release cycle.** Because we own the API, we can adapt to breaking changes in Nitro or h3 without requiring a new major, and we can release Nuxt majors without waiting for upstream releases. 3. **It makes Nuxt's code more maintainable.** It preserves the separation of concerns between our API (`nuxt/app` and `nuxt/server`) and the bundler and server you ultimately build your app with. There are other benefits too, from a single type surface to being able to iterate more quickly on features. Finally, a special thank-you to [@pi0](https://github.com/pi0), whose relentless focus on server agnosticism and work on h3, Nitro and web-standard server primitives over the last few years is what makes a portable `RequestEvent` possible at all. Thank you, Pooya. ❤️ Almost every feature in this release is already on the Nuxt 5 branch, and you can try most of the remaining Nuxt 5 defaults today with `future.compatibilityVersion: 5` ([more below](#nuxt-5-features-today)). ### Nuxt 3 End-of-Life Nuxt 3 reached end-of-life on July 31, 2026, so there is no 3.x release alongside this one. If you're still on v3, the [upgrade guide](https://nuxt.com/docs/getting-started/upgrade) is waiting for you. If you've read this far, I'm afraid I have bad news for you: there's a lot more still to say. You might want to grab a coffee. ☕️ ## 🧩 `nuxt/server` It has been asked for for a long time, and it now exists ([#36275](https://github.com/nuxt/nuxt/pull/36275))! 🎉 `nuxt/server` is a new import source for server code: handlers, middleware and utilities. It is the complement to `nuxt/app`, which is for the part of your application that also runs in the browser. ```ts [server/api/hello.ts] import { defineEventHandler, getQuery } from 'nuxt/server' export default defineEventHandler((event) => { const { name } = getQuery<{ name?: string }>(event) return { message: `Hello, ${name ?? 'world'}!` } }) ``` The utilities use web standards and are typed against a portable `RequestEvent`: ```ts event.req // Request event.url // URL event.res // { status, statusText, headers } event.res.headers // Headers event.context // per-request context ``` Under `@nuxt/nitro-server` these are implemented with Nitro and h3, but your code doesn't import from either. So the same handler runs under Nitro v2, Nitro v3 or `@nuxt/vite-server`, and a module that imports from `nuxt/server` doesn't need a peer dependency on `h3` or `nitropack`. We think this will make a big difference in smoothing out the upgrade to Nuxt v5 and Nitro v3. Types get simpler too. We no longer hoist h3 or Nitro types into your app to type `useRequestEvent`, `$fetch` or route rules, which removes a source of conflicts when versions differ. The surface is small, and covers what published modules and user code typically need: `defineEventHandler`, `createError`/`isNuxtError`, request URL, headers, query, body (plain, or validated with any [Standard Schema](https://standardschema.dev) library or a function), cookies, redirects, response status, `getRouterParam(s)`, `getRequestIP`, `handleCors`, `getRouteRules`, `useRuntimeConfig`, `useAppConfig` and sessions. ```ts [server/api/users.post.ts] import { defineEventHandler, readValidatedBody } from 'nuxt/server' import { z } from 'zod' export default defineEventHandler(async (event) => { const user = await readValidatedBody(event, z.object({ name: z.string() })) return { created: user.name } }) ``` We encourage you to use web APIs (`event.req.headers`, for example), or to raise an issue if there's functionality you're missing from `nuxt/server`. 🙏 If you do need to step outside `nuxt/server` for a particular handler, don't worry. Nothing has been taken away: import `defineEventHandler` and the helpers you need from `h3` or `nitropack/runtime` as before, and that handler works exactly as it did on Nuxt 4.5. ```ts [server/api/upload.post.ts] import { defineEventHandler, readMultipartFormData } from 'h3' export default defineEventHandler(async (event) => { const parts = await readMultipartFormData(event) return { received: parts?.length ?? 0 } }) ``` ::important On Nuxt 4, the *auto-imported* `defineEventHandler`, `getQuery` and `readBody` (and the rest of that family) are still h3's own helpers, which take a different shape of event. Import from `nuxt/server` explicitly, including `defineEventHandler`, to use the new server runtime. If you mix the two, you'll see a `NUXT_E8012` error which should tell you which import to change. :: A few helpers also behave differently from their h3 v1 namesakes: `sendRedirect` returns the response rather than sending it, `createError` takes `status` and `statusText`, and response headers are set through `event.res.headers`. The upgrade guide has [a table of the differences](https://nuxt.com/docs/getting-started/upgrade#moving-to-nuxtserver). Nothing in this release *requires* a migration. But if you have server code you'd like to make portable ahead of Nuxt 5, this is the best way. ::read-more{to="https://nuxt.com/docs/guide/going-further/server-imports"} Read the server imports guide. :: ## 🔐 `appSecret` and Sessions Thanks to [@onmax](https://github.com/onmax), Nuxt now has a root application secret: `runtimeConfig.appSecret`, set with `NUXT_APP_SECRET` ([#35874](https://github.com/nuxt/nuxt/pull/35874)). Modules and server features derive their own secrets from it with `deriveSecret(purpose)`, so it's the only secret you need to configure. ```bash openssl rand -base64 32 ``` In development, Nuxt generates and persists one if none is configured, and warns the first time a derived secret is used. Builds never generate one. The first feature to use it is a set of session helpers in `nuxt/server` ([#36358](https://github.com/nuxt/nuxt/pull/36358)). Sessions are sealed into a cookie with [iron](https://github.com/brc-dd/iron-webcrypto), so there's no server-side storage to set up: ```ts [server/api/visits.ts] import { defineEventHandler, useSession } from 'nuxt/server' export default defineEventHandler(async (event) => { const session = await useSession<{ visits: number }>(event) await session.update(data => ({ visits: (data.visits ?? 0) + 1 })) return { visits: session.data.visits } }) ``` ::note As a reminder, runtime config keys prefixed with `app` (`runtimeConfig.app`, `runtimeConfig.appSecret`) are reserved for Nuxt. :: ## 🎯 Typed `$fetch`, Rebuilt `$fetch` and `useFetch` have been typed from your server routes for a long time. The types came from Nitro's `InternalApi` interface, and past a few hundred routes they hit TypeScript's instantiation limit: ``` error TS2589: Type instantiation is excessively deep and possibly infinite. ``` We've rebuilt typed fetch on top of [`fetchdts`](https://github.com/danielroe/fetchdts) ([#36238](https://github.com/nuxt/nuxt/pull/36238)). Nuxt compiles your server routes into a route tree with an exact-match table for static paths and accessors specialised to your route set. Resolution cost now scales with call sites, not with route count: | routes | before | after | | --------------------- | -------------------------------- | --------------- | | 100 | 1,397,361 instantiations / 0.77s | 51,558 / 0.26s | | 300 | 5,774,425 / 3.49s (`TS2589`) | 51,558 / 0.16s | | 1000 | 12,831,467 / 7.44s (`TS2589`) | 51,558 / 0.20s | | 3000 (200 call sites) | 71,875,148 / 53.63s (`TS2589`) | 101,678 / 0.62s | Peak memory for the same runs dropped from 946 MB to 140 MB. 🔥 Plus, the route set also carries the `body`, `query` and `headers` each handler validates, so calls are checked against them: ```ts // server/api/users.post.ts validates { title: string, count: number } await $fetch('/api/users', { method: 'post', body: { title: 'a', count: 1 } }) await $fetch('/api/users', { method: 'post', body: { title: 'a', count: 'no' } }) // ^ not assignable to number await $fetch('/api/users', { method: 'post' }) // ^ body is required ``` ![Type error on a $fetch call with the wrong body shape](https://nuxt.com/assets/blog/v4-6/typed-fetch-error-light.png){.dark:hidden} ![Type error on a $fetch call with the wrong body shape](https://nuxt.com/assets/blog/v4-6/typed-fetch-error-dark.png){.hidden.dark:block} On Nuxt 4 this is opt-in, because there are small changes to type inference (a `Date` returned by a handler is a `string` at the call site, for example), and hand-written `ServerRoutes` augmentations need a small rewrite. It's the default in Nuxt 5. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { routeTypedFetch: true, }, }) ``` There's also a new `experimental.strictRouteTypes` option to reject calls to paths that don't exist (otherwise these just return `unknown`), and an `'isomorphic'` mode that types your pages as `GET` routes too, if you want to `$fetch` them from the Vue renderer with type safety. ::read-more{to="https://nuxt.com/docs/guide/going-further/experimental-features#routetypedfetch"} Read more about `routeTypedFetch`. :: ## 🐛 Better Errors in Development Server-side errors in development used to look like this: ``` ReferenceError: foo is not defined at Object.<anonymous> (/_nuxt/app/pages/index.vue:1:1) ``` There was no source position or code frame, and the Youch iframe we rendered could not show you the frame in your own source either. Both are now fixed ([#36258](https://github.com/nuxt/nuxt/pull/36258), [nuxt/cli#1518](https://github.com/nuxt/cli/pull/1518)). Dev SSR stack traces are now mapped before anything reads the error, and the Youch overlay has been replaced with [`my-bad`](https://github.com/danielroe/my-bad). It renders as an overlay in your app's own error page (or as a standalone page when the app can't render one), with the mapped stack trace and a code frame from your source. ![my-bad error overlay with a mapped stack trace and code frame](https://nuxt.com/assets/blog/v4-6/my-bad-ssr-expanded-light.png){.dark:hidden} ![my-bad error overlay with a mapped stack trace and code frame](https://nuxt.com/assets/blog/v4-6/my-bad-ssr-expanded-dark.png){.hidden.dark:block} There's a **Copy error** button with a few formats, including a prompt you can hand straight to an agent: ![The my-bad copy menu, with Markdown, agent prompt and JSON formats](https://nuxt.com/assets/blog/v4-6/my-bad-ssr-copymenu-dark.png) The same report is printed in your terminal: ![my-bad error report rendered in the terminal](https://nuxt.com/assets/blog/v4-6/my-bad-terminal-light.png){.dark:hidden} ![my-bad error report rendered in the terminal](https://nuxt.com/assets/blog/v4-6/my-bad-terminal-dark.png){.hidden.dark:block} With Nuxt CLI v4, errors also go through a single live channel at the CLI level, which survives worker restarts. A syntax error in `nuxt.config.ts` (or any other fatal startup error) is served as a page, and the page reloads once you fix it. Each error is rendered once, rather than at every layer it passes through. ![my-bad page for a module that could not be loaded from nuxt.config.ts](https://nuxt.com/assets/blog/v4-6/my-bad-fatal-light.png){.dark:hidden} ![my-bad page for a module that could not be loaded from nuxt.config.ts](https://nuxt.com/assets/blog/v4-6/my-bad-fatal-dark.png){.hidden.dark:block} We're working with [@atinux](https://github.com/atinux), [@HugoRCD](https://github.com/HugoRCD) and [@antfu](https://github.com/antfu) to make these pages nicer still. ## 🎨 A New Loading Screen, 404 and Error Pages [@HugoRCD](https://github.com/HugoRCD) has redrawn the loading screen you see while the dev server starts. It's now a WebGL2 particle field that traces a mountain range behind the Nuxt lockup ([#36178](https://github.com/nuxt/nuxt/pull/36178)), using a single shader and a single draw call. Without WebGL2 it falls back to the static lockup, and with `prefers-reduced-motion` the animation stops. Try hovering over it. 🏔️ <!-- TODO: past posts host video on Cloudinary; consider uploading these mp4s (3.6 MB + 5.9 MB) there rather than committing them --> ::themed-video ```yaml [props] light: /assets/blog/v4-6/loading-screen-light.mp4 light-poster: /assets/blog/v4-6/loading-screen-light.png dark: /assets/blog/v4-6/loading-screen-dark.mp4 dark-poster: /assets/blog/v4-6/loading-screen-dark.png controls: true autoplay: true loop: true class: rounded-lg ``` :: Hugo also gave the built-in 404 and error pages a neutral palette and lighter type ([#36255](https://github.com/nuxt/nuxt/pull/36255)), and [@MirkoJa](https://github.com/MirkoJa) added a back button to the 404 page ([#35688](https://github.com/nuxt/nuxt/pull/35688)). ![New 404 page with a back button](https://nuxt.com/assets/blog/v4-6/404-page-light.png){.dark:hidden} ![New 404 page with a back button](https://nuxt.com/assets/blog/v4-6/404-page-dark.png){.hidden.dark:block} ![New error page](https://nuxt.com/assets/blog/v4-6/error-page-prod-light.png){.dark:hidden} ![New error page](https://nuxt.com/assets/blog/v4-6/error-page-prod-dark.png){.hidden.dark:block} With `experimental.prerenderErrorPages`, you can prerender error pages as real HTML (`404.html`, or any status codes you pass) instead of an empty SPA shell ([#35193](https://github.com/nuxt/nuxt/pull/35193), thanks to [@Flo0806](https://github.com/Flo0806)). ## ⚡️ Vue Vapor Support Nuxt now supports Vue 3.6's [Vapor Mode](https://github.com/vuejs/core/releases/tag/v3.6.0-rc.1#about-vapor-mode) in interop mode ([#35759](https://github.com/nuxt/nuxt/pull/35759)). Your app root stays on the virtual DOM, and you can opt individual components or pages into Vapor with the `vapor` attribute on `<script setup>`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ vue: { vapor: true, }, }) ``` ```vue [app/pages/index.vue] <script setup vapor lang="ts"> const count = ref(0) </script> <template> <button @click="count++"> count is {{ count }} </button> </template> ``` Routing, `useAsyncData`, layouts and most built-in components work without changes. To get there, we taught the auto-import loader, `useAsyncData`, `definePageMeta` and slot inspection about Vapor, sent some fixes upstream to Vue, and added a Vapor test suite to track what's supported. ::note This requires Vue `^3.6.0-rc.2` or newer. See the [known limitations](https://nuxt.com/docs/guide/concepts/vuejs-development#known-limitations), and try it in a fresh project first. :: ## 🔮 Nuxt 5 Features, Today Most of what's new in Nuxt 5 is already in 4.6, either on by default or behind a flag. `future.compatibilityVersion: 5` turns on the Nuxt 5 defaults at once, and you can still toggle each one individually. ```ts [nuxt.config.ts] export default defineNuxtConfig({ future: { compatibilityVersion: 5, }, }) ``` This release adds these to the flag: - typed pages (`experimental.typedPages`) - typed `$fetch` (`experimental.routeTypedFetch`), described above - case-sensitive routing, matching Nitro ([#35650](https://github.com/nuxt/nuxt/pull/35650), thanks to [@Mateleo](https://github.com/Mateleo)) - `experimental.navigateToEarlyReturn`: calling `navigateTo` in `<script setup>` skips the rest of setup, so redirects and 404s don't throw on missing data - build-time extraction of serializable `definePageMeta` keys (`experimental.extractSerializablePageMeta`, [#35919](https://github.com/nuxt/nuxt/pull/35919)) - `experimental.payloadExtraction: 'client'` - no `baseUrl` in generated tsconfigs ([#36040](https://github.com/nuxt/nuxt/pull/36040), thanks to [@oritwoen](https://github.com/oritwoen)) - inline error rendering (`experimental.inlineErrorRendering`): when a server render fails, `error.vue` is rendered in the same request with a plain try/catch instead of an internal request to `/__nuxt_error`, so headers and cookies already set are kept, the error render no longer passes through Nitro middleware and route rules a second time, and `render:html` fires with the original event ([#36399](https://github.com/nuxt/nuxt/pull/36399)) - normalized page names, `clearNuxtState` resetting to defaults, and `experimental.watcher: 'builder'` - no auto-imported server-only head composables (`useServerHead`, `useServerHeadSafe` and `useServerSeoMeta`) The upgrade guide lists [exactly what the flag changes on Nuxt 4](https://nuxt.com/docs/getting-started/upgrade#testing-nuxt-5). ## 🧪 Experimental: `@nuxt/vite-server` `server.builder` has been configurable since v4.2. This release adds a second server builder: `@nuxt/vite-server`, which builds a Nuxt app with Vite alone ([#36218](https://github.com/nuxt/nuxt/pull/36218), [#36279](https://github.com/nuxt/nuxt/pull/36279), [#36288](https://github.com/nuxt/nuxt/pull/36288)). This is all you need to configure it: ```ts [nuxt.config.ts] export default defineNuxtConfig({ server: { builder: 'vite', // 'nitro' is the default }, }) ``` It can produce a client-only SPA, a server-rendered app with a small Node entry, a web-standard `fetch` handler for platforms that provide the server (there are e2e examples for [Cloudflare Workers](https://developers.cloudflare.com/workers/vite-plugin/), [Netlify](https://github.com/netlify/framework-adapters/tree/main/packages/vite-plugin) and [universal deploy](https://github.com/universal-deploy/universal-deploy)), as well as fully static output with `nuxt generate`. Right now, this helps keep Nuxt's code agnostic, enforces the contract behind `nuxt/server`, and gives Vite plugins that provide a deploy target something to build on. It doesn't have Nitro's full feature set (there is no storage, caching, tasks or server plugins), and we expect most apps to keep using Nitro. **Nitro remains the default.** ::warning This is highly experimental and the API will change. `'nitro'` and `'vite'` are new shorthands for `@nuxt/nitro-server` and `@nuxt/vite-server`. :: ## 🚀 Performance Here's how 4.6 compares to 4.5.2 on our benchmark machine (arm64 Linux, Node.js 24.15, medians of five runs): | | v4.5.2 | v4.6.0 | | | ---------------------------------------------------- | --------- | --------- | -------- | | `@nuxt/kit` install size | 6.4 MB | 2.1 MB | **-67%** | | `@nuxt/kit` transitive dependencies | 36 | 22 | **-39%** | | `nuxt build`, starter | 4.4 s | 3.6 s | **-18%** | | `nuxt build`, 200 pages / 200 components / 50 routes | 11.6 s | 10.3 s | **-11%** | | SSR throughput, page with 300 `<NuxtLink>`s | 110 req/s | 140 req/s | **+26%** | Dev server start-up (spawn to first HTML) is about 12% faster on a starter app, and the second request takes roughly half the time. The CLI major changed at the same time, so not all of that is Nuxt. Large apps start about as fast as on 4.5.2. The `<NuxtLink>` number comes from [#36015](https://github.com/nuxt/nuxt/pull/36015). Internal links now render on the server as a plain `<a>`, with no `useLink` and no reactive state. Rendering 200 links went from 1.36ms to 0.57ms, which is faster than a bare `<RouterLink>`. With `experimental.early404` ([#36117](https://github.com/nuxt/nuxt/pull/36117)), page routes are compiled into a static matcher at build time. A request that can't match any page skips creating the Vue app and running plugins and middleware. On an app with 30 pages and about 23ms of boot work per render, a JSON 404 went from 37.1ms to 0.3ms. Templates now declare what invalidates them ([#35875](https://github.com/nuxt/nuxt/pull/35875)). Before, every file change regenerated all of them. Now editing a component regenerates 0 of 49 core templates, and the `builder:watch` hook settles in 0.6ms instead of 36ms. `useCookie` parses the cookie header once per request, or once per microtask on the client ([#36114](https://github.com/nuxt/nuxt/pull/36114)). This also means a cookie set in a plugin during SSR can be read by a later `useCookie` in a page. Smaller changes: `ssr: false` pages are tree-shaken from the server bundle ([#35836](https://github.com/nuxt/nuxt/pull/35836), thanks to [@Austin1serb](https://github.com/Austin1serb)), `unctx` is no longer shipped to the browser and `defu` is skipped for a single `app.config` ([#36371](https://github.com/nuxt/nuxt/pull/36371)), Vite warms the server module graph before the client, crawls the client graph during warm-up, and yields to your first navigation ([#36154](https://github.com/nuxt/nuxt/pull/36154), [#35870](https://github.com/nuxt/nuxt/pull/35870), [#36412](https://github.com/nuxt/nuxt/pull/36412)), and `@nuxt/kit` has dropped `c12`, `untyped`, `confbox`, `pkg-types`, `ufo` and `mlly`, with `jiti` and `giget` now optional peers. ### Lighter Payloads `useAsyncData` and `useFetch` accept `serialize: false` to keep data out of the payload ([#35779](https://github.com/nuxt/nuxt/pull/35779)). This is mostly useful inside components that never hydrate, and `experimental.stripNeverHydratedData` applies it automatically within `hydrate-never` component trees. ```ts const { data } = await useFetch('/api/stats', { serialize: false }) ``` In development, Nuxt now warns when a page's payload exceeds 100 kB, and when a route rendered with `noScripts` relies on client-side JavaScript. These pages also keep their non-script resource hints and styles ([#35803](https://github.com/nuxt/nuxt/pull/35803), [#36356](https://github.com/nuxt/nuxt/pull/36356), [#36359](https://github.com/nuxt/nuxt/pull/36359)). `<NuxtLink>` also prefetches server page islands, and [@atinux](https://github.com/atinux) made prefetch hints throttled and prioritised so a page full of links doesn't flood the network ([#36261](https://github.com/nuxt/nuxt/pull/36261), [#36324](https://github.com/nuxt/nuxt/pull/36324)). Building on that, route chunks, layouts, middleware, payloads, islands and resource hints now all go through one client prefetch scheduler, with per-kind concurrency caps, deduplication by key and cancellation of in-flight work when you navigate away ([#36391](https://github.com/nuxt/nuxt/pull/36391)). ## 🧩 Addons for `useFetch` and `useAsyncData` [@cernymatej](https://github.com/cernymatej) has added an `addons` option to the `createUseFetch` and `createUseAsyncData` factories ([#35797](https://github.com/nuxt/nuxt/pull/35797)). An addon can declare custom call-site options, adjust the merged options, wrap the handler with middleware, and extend what the composable returns, and it can be reused across as many custom instances as you like. ```ts [app/composables/useApiFetch.ts] const refreshOnFocus = defineUseFetchAddon({ setup: (options: UseFetchAddonOptions<{ refreshOnFocus?: boolean }>) => { if (import.meta.server || !options.refreshOnFocus) { return } return (asyncData) => { const focused = useWindowFocus() watch(focused, focused => focused && asyncData.refresh()) return { focused } } }, }) export const useApiFetch = createUseFetch({ baseURL: '/api', addons: [refreshOnFocus] }) ``` ```vue [app/pages/todos.vue] <script setup lang="ts"> const { data, focused } = await useApiFetch('/todos', { refreshOnFocus: true }) </script> ``` This resolves a long list of feature requests for `useAsyncData` and `useFetch` (refresh on focus, polling, retries, auth headers and more) without making the core composables opinionated about any of them. ::read-more{to="https://nuxt.com/docs/api/utils/define-use-fetch-addon"} Read about `defineUseFetchAddon` and `defineUseAsyncDataAddon`. :: ## 🛠️ Developer Experience File paths in terminal output are now clickable ([#35898](https://github.com/nuxt/nuxt/pull/35898)). A warning that mentions a file links to it in your editor, at the relevant line when we know it. ![Clickable file paths in a terminal stack trace](https://nuxt.com/assets/blog/v4-6/clickable-paths-stacktrace-light.png){.dark:hidden} ![Clickable file paths in a terminal stack trace](https://nuxt.com/assets/blog/v4-6/clickable-paths-stacktrace-dark.png){.hidden.dark:block} Hovering a built-in component like `<NuxtLayout>` or `<NuxtLink>` in a template now shows a short description and a link to its docs ([#35701](https://github.com/nuxt/nuxt/pull/35701), thanks to [@Ibochkarev](https://github.com/Ibochkarev)). <!-- NOTE for Daniel: this hover-docs image is a mock-up, not a real editor capture --> ![Hover docs for NuxtLink in the editor](https://nuxt.com/assets/blog/v4-6/ide-hover-docs-nuxt-link-light.png){.dark:hidden} ![Hover docs for NuxtLink in the editor](https://nuxt.com/assets/blog/v4-6/ide-hover-docs-nuxt-link-dark.png){.hidden.dark:block} Other changes: - There's a new top-level `prerender` option, an alias for `nitro.prerender` in the same way `runtimeConfig` and `routeRules` are top-level ([#32356](https://github.com/nuxt/nuxt/pull/32356)). Nuxt now also nudges you towards top-level options where they exist, since those work across server builders ([#36416](https://github.com/nuxt/nuxt/pull/36416)). - Linking to a file in `public/` with `<NuxtLink>` (or from Markdown with Nuxt Content) no longer needs `external`: if the router has no route for the path, Nuxt falls through to a full-page load instead of rendering your 404 ([#36169](https://github.com/nuxt/nuxt/pull/36169)). - Renaming a component in development refreshes its imports, so you no longer see stale references to the old file ([#36165](https://github.com/nuxt/nuxt/pull/36165), thanks to [@oritwoen](https://github.com/oritwoen)). - Layers installed from `node_modules` have their dependencies pre-bundled by Vite ([#36208](https://github.com/nuxt/nuxt/pull/36208)), and symlinked layer directories resolve to their real path ([#36402](https://github.com/nuxt/nuxt/pull/36402), thanks to [@silverbackdan](https://github.com/silverbackdan)). - `app/types/` and `server/types/` are included in the right tsconfig, so ambient types and augmentations there are picked up ([#35783](https://github.com/nuxt/nuxt/pull/35783)). [@Flo0806](https://github.com/Flo0806) did this, and also added an augmentable `NuxtPageMeta` interface for typing `NuxtPage.meta` ([#34816](https://github.com/nuxt/nuxt/pull/34816)). - A `(group)/` folder inside `components/` is left out of the component name ([#35699](https://github.com/nuxt/nuxt/pull/35699), thanks to [@abaza738](https://github.com/abaza738)). - `useCookie` accepts a function for `expires` ([#35628](https://github.com/nuxt/nuxt/pull/35628), thanks to [@DarlanPrado](https://github.com/DarlanPrado)). - Nuxt warns when a `public/` file shadows an application route ([#35674](https://github.com/nuxt/nuxt/pull/35674), thanks to [@Norbiros](https://github.com/Norbiros)). - `typescript.tsConfig` is now the shared baseline for every generated tsconfig, with `appTsConfig` and `serverTsConfig` for per-context overrides ([#35697](https://github.com/nuxt/nuxt/pull/35697), thanks to [@chairulakmal](https://github.com/chairulakmal)). - [@DamianGlowala](https://github.com/DamianGlowala) exported the `$Fetch` type from `nuxt/app` ([#35625](https://github.com/nuxt/nuxt/pull/35625)) and added `ShallowRef` to the Vue auto-import preset ([#36266](https://github.com/nuxt/nuxt/pull/36266)). ## 🧰 For Module Authors If you maintain a module with server code, you can now make it compatible with both Nuxt 4 and Nuxt 5 without a major bump. (We'll also be opening PRs proactively after the release of Nuxt 4.6 to help modules prepare for Nuxt 5.) `addServerHandler`, `addDevServerHandler` and `addNitroPlugin` accept a map of variants per server API ([#36317](https://github.com/nuxt/nuxt/pull/36317)), and Nuxt chooses the most appropriate one: ```ts [module.ts] addServerHandler({ route: '/api/my-module/status', handler: { nuxt: resolve('./runtime/server/status'), nitro2: resolve('./runtime/server/status.legacy'), }, }) ``` A handler that imports only from `nuxt/server` doesn't need Nitro 2/3 variants at all (but does require Nuxt 4.6+). Nuxt reads the file's imports to work out which API it uses, and where that isn't enough you can declare `meta.compatibility.server`. `getNitroVersion` and `hasNitroVersion` are also there in case you have logic that *explicitly* needs to know which Nitro version is installed ([#36127](https://github.com/nuxt/nuxt/pull/36127)). ::read-more{to="https://nuxt.com/docs/guide/modules/server-compatibility"} Read the server compatibility guide. :: Other additions for module authors: - augmentable server types owned by Nuxt: `ServerTypes`, `ServerRoutes`, `AppRouteRules` and `NuxtRequestContext` ([#36293](https://github.com/nuxt/nuxt/pull/36293)) - `deriveSecret`, for a module-specific secret derived from `appSecret` - `useTerminal`, for prompts, status messages and task progress that the CLI's dev panel renders when there is one ([#36162](https://github.com/nuxt/nuxt/pull/36162)) - `module:before` and `module:done` hooks, which Nuxt CLI v4 uses to show per-module setup time ([#36173](https://github.com/nuxt/nuxt/pull/36173)) - `onConfigResolved` and `diffNuxtConfig`, to see what changed between two config loads ([#35853](https://github.com/nuxt/nuxt/pull/35853)) - `ensureDependencyInstalled` and `getAddDependencyCommand`, to offer optional dependencies using the user's package manager ([#34554](https://github.com/nuxt/nuxt/pull/34554)) - template `dependencies`, to say what should invalidate a template ([#35875](https://github.com/nuxt/nuxt/pull/35875)) - `addServerImports`, `addServerImportsDir` and `addServerTemplate` across Nitro versions, with generated server tsconfigs and versioned route config types ([#36265](https://github.com/nuxt/nuxt/pull/36265)) - an explicit public API for `@nuxt/kit` ([#36074](https://github.com/nuxt/nuxt/pull/36074)), with `@nuxt/schema` now an optional peer - modules can set `experimental.asyncContext` ([#36175](https://github.com/nuxt/nuxt/pull/36175), thanks to [@cernymatej](https://github.com/cernymatej)) - Vite plugins added via kit are registered at the top level ([#36037](https://github.com/nuxt/nuxt/pull/36037)), and more build-time warnings use [diagnostic codes](https://nuxt.com/docs/errors) ([#36138](https://github.com/nuxt/nuxt/pull/36138)) - wider `@nuxt/kit` peer dependency ranges ([#36417](https://github.com/nuxt/nuxt/pull/36417)), and `updateRuntimeConfig` can be called before Nitro exists without a warning ([#36403](https://github.com/nuxt/nuxt/pull/36403), thanks to [@Neekoras](https://github.com/Neekoras)) ## 🩹 Fixes and Security [@oritwoen](https://github.com/oritwoen) fixed scoped styles in server component slots, duplicate island asset requests and blank remounts with lazy hydration ([#36047](https://github.com/nuxt/nuxt/pull/36047), [#36048](https://github.com/nuxt/nuxt/pull/36048), [#36051](https://github.com/nuxt/nuxt/pull/36051)). [@hdwebpros](https://github.com/hdwebpros) made `useRequestFetch` forward request headers ([#36180](https://github.com/nuxt/nuxt/pull/36180)), [@Askerka00](https://github.com/Askerka00) fixed view transitions that are skipped mid-navigation ([#35537](https://github.com/nuxt/nuxt/pull/35537)), [@Flo0806](https://github.com/Flo0806) made changed auto-import sources rescan before their consumers ([#33671](https://github.com/nuxt/nuxt/pull/33671)), and [@onmax](https://github.com/onmax) preserved error causes in development ([#35632](https://github.com/nuxt/nuxt/pull/35632)). Other fixes cover awaited `useAsyncData` during hydration and lazy fetching ([#36124](https://github.com/nuxt/nuxt/pull/36124), [#36301](https://github.com/nuxt/nuxt/pull/36301)), `navigateTo` path encoding and `baseURL` with `open` ([#36055](https://github.com/nuxt/nuxt/pull/36055), [#36112](https://github.com/nuxt/nuxt/pull/36112), [#36197](https://github.com/nuxt/nuxt/pull/36197)), CSS extraction and inlined styles, and interrupted view transitions ([#36314](https://github.com/nuxt/nuxt/pull/36314)). On the security side, the internal error route can only be reached by an error render, dev error reports are scoped for remote peers and kept out of production builds, and unhandled error data is no longer passed to the error page. The release notes have the details. ## ⬆️ Upgrading Our recommendation for upgrading is to run: ```sh npx nuxt upgrade --dedupe ``` This will refresh your lockfile and pull in the latest dependencies Nuxt relies on, including Nuxt CLI v4. ::note This release requires Node.js `^22.22.3 || ^24.15.0 || >=26.0.0`. :: If you have server code you'd like to make portable, read [Moving to `nuxt/server`](https://nuxt.com/docs/getting-started/upgrade#moving-to-nuxtserver) in the upgrade guide. ## 👉 Full Release Notes ::read-more{icon="i-simple-icons-github" target="_blank" to="https://github.com/nuxt/nuxt/releases/tag/v4.6.0"} Read the full release notes of Nuxt `v4.6.0`. :: Thank you to everyone who contributed to this release. 💚 # Nuxt DevTools v4 > Nuxt DevTools v4 is built on Devframe and Vite DevTools. It brings the full Vue DevTools and the wider devtools ecosystem to Nuxt. We are happy to announce [Nuxt DevTools v4](https://github.com/nuxt/devtools). Nuxt DevTools v4 is built on top of [Devframe](https://github.com/devframes/devframe) and [Vite DevTools](https://github.com/vitejs/devtools). Because of this, Nuxt can now reuse the whole devtools ecosystem. We also no longer vendor Vue DevTools. You now get the full Vue DevTools, with all of its features. ::tip Nuxt DevTools v4 ships with Nuxt v5. You can also use it with Nuxt v4.5+. See [Use it with Nuxt v4](#use-it-with-nuxt-v4). :: You do not need to change anything in your app. Open the DevTools in the same way as before. The shortcut is `Shift + Option + D` (macOS) or `Shift + Alt + D` (Windows). ## Why We Rebuilt It Since [v1.0](https://nuxt.com/blog/nuxt-devtools-v1-0), the Nuxt DevTools grew in a way that only worked for Nuxt. Each module that wanted a panel had to use our own API. Each feature from Vue DevTools had to be copied into our code. This was a lot of work for us, and it was hard to keep in sync. We wanted to fix this at the root. The problem is not only about Nuxt. Every tool in the JavaScript world needs the same things: a panel, a way to talk to the server, and a way to show data. This has always been [our vision](https://devfra.me/posts/pluggable-extensible-playful-devtools) to provide a generalized, unified devtools experience for the ecosystem to share and build upon. So we built Devframe, the framework-agnostic and agentic toolkit for devtools, and now Vite DevTools, Vue DevTools, and Nuxt DevTools all run on top of it. ## What Is Devframe Devframe is a toolkit for building devtools, agnostic of any specific framework or tool. You describe a devtool once, with its panels, typed RPC, and shared state, and the same definition runs in any host: Nuxt, Vite, Next.js, a standalone CLI, a static report, or an MCP server for coding agents. Think of it as [`unplugin`](https://unplugin.unjs.io/) for devtools. Under the hood, a devframe is exposed as a plain `Request` → `Response` handler, and a hub can compose many devframes behind one handler. You can learn more in the [Devframe repository](https://github.com/devframes/devframe) and [its announcement post](https://devfra.me/posts/pluggable-extensible-playful-devtools). ## One Ecosystem With v4, Nuxt joins the same ecosystem as Vite and other tools. This works in two directions: - Devtools that other tools build on Devframe or Vite DevTools can now work in your Nuxt app. - Devtools that you build for Nuxt can now work outside of Nuxt. ![The Universal DevTools Ecosystem: general, build tool, framework-specific, and ecosystem devtools compose into each host](https://nuxt.com/assets/blog/devtools-v4/devtools-ecosystem.png) The picture above shows how this composes for Nuxt. Most of what you see in the panel is not written for Nuxt at all. Tools like accessibility checks, terminals, and the data inspector are general, so every host gets them. Vite's own tools, like Vitest, Rolldown, and Oxlint, come with the build tool. Only the framework-specific layer, such as Vue's components and reactivity, Pinia, or Nuxt modules and auto imports, is specific to the framework. Community tools like UnoCSS or the ESLint Config Inspector plug in the same way. ::video ```yaml [props] src: https://nuxt.com/assets/blog/devtools-v4/nuxt-devtools-compose.mp4 controls: true autoplay: true loop: true muted: true class: rounded-lg ``` :: Other hosts pick their own combination. A DevTools for Next.js or Svelte could reuse the same general and build tool layers, and only add its own framework-specific part. That means a new host starts with a rich set of tools, and every tool you write reaches more users. You build a panel once, and many people can use it. ## The Full Vue DevTools and Vite DevTools Thanks to Devframe, the latest Vue DevTools and Vite DevTools are now fully built on top of it. The stack has three layers, and each layer inherits and extends the one above: - **Devframe Hub** is the foundation. It composes portable devframes behind one standard handler. - **Vite DevTools** builds on the hub and adds the tools for the Vite toolchain, such as Vite plugins, Vitest, Rolldown, and Oxc. - **Nuxt DevTools** builds on Vite DevTools and adds the Nuxt layer: Vue DevTools, Pinia, Nuxt modules, auto imports, and more. ![The Vite and Nuxt DevTools stack: Devframe Hub, Vite DevTools, and Nuxt DevTools, each layer inheriting from the previous one](https://nuxt.com/assets/blog/devtools-v4/nuxt-devtools-stack.png) This is why we no longer need to vendor Vue DevTools. Instead of copying its features into our code, Nuxt DevTools inherits them, so you always get the complete and up-to-date Vue DevTools. Improvements to Vite DevTools reach Nuxt without extra work from us. Under the hood, every devtool is a devframe that can run alone, join a hub, or mount into any host through the same standard handler. Vite DevTools is one host, and framework-specific hosts like Vue DevTools and Nuxt DevTools inherit and extend it. ![One devframe, many destinations: a single devframe definition can run standalone, join a hub, or mount into any host framework through the standard handler](https://nuxt.com/assets/blog/devtools-v4/devtools-inherit.png) ## A Transition Release Regardless of the underlying architecture having undergone an overhaul and restructuring, the user-facing experience remains consistent, as we always aim to make progressive releases and make the migration smooth. **Nuxt DevTools v4 is a transition release**. We know that many modules use the previous Nuxt DevTools API from `@nuxt/devtools-kit`. If your module uses that API, it keeps working in v4. We added a shim that maps the old API to the new system. In development, the shim logs a warning so that you know which module to update. ## For Module Authors The shim is only there to give you time. We recommend that you move your module to one of the new APIs: - **Devframe**, if you want your panel to work in any tool: Nuxt, Vite, Next.js, a standalone CLI, or an MCP server. - **Vite DevTools**, if your module only works with Vite and you want the smallest change. Nuxt DevTools v4 passes you the Vite DevTools context directly. ### Quick Migration The old `@nuxt/devtools-kit` helpers map to the new context one by one. Each deprecated helper now logs a diagnostic code, so you can find the exact call to update: | Before (`@nuxt/devtools-kit`) | After (`onDevtoolsReady`) | Warning | | ----------------------------- | ---------------------------------------------------- | -------------- | | `addCustomTab()` | `ctx.docks.register()` | `NDT_DEP_0005` | | `refreshCustomTabs()` | update the handle returned by `ctx.docks.register()` | `NDT_DEP_0006` | | `extendServerRpc()` | `ctx.scope('my-module').rpc.register()` | `NDT_DEP_0003` | | `startSubprocess()` | `ctx.terminals.startChildProcess()` | `NDT_DEP_0004` | Here is how a panel looks before and after the move to the Vite DevTools context: ```ts [Before] import { addCustomTab, extendServerRpc } from '@nuxt/devtools-kit' export default defineNuxtModule({ setup() { addCustomTab({ name: 'my-module', title: 'My Module', icon: 'carbon:apps', view: { type: 'iframe', src: '/__my-module/' }, }) extendServerRpc('my-module', { getModules: () => loadModules(), }) }, }) ``` ```ts [After] import { onDevtoolsReady } from '@nuxt/devtools-kit' export default defineNuxtModule({ setup() { onDevtoolsReady((ctx) => { ctx.docks.register({ id: 'my-module', title: 'My Module', icon: 'carbon:apps', type: 'iframe', url: '/__my-module/', }) ctx.scope('my-module').rpc.register({ name: 'get-modules', // registered as `my-module:get-modules` type: 'query', handler: () => loadModules(), }) }) }, }) ``` ::note The docks host does not cover `vnode` views and tab categories yet. If your module relies on them, keep using the shim for now. :: ::tip Read more in the [module migration guide](https://devtools.nuxt.com/module/migration-v4). :: ### Go Portable with Devframe If you want your tool to run beyond Nuxt, define it once with `defineDevframe()` and keep the Nuxt module as a thin wrapper. The RPC functions are typed, validated with any [Standard Schema](https://standardschema.dev/) library, and namespaced by your devframe id: ```ts [src/devframe.ts] import { defineDevframe, defineRpcFunction } from 'devframe' import * as v from 'valibot' const getModules = defineRpcFunction({ name: 'get-modules', type: 'query', args: [v.object({ limit: v.number() })], returns: v.array(v.object({ id: v.string(), size: v.number() })), handler: async ({ limit }) => (await loadModules()).slice(0, limit), }) export default defineDevframe({ id: 'my-tool', name: 'My Tool', icon: 'ph:gauge-duotone', clientAssets: 'client/dist', setup(ctx) { ctx.scope('my-tool').rpc.register(getModules) }, }) ``` Then you can ship the same definition through whichever entry points suit your users: ```ts import { createPluginFromDevframe } from '@vitejs/devtools-kit/node' import { createBuild } from 'devframe/adapters/build' import { createCac } from 'devframe/adapters/cac' import { createMcpServer } from 'devframe/adapters/mcp' import devframe from './devframe' export const vitePlugin = createPluginFromDevframe(devframe) // Vite DevTools, and therefore Nuxt export const runCli = () => createCac(devframe).parse() // standalone CLI export const buildReport = () => createBuild(devframe, { outDir: 'dist-static' }) // static report export const startMcp = () => createMcpServer(devframe, { transport: 'stdio' }) // for coding agents ``` If the UI of your tool is a Nuxt app, [`@devframes/nuxt/single`](https://github.com/devframes/devframe) wires it up as the browser side of a devframe and gives you a typed `$rpc` client in `useNuxtApp()`. Head to the [Devframe documentation](https://github.com/devframes/devframe) to learn more, including a step-by-step tutorial that builds a data inspector from scratch. ## Give it a try The easiest way to try Nuxt DevTools v4 is to [use Nuxt 5](https://nuxt.com/docs/5.x/getting-started/upgrade#migrating-to-nuxt-5), as it ships with DevTools v4 by default. ### Use It with Nuxt v4 If you are on Nuxt v4.5+ and want to try out Nuxt DevTools v4, you can do so by adding it as a module: ```bash npx nuxt module add @nuxt/devtools@4 ``` ## Thank You This release would not be possible without the people behind Devframe, Vite DevTools, and Vue DevTools. Thank you to all the contributors who helped to build Devframe and to make these tools work together. Please try the new Nuxt DevTools and let us know what you think. Feel free to open issues on [GitHub](https://github.com/nuxt/devtools/issues). Happy debugging! # Nuxt Icon > Add 200,000+ ready to use icons to your Nuxt application. ## Features - SSR friendly - Support 200,000 open-source vector icons via [Iconify](https://iconify.design) - Support both CSS mode / SVG mode - Custom SVG support (via Vue component, or via local SVG files) ## Installation Run the following command to add the module to your project, with the [`nuxt add`](https://nuxt.com/docs/api/commands/add) command: ```bash npx nuxt add icon ``` That's it, you can now use the `<Icon />` in your components, see [Usage](https://nuxt.com/modules/icon/usage). ✨ If you are using VS Code, you can use the [Iconify IntelliSense](https://marketplace.visualstudio.com/items?itemName=antfu.iconify) extension by [@antfu](https://github.com/antfu) <details> <summary>Manual Setup</summary> You can install the module manually with: ```bash npm i @nuxt/icon ``` Update your `nuxt.config.ts` ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ] }) ``` If you have the legacy module `nuxt-icon` installed, you might want to remove it from the `modules` list. </details> ## Vite (without Nuxt) If you are building a plain Vue app with Vite (for example when using [Nuxt UI's Vue integration](https://ui.nuxt.com/docs/getting-started/integrations/vue)), there is no Nuxt server to serve icons from, and `@iconify/vue` would fetch icons from the Iconify API at runtime. To render icons offline without network requests, `@nuxt/icon` ships a standalone Vite plugin that pre-bundles the icons you use into the client build. Install it with `@iconify/vue`, which the registration module imports, and the icon collections you use: ```bash npm i @nuxt/icon @iconify/vue npm i -D @iconify-json/uil @iconify-json/heroicons ``` Then add the plugin to your Vite config: ```ts // vite.config.ts import { defineConfig } from 'vite' import { NuxtIconBundle } from '@nuxt/icon/vite' export default defineConfig({ plugins: [ NuxtIconBundle({ // Explicitly pre-bundle known icons (hard-fails the build when missing locally) icons: ['uil:github', 'heroicons:home'], // Scanning your source files for icon usages is enabled by default. // It reads .vue, .jsx, .tsx, Markdown and YAML files: list icons only named in .js/.ts files in `icons`. scan: true, }), ], }) ``` Then register the bundled icons once in your entry file: ```ts // main.ts import 'virtual:nuxt-icon-bundle/register' ``` Add the type shims for the virtual modules to the `types` of your `tsconfig.json`, next to the ones you already list: ```json { "compilerOptions": { "types": ["vite/client", "@nuxt/icon/client"] } } ``` The plugin accepts the same options as `icon.clientBundle` (`icons`, `scan`, `sizeLimitKb`), plus `customCollections` — with two differences in defaults: `scan` is enabled by default (bundling is the whole point of the plugin), and `includeCustomCollections` is always enabled by default (there is no server provider to fall back to). For library and framework authors integrating icons themselves: `virtual:nuxt-icon-bundle` exports `init(addIcon)` so you can register the bundled data on your own copy of `@iconify/vue` (or any compatible store), and the underlying primitives (`IconUsageScanner`, `resolveBundleIcons`, `generateClientBundleCode`, ...) are exported from `@nuxt/icon/utils`. For server-side rendering, `@iconify/vue` has its own requirements: see its [documentation](https://iconify.design/docs/icon-components/vue/). See [`playgrounds/vite/`](https://github.com/nuxt/icon/tree/main/playgrounds/vite) for a working example (`pnpm play:vite` to run it locally). # Usage > Render any of the 200,000+ Iconify icons, or your own Vue components. **Props:** - `name` (required): icon name or global component name - `size`: icon size (default: `1em`) - `mode`: icon rendering mode (`svg` or `css`, default: `css`) **Attributes**: When using an icon from Iconify, a `<span>` or `<svg>` will be created based on the rendering mode, you can give [all the attributes](https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute) of the native element. ```html <Icon name="uil:github" style="color: black" /> ``` **TailwindCSS v4**: When using TailwindCSS v4 with the `css` mode, you should configure the `cssLayer` in Nuxt's app config: ```ts // ~/app.config.ts export default defineAppConfig({ icon: { mode: 'css', cssLayer: 'base' } }) ``` ## Iconify Dataset You can use any name from the <https://icones.js.org> collection: ```html <Icon name="uil:github" /> ``` It supports the `i-` prefix (for example, `i-uil-github`). It's highly recommended to install the icon data locally with ```bash npm i -D @iconify-json/collection-name ``` For example, to use the `uil:github` icon, install its collection with `@iconify-json/uil`. This way the icons can be served locally or from your serverless functions, which is faster and more reliable on both SSR and client-side. > [!NOTE] > You may also know you can install `@iconify/json` package to include all iconify icons. This is not recommended because it will increase your server bundle size and building performance. If you choose to do so, we'd recommend to explicitly specify the collection names you need: > > ```ts > export default defineNuxtConfig({ > modules: ['@nuxt/icon'], > icon: { > serverBundle: { > collections: ['uil', 'mdi'] // <!--- this > } > } > }) > ``` ## Vue Component When the `name` matches a global registered component, it will be rendered as that component (in this case `mode` will be ignored): ```html <Icon name="MyComponent" /> ``` Note that `MyComponent` needs to be inside `components/global/` folder (see [example](https://github.com/nuxt/icon/blob/main/playgrounds/nuxt/components/global/NuxtLogo.vue)). > [!TIP] > You can also change the component name with: > > ```ts > export default defineNuxtConfig({ > icon: { > componentName: 'NuxtIcon' > } > }) > ``` ## Render Function You can use the `Icon` component in a render function (useful if you create a functional component), for this you can import it from `#components`: ```ts import { Icon } from '#components' ``` See an example of a `<MyIcon>` component: ```vue <script setup> import { Icon } from '#components' const MyIcon = h(Icon, { name: 'uil:twitter' }) </script> <template> <p><MyIcon /></p> </template> ``` # Configuration > Every option of the icon key in your Nuxt config. Configure Nuxt Icon with the `icon` key in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/icon'], icon: { mode: 'css', cssLayer: 'base' } }) ``` ## Options | Option | Default | Description | | -------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `size` | `undefined` | **Icon Size** — Set the default icon size. | | `class` | `''` | **CSS Class** — Set the default CSS class. | | `mode` | `'css'` | **Default Rendering Mode** — Set the default rendering mode for the icon component | | `aliases` | `{}` | **Icon aliases** — Define Icon aliases to update them easily without code changes. | | `cssSelectorPrefix` | `'i-'` | **CSS Selector Prefix** — Set the default CSS selector prefix. | | `cssLayer` | `undefined` | **CSS Layer Name** — Set the default CSS `@layer` name. | | `cssWherePseudo` | `true` | **Use CSS `:where()` Pseudo Selector** — Use CSS `:where()` pseudo selector to reduce specificity. | | `collections` | `null` | **Icon Collections** — List of known icon collections name. Used to resolve collection name ambiguity. e.g. `simple-icons-github` -> `simple-icons:github` instead of `simple:icons-github` When not provided, will use the full Iconify collection list. | | `customCollections` | `undefined` | **Custom Icon Collections** — Local SVG directories or Iconify JSON collections to serve, see [Custom Collections](https://nuxt.com/modules/icon/custom-collections). | | `provider` | `undefined` | **Icon Provider** — Provider to use for fetching icons - `server` - Fetch icons with a server handler - `iconify` - Fetch icons with Iconify API, purely client-side - `none` - Do not fetch icons (use client bundle only) `server` by default; `iconify` when `ssr: false` Use `{ server, client }` to set a different provider for SSR and for the browser, e.g. `{ server: 'server', client: 'iconify' }`. A string is the same as setting both sides. Both `server` and `client` are required in the object form. | | `iconifyApiEndpoint` | `'https://api.iconify.design'` | **Iconify API Endpoint URL** — Define a custom Iconify API endpoint URL. Useful if you want to use a self-hosted Iconify API. Learn more: <https://iconify.design/docs/api>. | | `fallbackToApi` | `true` | **Fallback to Iconify API** — Fallback to Iconify API if server provider fails to found the collection. | | `localApiEndpoint` | `'/api/_nuxt_icon'` | **Local API Endpoint Path** — Define a custom path for the local API endpoint. | | `fetchTimeout` | `1500` | **Fetch Timeout** — Set the timeout for fetching icons. | > [!NOTE] > `customize` is the only option you can't set in `nuxt.config.ts`: it's a function, so set it in `app.config.ts` or with the component's `customize` prop. See [Icon Customization](https://nuxt.com/modules/icon/customization). # Server & Client Bundle > Choose how icons are served by your Nuxt server, and which ones ship with the client. ## Server Bundle Since `@nuxt/icon` v1.0, we have introduced the server bundle concept to serve the icons from Nuxt server endpoints. This keeps the client bundle lean and able to load icons on-demand, while having all the dynamic features to use icons that might not be known at build time. ### Server Bundle Mode: `local` This mode will bundle the icon collections you have installed locally (like `@iconify-json/*`), into your server bundle as dynamic chunks. The collection data will be loaded on-demand, only when your client request icons from that collection. ### Server Bundle Mode: `remote` Introduced in `@nuxt/icon` v1.2, you can now use the `remote` server bundle to serve the icons from a remote CDN. ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { serverBundle: 'remote', }, }) ``` Or you can specify the remote provider: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { serverBundle: { remote: 'jsdelivr', // 'unpkg' or 'github-raw', or a custom function } }, }) ``` Which will make server requests to `https://cdn.jsdelivr.net/npm/@iconify-json/ph/icons.json` to fetch the icons at runtime, instead of bundling them with your server. Under the hood, instead of bundling the collection into your server: ```ts () => import('@iconify-json/ph/icons.json') ``` it will now use something like this, where the collections are not inlined: ```ts () => fetch('https://cdn.jsdelivr.net/npm/@iconify-json/ph/icons.json').then(res => res.json()) ``` This would be useful when server bundle size is a concern, like in serverless or worker environments. ### Server Bundle Mode: `auto` This is the default option, where the module picks between `local` and `remote` based on your deployment target. It uses `remote` when you build (not in development) for an edge or worker target, that is a Nitro preset, `NITRO_PRESET` or `SERVER_PRESET` containing `edge`, `cloudflare` or `worker` (like `vercel-edge` or `cloudflare-module`), and `local` otherwise. ### Externalize Icons JSON By default, Nitro will bundle the icon collections you have installed locally (like `@iconify-json/*`), into your server bundle as dynamic chunks. When you have a large number of icons, this might make your bundling process slow and memory-intensive. You can change to externalize the icons JSON files by setting `icon.serverBundle.externalizeIconsJson` to `true`. ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { serverBundle: { externalizeIconsJson: true, } }, }) ``` Note that this will require your production Node.js server to be able to import JSON files (Note that as in Node.js v22, [JSON modules are still an experimental feature](https://nodejs.org/api/esm.html#json-modules)). In the final build, it will contain statements like `() => import('@iconify-json/ph/icons.json', { with: { type: 'json' } })`. Also note that in some serverless environments, like Cloudflare Workers, where they don't have dynamic imports, they will always be inlined regardless of this option. This option will be ignored when `icon.serverBundle.remote` is enabled. ### Completely Disable Server Bundle If you want to disable the server bundle completely, you can set `icon.serverBundle` to `false` and `provider` to `iconify` ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { provider: 'iconify', serverBundle: false, }, }) ``` This will make requests to Iconify API every time the client requests an icon. We do not recommend doing so unless the other options are not feasible. ### Separate Server and Client Providers `provider` can also be set separately for SSR and for the browser. ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { // SSR uses the server bundle through the local endpoint, the browser fetches from the Iconify API directly provider: { server: 'server', client: 'iconify' }, iconifyApiEndpoint: 'https://icons.example.com', serverBundle: { collections: ['ph', 'lucide'], }, }, }) ``` - A string `provider: X` is the same as `{ server: X, client: X }`. - The local endpoint is enabled when at least one side uses `server`, and so is the server bundle unless `serverBundle` is `false`. This is decided at build time, so overriding `provider` in `app.config.ts` does not enable or disable them. - `provider` in `app.config.ts` is not validated: set both `server` and `client` there too. A side left out is taken from the module options when they also use the object form; otherwise it is treated as unset and falls back to the `iconify` provider. - Unless both sides use `server`, `clientBundle.includeCustomCollections` defaults to `true`, since the client bundle is also used during SSR. ## Client Bundle For icons that you know you are going to use frequently, you can bundle them with your client bundle to avoid network requests. ### Prefetch (Preload) Icons If you want icons to be available on first render, use the client bundle as the prefetch mechanism: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon', ], icon: { clientBundle: { // Explicitly pre-bundle known icons icons: ['uil:github', 'heroicons:home'], // Keep disabled if you only want explicit entries scan: false, }, }, }) ``` You can also enable static scanning to pre-bundle icons referenced literally in your source files: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon', ], icon: { clientBundle: { // Automatically includes statically-detected icon usages scan: true, // Keep explicit entries for dynamic icon names icons: ['uil:github'], }, }, }) ``` If you want to disable runtime icon fetching entirely, see [Disabling Runtime Fetching](https://nuxt.com/modules/icon/custom-collections#disabling-runtime-fetching) for the `provider: 'none'` + client bundle pattern. > [!TIP] > Static scanning only detects literal icon names. See [Scan Components](#scan-components) for details and examples, and [Rendering Icons in Component Tests](https://nuxt.com/modules/icon/testing) for test-environment setup. ### Available Options ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { clientBundle: { // list of icons to include in the client bundle icons: [ 'uil:github', 'logos:vitejs' ], // scan all components in the project and include icons scan: true, // include all custom collections in the client bundle includeCustomCollections: true, // guard for uncompressed bundle size, will fail the build if exceeds sizeLimitKb: 256, }, }, }) ``` `includeCustomCollections` will include all the custom collections you have defined in `icon.customCollections` in the client bundle. It's disabled by default when both sides of `provider` use `server`, and enabled otherwise (e.g. when `ssr: false` is set). ### Scan Components When `scan` is enabled, the module will scan all the components in your project and include the icons used in the client bundle. This would significantly reduce the number of network requests needed for statically known icons, but might also increase the client bundle size depending on the number of icons used in your project. You can also fine-tune tine scanning targets like: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { clientBundle: { scan: { // note that when you specify those values, the default behavior will be overridden globInclude: ['components/**/*.vue', /* ... */], globExclude: ['node_modules', 'dist', /* ... */], }, }, }, }) ``` > [!TIP] > Scanning is relying on static analysis, which means only literal usages will be detected. Avoid constructing the icon name dynamically whenever possible. > > ```vue > <template> > <!-- Avoid this --> > <Icon :name="`carbon:${dark ? 'moon' : 'sun'}`" /> > > <!-- Prefer this --> > <Icon :name="dark ? 'carbon:moon' : 'carbon:sun'" /> > </template> > ``` # Icon Customization > Change stroke width, colors and more at render time. To update the default size (`1em`) of the `<Icon />`, create an `app.config.ts` with the `icon.size` property. Update the default class (`.icon`) of the `<Icon />` with the `icon.class` property, for a headless Icon, set `icon.class` to `''`. You can also define aliases to make swapping out icons easier by leveraging the `icon.aliases` property. > [!NOTE] > Note it's `app.config.ts` and not `nuxt.config.ts` for runtime configs. ```ts // app.config.ts export default defineAppConfig({ icon: { size: '24px', // default <Icon> size applied class: 'icon', // default <Icon> class applied mode: 'css', // default <Icon> mode applied aliases: { 'nuxt': 'logos:nuxt-icon', }, cssLayer: 'base' // set the css layer to inject to } }) ``` The icons will have the default size of `24px` and the `nuxt` icon will be available: ```html <Icon name="nuxt" /> ``` By default, this module will create a server endpoint `/api/_nuxt_icon/:collection` to serve the icons from your local server bundle (you can override the default path by setting `icon.localApiEndpoint` to your desired path). When requesting an icon that does not exist in the local bundle, it will fallback to requesting [the official Iconify API](https://api.iconify.design). You can disable the fallback by setting `icon.fallbackToApi` to `false`, or set up [your own Iconify API](https://iconify.design/docs/api/hosting.html) and update `icon.iconifyApiEndpoint` to your own API endpoint. **Customizing Icons with the customize Option** The customize option allows you to modify various aspects of the SVG icons used in your project. With this option, you can: - Change Stroke Width - Change Colors - Change Animation Duration - Change Opacity - Add Extra Shapes You have full control over SVG content with these customization options. In a Component You can define a customize function within a component to apply various modifications to your icons. ```vue <script setup lang="ts"> // Define the customize function to modify SVG content const customize = (content: string, name?: string, prefix?: string, provider?: string) => { if (prefix !== 'tabler') return content // Ignore Prefix return content .replace(/stroke-width="[^"]*"/g, `stroke-width="2"`) // Change stroke width to 2 .replace(/stroke="[^"]*"/g, `stroke="#FF5733"`) // Change stroke color to red .replace(/fill="[^"]*"/g, `fill="#FF5733"`) // Change fill color to red .replace(/animation-duration="[^"]*"/g, `animation-duration="1s"`) // Change animation duration to 1s (for animated icons) .replace(/opacity="[^"]*"/g, `opacity="0.8"`);// Change opacity to 0.8 } </script> <template> <Icon name="tabler:star" :customize="customize" /> </template> <!-- You can also use `:customize="false"` to disabled the global customization function per-usage --> ``` In the App Configuration File: Alternatively, you can apply these customizations globally in the `app.config.ts` file. ```ts // app.config.ts export default defineAppConfig({ icon: { customize: (content: string, name?: string, prefix?: string, provider?: string) => { // ... }, } }) ``` With this configuration, all icons throughout your application will have these customizations applied consistently. # Custom Collections > Serve your own SVG files as an icon collection. You can use local SVG files to create a custom Iconify collection. For example, place your icons' SVG files under a folder of your choice, for example, `./assets/my-icons`: ```bash assets/my-icons ├── foo.svg ├── bar-outline.svg ``` In your `nuxt.config.ts`, add an item in `icon.customCollections`: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { customCollections: [ { prefix: 'my-icon', dir: './assets/my-icons', // if you want to include all the icons in nested directories: // recursive: true, }, ], }, }) ``` > [!NOTE] > A relative `dir` is resolved from the directory of the project that declares it. When your project is used as an [extendable layer](https://nuxt.com/docs/4.x/getting-started/layers), the icons are loaded from the layer's own directory, not from the consuming project. Absolute paths, such as one built with `createResolver(import.meta.url)`, keep working as before. > [!NOTE] > If you are running on Nuxt 4 with the new `app` directory, the assets directory is `'./app/assets/*'` instead of `'./assets/*'`. Then you can use the icons like this: ```vue <template> <Icon name="my-icon:foo" /> <Icon name="my-icon:bar-outline" /> </template> ``` You can also pass a full custom `IconifyJSON` object: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { customCollections: [ { prefix: 'paid-icons', icons: { 'nuxt': { body: '<path d="M281.44 ... />' }, }, width: 512, height: 512, } ], }, }) ``` Note that custom local collections require you to have a server to serve the API. When setting `ssr: false`, or when generating a static app using `nuxt generate` (which is equivalent to ssr: false), the provider will default to the Iconify API (which does not have your custom icons). If you want to build a SPA with server endpoints, you can explicitly set `provider: 'server'`: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], ssr: false, icon: { provider: 'server', // <-- this customCollections: [ { prefix: 'my-icon', dir: './assets/my-icons' }, ], }, }) ``` ## Disabling Runtime Fetching Or if you want to disable the dynamic icon fetching completely and only use icons from the [client bundle](https://nuxt.com/modules/icon/bundles#client-bundle), you can set `provider: 'none'`: ```ts export default defineNuxtConfig({ icon: { provider: 'none', clientBundle: { scan: true, // ...or other bundle options }, } }) ``` ## Case Sensitive Custom Collections Before `v1.10`, due to the limitation of Iconify's previous convention, all custom icons were normalized to `kebab-case` with a warning. Thanks to the updates on Iconify side, starting from `v1.10`, you can opt in to case-sensitive custom collections and bypass the normalization. ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon' ], icon: { customCollections: [ { prefix: 'my-icon', dir: './assets/my-icons', normalizeIconName: false, // <-- this }, ], }, }) ``` Which enable to use `assets/my-icons/FooBar.svg` as `my-icon:FooBar`, for example. `normalizeIconName` is default to `true` for backward compatibility, and will be flipped in the future major version. See [#265](https://github.com/nuxt/icon/issues/265) for more context. # Testing > Render icons in component tests. In in-browser component testing environments (such as [**Vitest Browser Mode**](https://vitest.dev/guide/browser/) or [**Cypress Component Testing**](https://on.cypress.io/component-testing)), the internal Nuxt server routes used to fetch icons on demand are **not available**. As a result, icons may fail to render during tests. To ensure icons render correctly in component tests, configure **@nuxt/icon** to use the **client bundle** when running in test mode. > **Note** > Projects using [**@nuxt/ui**](https://ui.nuxt.com/) must do this to see any UI icons during component testing. ## Requirements - Install the icon collections you use locally (for example, `@iconify-json/lucide`). - With `provider: 'none'`, as below, icons are never fetched remotely: only the client bundle is used, so an icon missing from it won't render. ## Test-only Configuration Conditionally switch to the client bundle in your Nuxt config when `NODE_ENV === 'test'`: ```ts export default defineNuxtConfig({ modules: [ '@nuxt/icon', ], icon: process.env.NODE_ENV !== 'test' ? { // Production or development icon configuration } : { // Disable all network icon fetching in component tests provider: 'none', clientBundle: { // Explicitly include dynamically constructed icons icons: ['lucide:check'], // Scan your app and Nuxt UI runtime for static icon usage scan: { globInclude: [ '{app,shared}/**', 'node_modules/@nuxt/ui/dist/**', ], // Replaces the default, which excludes `node_modules` and would skip the Nuxt UI files above globExclude: [], }, }, }, }) ``` > **Tips** > > - Dynamically generated icon names may not be detected by static scanning—add them explicitly to `icons`. > - For large apps, consider moving test-only configuration into `vitest.config.ts` or `cypress.config.ts` for clarity. # Nuxt DevTools > Insights and transparency about your Nuxt app, right in the Vite DevTools panel. Nuxt DevTools shows you how your Nuxt app is put together: its pages, components, auto-imports, modules, server routes, payloads and configuration. It lives as a `Nuxt` group inside the [Vite DevTools](https://github.com/vitejs/devtools) panel, next to Vue DevTools, Inspect, Terminals and the tools your other Vite plugins provide. ![The Nuxt DevTools Overview in the Vite DevTools panel](https://nuxt.com/modules/devtools/images/overview.webp) ## Requirements Nuxt DevTools v4 needs **Vite 8**, so **Nuxt 5**, or **Nuxt 4.5 or later**. Nuxt 4.0–4.4 ship Vite 7 and cannot run it; see [Upgrading to v4](https://nuxt.com/modules/devtools/upgrading-to-v4) if you are coming from v3. ## Installation ### Nuxt 5 Nuxt 5 ships [`@nuxt/devtools-onboard`](https://github.com/nuxt/devtools/tree/main/packages/devtools-onboard), a tiny module that loads Nuxt DevTools when it is installed. The first time you run `nuxt dev` without it, a floating button offers to install `@nuxt/devtools` with your package manager and restarts the dev server. Each install waits for you to confirm it in the terminal running `nuxt dev`. You can also install it yourself: ```bash npm i -D @nuxt/devtools ``` ### Nuxt 4.5+ Nuxt 4 still bundles DevTools v3. Override the bundled version with your package manager, then reinstall: ::code-group ```json [npm] { "overrides": { "@nuxt/devtools": "^4.0.0" } } ``` ```json [yarn] { "resolutions": { "@nuxt/devtools": "^4.0.0" } } ``` ```yaml [pnpm] # pnpm-workspace.yaml overrides: '@nuxt/devtools': ^4.0.0 ``` :: Remove your lockfile (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) and install again so the override takes effect. ### Turning it off DevTools is enabled by default in development, and nothing is injected in production. To turn it off, set `enabled` to `false`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ devtools: { enabled: false }, }) ``` ## Opening the DevTools Run `nuxt dev` and open your app in the browser. Click the **Nuxt** button in the Vite DevTools dock at the bottom of the page, or press :kbd{value="Shift"} + :kbd{value="Alt"} / :kbd{value="⇧ Shift"} + :kbd{value="⌥ Option"} + :kbd{value="D"}, to toggle the panel. Vite DevTools can run commands and read files on your machine, so the first time you open it, it asks you to **authorize the browser** with the code printed in the terminal running `nuxt dev`. The prompt is skipped in sandboxes such as StackBlitz and CodeSandbox. To skip it elsewhere, set `devtools.disableAuthorization: true`; only do this on a machine you trust, as any browser that reaches the dev server then gets access. ## Module Options Configure Nuxt DevTools with the `devtools` key of your `nuxt.config`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ devtools: { // Enable DevTools (default: true) enabled: true, // Coder code-server integration (default: enabled, launched on demand) codeServer: {}, // Record composable calls and route changes in the Timeline tab (default: false) timeline: { enabled: true }, }, }) ``` | Option | Default | Description | | ------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- | | `enabled` | `true` | Enable Nuxt DevTools. | | `componentInspector` | `true` | Inspect the DOM to find the component rendering it. | | `vueDevTools` | `true` | Register the Vue DevTools dock next to the `Nuxt` group. | | `viteInspect` | `true` | Register the [Inspect](https://nuxt.com/modules/devtools/features#inspect) dock, or its install launcher. | | `dataInspector` | `true` | Register the [Data Inspector](https://nuxt.com/modules/devtools/features#data-inspector) panel. | | `codeServer` | | [Code Server](https://nuxt.com/modules/devtools/features#code-server) options; `codeServer.enabled: false` removes it. | | `timeline` | | [Timeline](https://nuxt.com/modules/devtools/features#timeline) options; `timeline.enabled` defaults to `false`. | | `assets.uploadExtensions` | | File extensions the [Assets](https://nuxt.com/modules/devtools/features#assets) panel accepts for upload, or `'*'` for any. | | `disableAuthorization` | | Skip the browser authorization prompt. On by default in sandboxes. | | `mcp` | `'auto'` | Expose the Vite DevTools [MCP](https://modelcontextprotocol.io) endpoint for AI agents; `false` turns it off. | | `telemetry` | | Anonymous telemetry. Follows your Nuxt telemetry setting; set to `false` to opt out. | For every option, see the TSDocs in your IDE or the [type definition file](https://github.com/nuxt/devtools/blob/main/packages/devtools-kit/src/_types/options.ts). ## Nightly Release Channel Similar to [Nuxt's Nightly Channel](https://nuxt.com/docs/guide/going-further/nightly-release-channel), DevTools also offers a nightly release channel, that automatically releases for every commit to the `main` branch. On Nuxt 5, install it next to your app's dependencies; it takes precedence over `@nuxt/devtools`: ```bash npm i -D @nuxt/devtools-nightly ``` If your `nuxt.config` lists `@nuxt/devtools` in `modules`, replace it with `@nuxt/devtools-nightly`. On Nuxt 4, alias it with your package manager's overrides: ::code-group ```json [npm] { "overrides": { "@nuxt/devtools": "npm:@nuxt/devtools-nightly@latest" } } ``` ```json [yarn] { "resolutions": { "@nuxt/devtools": "npm:@nuxt/devtools-nightly@latest" } } ``` ```yaml [pnpm] # pnpm-workspace.yaml overrides: '@nuxt/devtools': 'npm:@nuxt/devtools-nightly@latest' ``` :: Remove your lockfile (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) and reinstall dependencies. # Features > Discover everything Nuxt DevTools shows you about your Nuxt app. Nuxt DevTools registers a `Nuxt` group in the [Vite DevTools](https://github.com/vitejs/devtools) panel. Its tabs are sorted into sections: **App**, **Analyze**, **Server**, **Modules** and **Advanced**. Some tabs only appear when they have something to show, for example Server Tasks when your project defines tasks. Next to the `Nuxt` group, the panel also holds docks shared with every Vite tool: [Vue DevTools](#vue-devtools), [Inspect](#inspect), [Terminals](#terminals) and [Messages](#messages). ## App ### Overview Shows a quick overview of your app: the Nuxt and Vue versions (with a hint when an update is available), how many pages, components, imports, modules and plugins you use, and how long your app took to load. ![Overview tab](https://nuxt.com/modules/devtools/images/overview.webp) ### Pages Shows your current routes with their layout and middleware, and provides a quick way to navigate between pages. Edit the current path to see how each route is matched. ![Pages tab](https://nuxt.com/modules/devtools/images/pages.webp) ### Components Shows all the components in your app and where they come from. Search for them and jump to their source code. ![Components tab](https://nuxt.com/modules/devtools/images/components.webp) The Graph view shows the relationships between components and the dependencies of each one. Its dependency edges need the optional [`vite-plugin-inspect`](#inspect) installed. You can also inspect your app's DOM tree to find which component renders it. Turn the inspector off with `devtools.componentInspector: false`. ### Imports Shows all the auto-imports registered to Nuxt, which files use them, and where they come from. Some entries provide short descriptions and documentation links. ![Imports tab](https://nuxt.com/modules/devtools/images/imports.webp) ### Modules Shows the modules you have installed with their repository, documentation, version and setup time. You can install or remove any Nuxt module from the [module registry](https://nuxt.com/modules) with one click. ![Modules tab](https://nuxt.com/modules/devtools/images/modules.webp) ### Error Appears while the current page holds an error, and shows it with its stack trace. ## Analyze ### Payload Shows the state and data payloads of your page: keyed state from `useState()` and the data fetched with `useAsyncData()` and `useFetch()`, which you can re-fetch. ![Payload tab](https://nuxt.com/modules/devtools/images/payload.webp) ### Runtime Configs Shows your App Config and runtime config. App Config and public runtime config can be edited live; private runtime config is read-only, as it is never exposed to the client. ![Runtime Configs tab](https://nuxt.com/modules/devtools/images/runtime-configs.webp) ### Plugins Shows the plugins of your app, with their mode (client or server) and initialization time. ### Timeline Records when auto-imported composables run and when the route changes, to help you understand the order in which things happen. It is opt-in: until it is enabled, the tab offers to enable it for you, which updates your `nuxt.config` and restarts Nuxt. ![Timeline tab](https://nuxt.com/modules/devtools/images/timeline.webp) ```ts [nuxt.config.ts] export default defineNuxtConfig({ devtools: { timeline: { enabled: true, }, }, }) ``` By default it tracks the composables from `#app`, `@unhead/vue` and your `composables/` directory. Use `timeline.functions.include`, `includeFrom` and `exclude` to change what is tracked. ### Build Analyze Runs an analyze build and shows the bundle size of your project. Reports are saved so you can compare the bundle size of different builds. The build output streams to the [Terminals](#terminals) dock. ### Assets The Assets panel, powered by [`@devframes/plugin-assets`](https://npmx.dev/package/@devframes/plugin-assets), lists the files of your `public/` directory. Preview, upload, rename and delete them from the panel. Limit which files can be uploaded with `devtools.assets.uploadExtensions`, or set it to `'*'` to accept any. ### Open Graph The Open Graph panel, powered by [`@devframes/plugin-og`](https://npmx.dev/package/@devframes/plugin-og), fetches a page from your dev server, shows its Open Graph and Twitter metadata, and previews its social card on X (Twitter), Facebook, LinkedIn and Telegram. ## Server ### Server Routes Shows all the [Nitro](https://nitro.build) routes of your project, with a playground to send requests to your endpoints and inspect their responses. ![Server Routes tab](https://nuxt.com/modules/devtools/images/server-routes.webp) ### Server Tasks Appears when Nitro's `experimental.tasks` is enabled and your project defines [tasks](https://nitro.build/guide/tasks). Pass task payloads, run them, and see their results. ![Server Tasks tab](https://nuxt.com/modules/devtools/images/server-tasks.webp) ### Storage Shows the [Nitro storage](https://nitro.build/guide/storage) mounts of your project. You can create, edit, and delete their items. ![Storage tab](https://nuxt.com/modules/devtools/images/storage.webp) ## Modules Tabs contributed by the Nuxt modules you install (for example `@nuxt/fonts`, `@nuxt/scripts` or `nuxt-og-image`) appear in this section. See the [Module Authors Guide](https://nuxt.com/modules/devtools/module-authors) to add your own. ### Code Server Embeds [Coder's `code-server`](https://coder.com/docs/code-server/install). It is enabled by default: Nuxt DevTools detects the local `code-server` binary during setup, but starts no process until you choose **Launch editor**. If the binary is missing, the tab shows installation instructions instead. When launched, Code Server opens the Nuxt root directory by default, hands the generated authenticated session to the iframe, and streams process output to the [Terminals](#terminals) dock. The process is also stopped when Nuxt closes. ```ts [nuxt.config.ts] export default defineNuxtConfig({ devtools: { codeServer: { enabled: true, // All fields below are optional. bin: '/usr/local/bin/code-server', cwd: '/workspace/my-app', serverPort: 8080, host: '127.0.0.1', args: ['--disable-file-downloads'], env: { LOG_LEVEL: 'info' }, cookieSuffix: 'my-app', startTimeout: 30_000, }, }, }) ``` The options are `enabled`, `bin`, `cwd`, `serverPort`, `host`, `args`, `env`, `cookieSuffix`, and `startTimeout`. When omitted, the upstream plugin defaults apply: the `code-server` binary, loopback host, a free port near Code Server's port 8080, and a 30-second startup timeout. Nuxt changes only the default `cwd`, setting it to the Nuxt root. Set `codeServer.enabled: false` to remove the tab. Nuxt DevTools owns authentication, so `args` cannot override `--auth`, `--bind-addr`, or `--cookie-suffix`, and `env` cannot set `PASSWORD` or `HASHED_PASSWORD`. Configure the bind host and cookie suffix through their dedicated fields. The editor iframe connects directly to the Code Server port; Nuxt does not proxy it through `app.baseURL`. The browser must be able to reach that host and port, and an HTTPS page cannot embed an HTTP-only editor because of browser mixed-content rules. Binding to a non-loopback interface also exposes the port to that network, so apply the usual network controls. This integration uses the experimental upstream `@devframes/plugin-code-server`. HTTPS, reverse proxies, containers, and remote port forwarding are governed by that plugin and your environment; Nuxt does not extend them. ## Advanced ### Hooks Shows the time spent in each Nuxt hook on the client and the server, to help you find performance bottlenecks. ![Hooks tab](https://nuxt.com/modules/devtools/images/hooks.webp) ### Virtual Files Shows the virtual files Nuxt generates to support its conventions. ### Debug With `debug: true` in your `nuxt.config`, lists which module changed which Nuxt option during setup. ### Data Inspector An interactive [jora](https://discoveryjs.github.io/jora/) query workbench over your live server-side configuration. It replaces the read-only Nuxt Options Viewer of v3. Turn it off with `devtools.dataInspector: false`. Its `Nuxt Application` source exposes three root fields: - `nuxt` — the resolved Nuxt options. - `nitro` — the resolved Nitro options, once Nitro is created. - `vite` — the live Vite dev server: its resolved config at `vite.config`, and its `environments` (`client` and `ssr`). The source is live: every query reads the current state, so re-running a query after the Nitro and Vite hooks have completed reflects the latest values. Six presets are provided: **Overview**, **Nuxt modules**, **Vite plugins**, **Vite config**, **Vite environments** and **Nitro routes**, each leaving functions out so browsing configuration does not foreground methods. Module authors who want to expose their own data can install [`@devframes/plugin-data-inspector`](https://npmx.dev/package/@devframes/plugin-data-inspector) and register sources through its `registerDataSource` API; they appear in the same inspector without any Nuxt-specific wrapper. ::warning The `getServerData` DevTools RPC that backed the old viewer is deprecated (`NDT_DEP_0009`). It still works as a compatibility shim but will be removed in v5; use the Data Inspector instead. :: ### Settings Change the UI scale, toggle the help buttons, choose the editor used by *Open in editor*, opt out of anonymous telemetry, or reset the local DevTools state. Hiding, pinning and reordering tabs is handled by Vite DevTools. ## Vite DevTools Docks ### Vue DevTools Nuxt installs [Vue DevTools](https://devtools.vuejs.org) (`vite-plugin-vue-devtools`), which registers its own dock next to the `Nuxt` group to inspect your components' state, Pinia stores and more. Turn it off with `devtools.vueDevTools: false`. ### Inspect Exposes the [`vite-plugin-inspect`](https://github.com/antfu/vite-plugin-inspect) integration as its own dock, to inspect the transformation steps of Vite. `vite-plugin-inspect` is an optional peer dependency, so it is not installed by default. Until you add it to your project, the Inspect dock shows an install launcher: click it to install the plugin and restart the dev server. Set `devtools.viteInspect: false` to remove the dock and its launcher. ### Terminals Processes started by DevTools and modules, such as module installs, Build Analyze and Code Server, appear in the shared Terminals dock of Vite DevTools. ### Messages Notifications from DevTools and modules land in the Messages dock of Vite DevTools as a persistent list, optionally also shown as a toast. # Composables > Open or control Nuxt DevTools from your app with the useNuxtDevTools composable. To open or control Nuxt DevTools from your application in development, use the auto-imported `useNuxtDevTools()` composable. ```vue <script setup> // A ref, whose value is undefined in production, during SSR, or until DevTools connects const devtoolsClient = useNuxtDevTools() // Ref<NuxtDevtoolsHostClient | undefined> </script> <template> <button v-if="devtoolsClient" @click="devtoolsClient.devtools.navigate('/modules/components')" > <!-- Open the DevTools and navigate to the Components tab --> Open DevTools </button> </template> ``` In `<script>`, reach the client through `.value`: ```ts const devtoolsClient = useNuxtDevTools() function openDevTools() { devtoolsClient.value?.devtools.open() } ``` When you have auto-import disabled, import it explicitly: ```ts import { useNuxtDevTools } from '#imports' ``` The client exposes: - `devtools.open()`, `close()`, `toggle()` and `reload()` to drive the Vite DevTools panel, and `navigate(path)` to open a tab, for example `/modules/pages`. - `app.navigate(path)` and `app.reload()` to drive your app. - `inspector.enable()`, `disable()` and `toggle()` for the component inspector. - `metrics`, the client hooks, plugins and loading times DevTools collects. See [`NuxtDevtoolsHostClient`](https://github.com/nuxt/devtools/blob/main/packages/devtools-kit/src/_types/client-api.ts) for the full type. # Upgrading to v4 > What changes for your app when you move from Nuxt DevTools v3 to v4. Nuxt DevTools v4 is built on [Vite DevTools](https://github.com/vitejs/devtools). Instead of shipping its own floating panel, it registers a `Nuxt` group inside the Vite DevTools panel, next to Vue DevTools, Vite Inspect, Terminals, Messages and the other tools your Vite plugins provide. Everything you know from the Nuxt side (Pages, Components, Imports, Modules, Server Routes, Timeline, …) lives in that group. This page is for app developers. If you maintain a Nuxt module that integrates with DevTools, read the [module migration guide](https://nuxt.com/modules/devtools/module-migration) as well. ## Requirements - **Vite 8.1.5 or later.** Vite DevTools only works with Vite 8. - That means **Nuxt 4.5 or later**, or **Nuxt 5**. Nuxt 4.0–4.4 ship Vite 7 and cannot run DevTools v4. If you are on an older Nuxt 4, upgrade Nuxt first. Overriding `vite` on Nuxt 4.4 to Vite 8 is not supported. ## Installing ### Nuxt 5 Nuxt 5 ships [`@nuxt/devtools-onboard`](https://github.com/nuxt/devtools/tree/main/packages/devtools-onboard) instead of a DevTools version. If `@nuxt/devtools` is not installed yet, `nuxt dev` shows a floating button that installs it for you, once you confirm in the terminal. To install it yourself: ```bash npm i -D @nuxt/devtools ``` ### Nuxt 4.5+ Nuxt 4 still bundles DevTools v3. Override the bundled version with your package manager, then reinstall: ::code-group ```json [npm] { "overrides": { "@nuxt/devtools": "^4.0.0" } } ``` ```json [yarn] { "resolutions": { "@nuxt/devtools": "^4.0.0" } } ``` ```yaml [pnpm] # pnpm-workspace.yaml overrides: '@nuxt/devtools': ^4.0.0 ``` :: Remove your lockfile (`package-lock.json`, `yarn.lock`, or `pnpm-lock.yaml`) and run the install again so the override takes effect. ## Opening DevTools The floating Nuxt button at the bottom of the page is gone. Open DevTools with the **Nuxt** button in the Vite DevTools dock instead. The keyboard shortcut is unchanged: :kbd{value="Shift"} + :kbd{value="Alt"} / :kbd{value="⇧ Shift"} + :kbd{value="⌥ Option"} + :kbd{value="D"} toggles the Nuxt entry. Vite DevTools can run shell commands and read files on your machine, so it asks you to **authorize the browser once per project**. On first open you will see an *Unauthorized* badge; enter the 6-digit code printed in your terminal (or click the link printed next to it). In sandboxed environments such as StackBlitz and CodeSandbox the prompt is skipped automatically. To skip it elsewhere, set `devtools.disableAuthorization: true` in `nuxt.config.ts` — only do this on a machine you trust. To turn Vite DevTools off entirely (Nuxt DevTools included), disable it in your Vite config on Vite 8.3+: ```ts [nuxt.config.ts] export default defineNuxtConfig({ vite: { devtools: false, }, }) ``` ## What moved | v3 | v4 | | ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | | Floating Nuxt panel | `Nuxt` group in the Vite DevTools panel | | Nuxt Options Viewer | **Data Inspector** → `Nuxt Application` source, with a live [jora](https://discoveryjs.github.io/jora/) query workbench | | Terminals tab | Vite DevTools **Terminals** dock (shared with every tool) | | Built-in VS Code integration (`devtools.vscode`) | **Code Server** dock (`devtools.codeServer`), see below | | Vue DevTools tab | Vue DevTools dock, next to the `Nuxt` group (`vite-plugin-vue-devtools` v9) | | Popup (Picture-in-Picture) and Split Screen | Removed; Vite DevTools owns the panel layout | | Assets tab | **Assets** panel, powered by `@devframes/plugin-assets` | | Open Graph tab | **Open Graph** panel, powered by `@devframes/plugin-og` | | Inspect tab | **Inspect** dock; `vite-plugin-inspect` is an optional peer dependency, installed from the dock's launcher | | Notifications | Vite DevTools **Messages** dock (shared with every tool) | | Hiding, pinning and ordering tabs in Settings | Vite DevTools dock settings | | `nuxi devtools enable/disable` | Removed; set `devtools.enabled` in `nuxt.config.ts` | Module tabs contributed by Nuxt modules you install (for example `@nuxt/fonts`, `@nuxt/scripts`, `nuxt-og-image`) keep working. They appear in the `Nuxt` group under the *Modules* category. ## Configuration changes ### `vscode` is replaced by `codeServer` {#code-server} The VS Code integration is now the Code Server plugin. It supports a locally installed Coder [`code-server`](https://coder.com/docs/code-server/install), detected when DevTools starts and launched on demand. ```diff [nuxt.config.ts] export default defineNuxtConfig({ devtools: { - vscode: { enabled: true }, + codeServer: { enabled: true }, }, }) ``` The old `vscode` value is ignored and a `NDT_DEP_0008` warning is printed. The following v3 modes no longer exist: - Microsoft `code serve-web` and `code-server serve-local` - VS Code tunnels - reusing an already running server, or starting one on boot - the `vscode-server-controller` extension The generic *open in editor* action (from component and file links) is unaffected. `codeServer` accepts `enabled`, `bin`, `cwd`, `serverPort`, `host`, `args`, `env`, `cookieSuffix` and `startTimeout`. ### Removed options - `viteDevTools` — Vite DevTools is always on; use `vite.devtools: false` to turn it off. - `ui.showPanel` and `ui.minimizePanelInactive` — the floating panel no longer exists. - `devtoolsGlobal` — DevTools cannot be installed globally any more. ### Deprecated options - `experimental.timeline` — still honored, but use `timeline.enabled` instead. ### New options - `vueDevTools` (default `true`) — set to `false` to skip the Vue DevTools dock. - `dataInspector` (default `true`) — set to `false` to skip the Data Inspector panel and its `Nuxt Application` source. - `disableAuthorization` (default: on in sandboxes) — skips the authorization prompt described above. - `mcp` (default `'auto'`) — set to `false` to turn off the MCP endpoint that lets AI agents use DevTools. ## Deprecation warnings in your terminal Lines such as `[NDT_DEP_0003] extendServerRpc is deprecated` come from a Nuxt module you installed, not from your app. The module keeps working; the warning tells its author which v4 API to move to. Each code links to the relevant section of the [module migration guide](https://nuxt.com/modules/devtools/module-migration), which is the right place to point the maintainer to. ## Troubleshooting - **The dev server crashes at startup mentioning `@vitejs/devtools-rolldown`.** You are on Vite 7 (Nuxt ≤ 4.4). Upgrade to Nuxt 4.5 or later. - **Opening `/__nuxt_devtools__/client/` directly shows empty data.** The client needs the connection the Vite DevTools panel gives it; open DevTools from the app page instead. - **The authorization code does not appear.** It is printed by the dev server process; look in the terminal running `nuxt dev`, not in the browser console. # Module Authors Guide > Add your own modules integration to the Nuxt DevTools. Nuxt DevTools is designed to be extensible. You can add your own modules integration to the DevTools. Nuxt DevTools v4 is built on [Vite DevTools](https://github.com/vitejs/devtools): your module registers **dock entries** (tabs), **RPC functions** and **terminals** on the same hosts every other Vite tool uses, from one hook, [`onDevtoolsReady()`](https://nuxt.com/modules/devtools/utils-kit#ondevtoolsready). If your module still uses the v3 API (`addCustomTab`, `extendServerRpc`, …), see the [migration guide](https://nuxt.com/modules/devtools/module-migration). ## Starter Template The [`module-starter` playground](https://github.com/nuxt/devtools/tree/main/playgrounds/module-starter) in the DevTools repo is a module with a fully custom view contributed as a tab, on the v4 API. Copy it to start your integration. ::note The `module-devtools` template of `nuxi init` still targets the v3 API (`devtools:customTabs`). It keeps working through the compatibility shims, but prints deprecation warnings. :: ## Contributing to View Most integrations contribute an `iframe` view: serve your module's view yourself, then register it as a dock entry in the `Nuxt` group. Vite DevTools also supports `launcher`, `action` and `json-render` entries. ```ts import { NUXT_DEVTOOLS_GROUP_ID, onDevtoolsReady } from '@nuxt/devtools-kit' onDevtoolsReady((ctx) => { ctx.docks.register({ // unique identifier id: 'my-module', // title to display in the dock title: 'My Module', // any icon from Iconify, or a URL to an image icon: 'carbon:apps', // iframe view type: 'iframe', url: '/url-to-your-module-view', // show it inside the `Nuxt` group, next to the built-in tabs groupId: NUXT_DEVTOOLS_GROUP_ID, }) }) ``` Set `category` (`app`, `analyze`, `server`, `modules`, `documentation`, `advanced`) to choose the section of the `Nuxt` group the entry appears in, or omit `groupId` to register a top-level dock next to Vue DevTools and Vite Inspect. `register()` returns a handle to patch the entry later, for example to show a badge: ```ts onDevtoolsReady((ctx) => { const entry = ctx.docks.register({ id: 'my-module', /* ... */ }) entry.update({ badge: '3' }) }) ``` Learn more about [DevTools Utility Kit](https://nuxt.com/modules/devtools/utils-kit). ## Lazy Service Launching If the view you are contributing is heavy to load, register a `launcher` entry first and swap it for the iframe once the user starts it. Re-registering the same `id` with `true` as the second argument replaces the entry: ```ts let promise: Promise<void> | undefined onDevtoolsReady((ctx) => { ctx.docks.register({ id: 'my-module', title: 'My Module', icon: 'carbon:apps', type: 'launcher', groupId: NUXT_DEVTOOLS_GROUP_ID, launcher: { title: 'Start My Module', description: 'Starts the inspector server on demand.', async onLaunch() { promise ||= launchService() await promise ctx.docks.register({ id: 'my-module', title: 'My Module', icon: 'carbon:apps', type: 'iframe', url: '/url-to-your-module-view', groupId: NUXT_DEVTOOLS_GROUP_ID, }, true) }, }, }) }) ``` It will first display a launch page with a button to start the service. When the user clicks the button, `onLaunch()` is called, and the entry becomes the iframe. ## Custom RPC Functions Nuxt DevTools uses Remote Procedure Call (RPC) to communicate between the server and client. For modules you can also leverage that to communicate with your server code. Scope the RPC host to your module so every function and event name is prefixed with your namespace (`my-module:` below) without repeating it: ```ts import { onDevtoolsReady } from '@nuxt/devtools-kit' import { defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup(options, nuxt) { onDevtoolsReady((ctx) => { const { rpc } = ctx.scope('my-module') // register server RPC functions rpc.register({ name: 'get-options', type: 'query', handler() { return options }, }) // call client RPC functions when something changes; `broadcast` reaches // every connected client (none are connected yet when this hook runs) nuxt.hook('builder:watch', (_event, path) => { rpc.broadcast({ method: 'show-notification', args: [`${path} changed`], event: true }) }) }) }, }) ``` `type` is `'query'` for reads, `'action'` for mutations, `'event'` for fire-and-forget and `'static'` for values that never change. And on the embedded iframe client side, connect to Vite DevTools and use the same scope: ```ts import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client' const kit = await getDevToolsRpcClient() const { rpc } = kit.scope('my-module') // register client RPC functions rpc.register({ name: 'show-notification', type: 'event', handler(message: string) { console.log(message) }, }) // call server RPC functions const options = await rpc.call('get-options') ``` See the [iframe client](https://nuxt.com/modules/devtools/utils-kit#nuxtdevtools-kitiframe-client) section for how to reach the user's app from your iframe. ## Trying Local Changes You can clone Nuxt DevTools repo and try your changes locally. Please refer to [Trying Local Changes](https://nuxt.com/modules/devtools/contributing#trying-local-changes). ## Examples Here are a few examples of how to integrate Nuxt DevTools in modules: - [Nuxt DevTools `module-starter` playground](https://github.com/nuxt/devtools/tree/main/playgrounds/module-starter), on the v4 API These ones use the v3 API, which keeps working through the compatibility shims: - [VueUse adds a docs tab](https://github.com/vueuse/vueuse/blob/ce28cef154489c73abe308104bef8568594a9bcd/packages/nuxt/index.ts#L89-L99) - [UnoCSS Inspector](https://github.com/unocss/unocss/blob/25021a751494e99e85cfd82cca3855cdf78f6a12/packages/nuxt/src/index.ts#L81-L94) - [Nuxt Test Utils](https://github.com/nuxt/test-utils/blob/fd024f45f752c2faa4c9e0cc1fc1f8b3eae59553/src/module.ts#L150-L192) - [Nuxt OG Image Playground](https://github.com/nuxt-modules/og-image/blob/551b5474b44b8ff3190643e861c0b453813683b0/src/build/devtools.ts#L65-L80) - [Nuxt Mongoose](https://github.com/arashsheyda/nuxt-mongoose/blob/89d50bd977de0f4edfdbe3ed150d0dca4d040709/src/devtools.ts#L38-L48) # Utility Kit > Utility kit for easier DevTools integrations. `@nuxt/devtools-kit` is the utility kit for DevTools integrations, similar to `@nuxt/kit`. ```bash npm i @nuxt/devtools-kit ``` ```ts import { NUXT_DEVTOOLS_GROUP_ID, onDevtoolsReady } from '@nuxt/devtools-kit' ``` We recommend module authors to install `@nuxt/devtools-kit` as a dependency and `@nuxt/devtools` as a dev dependency. ## `@nuxt/devtools-kit` ### Joining the `Nuxt` Dock Group Nuxt DevTools registers a public `Nuxt` dock group (`NUXT_DEVTOOLS_GROUP_ID`, id `nuxt`) on the framework category, with `nuxt:devtools` as its default member. Module authors join the group natively by pointing their own dock entries at it — no special Nuxt API required: ```ts import { NUXT_DEVTOOLS_GROUP_ID, onDevtoolsReady } from '@nuxt/devtools-kit' onDevtoolsReady((ctx) => { ctx.docks.register({ id: 'my-module', type: 'iframe', title: 'My Module', icon: 'i-ph-puzzle-piece', url: '/my-module/', groupId: NUXT_DEVTOOLS_GROUP_ID, }) }) ``` ### `addCustomTab()` ::warning **Deprecated.** `addCustomTab()` is soft-deprecated (`NDT_DEP_0005`) in favour of registering a dock entry on the Vite DevTools docks host (`onDevtoolsReady((ctx) => ctx.docks.register(...))`). It still works as a shim. Note the docks host does not cover `vnode` views. See the [migration guide](https://nuxt.com/modules/devtools/module-migration#ndt_dep_0005). :: A shorthand for calling the hook `devtools:customTabs`. ```ts import { addCustomTab } from '@nuxt/devtools-kit' addCustomTab(() => ({ // unique identifier name: 'my-module', // title to display in the tab title: 'My Module', // any icon from Iconify, or a URL to an image icon: 'carbon:apps', // iframe view view: { type: 'iframe', src: '/url-to-your-module-view', }, })) ``` The iframe view supports the following options: - `src`: URL of the iframe - `persistent`: Whether to persist the iframe instance when switching tabs (default: `true`) - `permissions`: Additional permissions to allow in the iframe (merged with default `clipboard-write` and `clipboard-read`) ```ts const view: ModuleIframeView = { type: 'iframe', src: '/url-to-your-module-view', persistent: true, permissions: ['camera', 'microphone'], } ``` ### `refreshCustomTabs()` ::warning **Deprecated.** `refreshCustomTabs()` is soft-deprecated (`NDT_DEP_0006`). Update dock entries directly via the handle returned by `ctx.docks.register(...)`. It still works as a shim. See the [migration guide](https://nuxt.com/modules/devtools/module-migration#ndt_dep_0006). :: A shorthand for call hook `devtools:customTabs:refresh`. It will refresh all custom tabs. ### Spawning terminals **Use Vite DevTools' own terminals host** (`ctx.terminals`), reached through the connected context from [`onDevtoolsReady()`](#ondevtoolsready). It spawns and owns the process and surfaces it in the built-in **Terminals** dock — no Nuxt-specific wrapper needed: ```ts import { onDevtoolsReady } from '@nuxt/devtools-kit' onDevtoolsReady((ctx) => { // Output-only child process ctx.terminals.startChildProcess( { command: 'npm', args: ['run', 'dev'] }, { id: 'my-module:dev', title: 'Dev Server', icon: 'logos-npm-icon' }, ) // Fully interactive PTY (powered by `zigpty`, with a graceful pipe fallback) ctx.terminals.startPtySession( { command: 'bash' }, { id: 'my-module:shell', title: 'Shell', interactive: true }, ) }) ``` If your module needs to keep owning the process itself and merely stream output, register a read-only session with `ctx.terminals.register({ id, title, status, stream })`. See the [Vite DevTools Kit](https://github.com/vitejs/devtools) docs for the full `ctx.terminals` API. ### `startSubprocess()` ::warning **Deprecated — do not use in new code.** `startSubprocess()` is soft-deprecated (`NDT_DEP_0004`) in favour of the Vite DevTools terminals host, used from the [`onDevtoolsReady`](#ondevtoolsready) hook (`onDevtoolsReady((ctx) => ctx.terminals.startChildProcess(...))` — see [Spawning terminals](#spawning-terminals) above). It still works as a shim (bridged onto `ctx.terminals`), so existing modules keep working, but it emits the `NDT_DEP_0004` deprecation diagnostic and will be removed in a future major. See the [migration guide](https://nuxt.com/modules/devtools/module-migration#ndt_dep_0004). :: Legacy usage (module owns the process; output is surfaced as a read-only session in the built-in **Terminals** dock): ```ts import { startSubprocess } from '@nuxt/devtools-kit' const subprocess = startSubprocess( { command: 'vite', args: ['build', '--watch'] }, { id: 'my-module:build', name: 'Build', icon: 'ph:terminal-duotone' }, ) subprocess.restart() subprocess.terminate() ``` ### `extendServerRpc()` ::warning **Deprecated.** `extendServerRpc()` is soft-deprecated (`NDT_DEP_0003`) in favour of the Vite DevTools RPC registration. It still works as a shim. See the [migration guide](https://nuxt.com/modules/devtools/module-migration#ndt_dep_0003). :: Extend the server RPC with your own methods. ```ts import { extendServerRpc } from '@nuxt/devtools-kit' const rpc = extendServerRpc('my-module', { async myMethod() { return 'hello' }, }) ``` The forward path registers functions on the Vite DevTools RPC host from the [`onDevtoolsReady()`](#ondevtoolsready) hook, scoped to your module's namespace: ```ts import { onDevtoolsReady } from '@nuxt/devtools-kit' export default defineNuxtModule({ setup(_options, nuxt) { onDevtoolsReady((ctx) => { ctx.scope('my-module').rpc.register({ name: 'my-method', // registered as `my-module:my-method` type: 'query', handler: async () => 'hello', }) }) }, }) ``` Learn more about [Custom RPC functions](https://nuxt.com/modules/devtools/module-authors#custom-rpc-functions). ### `onDevtoolsReady()` **The recommended entry point for DevTools integration.** The callback runs once the Vite DevTools kit has connected and receives the connected `ViteDevToolsNodeContext`, so the full devframe surface is guaranteed to be available — no connect-safe buffering needed. Do all your DevTools integration here: ```ts import { NUXT_DEVTOOLS_GROUP_ID, onDevtoolsReady } from '@nuxt/devtools-kit' export default defineNuxtModule({ setup(_options, nuxt) { onDevtoolsReady((ctx) => { // `ctx` is the connected ViteDevToolsNodeContext ctx.docks.register({ id: 'my-module', title: 'My Module', icon: 'carbon:apps', type: 'iframe', url: '/__my-module__/', groupId: NUXT_DEVTOOLS_GROUP_ID, }) ctx.scope('my-module').rpc.register({ name: 'my-method', // registered as `my-module:my-method` type: 'query', handler: async () => 'hello', }) ctx.messages.add({ message: 'My module is ready', level: 'info' }) }) }, }) ``` It is backed by the `devtools:ready` Nuxt hook: ```ts nuxt.hook('devtools:ready', (ctx) => { // ... }) ``` Inside `onDevtoolsReady()`, the `ctx` is the connected `ViteDevToolsNodeContext`, which exposes the full devframe-native surface: - `ctx.docks` — register dock entries (iframe / action / json-render / launcher / custom-render / group) - `ctx.terminals` — spawn and manage child processes / PTY sessions - `ctx.messages` — structured messages and toast notifications - `ctx.commands` — command-palette entries - `ctx.diagnostics` — structured, coded diagnostics (powered by [nostics](https://github.com/vercel-labs/nostics)) - `ctx.rpc` — the devframe RPC host (`register`/`invokeLocal`/`broadcast`/`sharedState`/`streaming`/…) - `ctx.scope(namespace)` — the same RPC host with every name prefixed by `namespace:` If you need the raw context outside the hook, `nuxt.devtools.devtoolsKit` is available as an escape hatch (it is `undefined` until the kit connects). See the [Vite DevTools Kit](https://github.com/vitejs/devtools) docs for the full host API. ### Deprecation diagnostics Nuxt DevTools emits coded deprecation diagnostics (`NDT_DEP_xxxx`) through `nostics`. They print to the terminal and, once the Vite DevTools kit connects, also surface in the DevTools diagnostics UI, each with a code, a fix, and a link to the [migration guide](https://nuxt.com/modules/devtools/module-migration). See [`extendServerRpc()`](https://nuxt.com/modules/devtools/module-migration#ndt_dep_0003) and [`startSubprocess()`](https://nuxt.com/modules/devtools/module-migration#ndt_dep_0004). ### `devtools:notify` hook Push a notification into the unified devframe **Messages** system — the same system that powers the Vite DevTools **Messages** dock and its toast overlay. The notification is forwarded to `ctx.messages`, so no direct kit access is needed, and calls made before the kit connects are buffered and replayed once it does: ```ts // server-side (module setup) nuxt.callHook('devtools:notify', { message: 'Something happened', level: 'error', // 'info' | 'warn' | 'error' | 'success' | 'debug' (default 'info') description: 'More details about what happened.', notify: true, // also show a transient toast }) ``` Omit `autoDelete` for a **persistent**, leveled entry that builds history in the Messages dock; set `notify` + `autoDismiss` + `autoDelete` for an **ephemeral**, toast-only notification. From the client, the same input is available on the injected client as `client.devtools.notify(...)`. ## `@nuxt/devtools-kit/iframe-client` To provide complex interactions for your module integrations, we recommend to host your own view and display it in devtools via iframe. ::warning The helpers below depend on the `__NUXT_DEVTOOLS__` object that the Nuxt client injects into **legacy** custom tabs (`addCustomTab()`). A dock entry registered with `ctx.docks.register()` is rendered by Vite DevTools itself and does not receive it. In a native dock, connect to Vite DevTools directly and read the host client from the parent window: ```ts import type { NuxtDevtoolsHostClient } from '@nuxt/devtools-kit/types' import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client' const kit = await getDevToolsRpcClient() const { rpc } = kit.scope('my-module') // the user's app is the parent window of every dock iframe (same origin only) const host = (window.parent as Window & { __NUXT_DEVTOOLS_HOST__?: NuxtDevtoolsHostClient }).__NUXT_DEVTOOLS_HOST__ ``` See [Custom RPC Functions](https://nuxt.com/modules/devtools/module-authors#custom-rpc-functions) for the full flow. :: To get the information from the devtools and the client app in a legacy custom tab, you can do this in your client app: ```ts import { useDevtoolsClient } from '@nuxt/devtools-kit/iframe-client' export const devtoolsClient = useDevtoolsClient() ``` When the iframe been served with the same origin (CORS limitation), devtools will automatically inject `__NUXT_DEVTOOLS__` to the iframe's window object. You can access it as a ref using `useDevtoolsClient()` utility. ### `onDevtoolsReady()` The client-side mirror of the server's [`onDevtoolsReady`](#ondevtoolsready). The callback runs once the Vite DevTools client is connected and receives the `DevToolsRpcClient` (the same object as `client.devtools.devtoolsKit`), giving full devframe-native access. This is the recommended way to register client RPC functions: ```ts import { onDevtoolsReady } from '@nuxt/devtools-kit/iframe-client' onDevtoolsReady((kit) => { kit.client.register({ name: 'my-module:on-update', type: 'event', handler: (payload) => { // ... }, }) }) ``` ### `useDevtoolsClient()` It will return a ref of `NuxtDevtoolsIframeClient` object that is initially `undefined` and will be updated when the connection is ready. `NuxtDevtoolsIframeClient` contains two properties: - `host`: APIs to communicate with the main app in browser - `devtools`: APIs to communicate with the devtools `host` can be undefined when devtools are accessed standalone or from a different origin. For example, you can get the router instance from the client app: ```ts const router = computed(() => devtoolsClient.value?.host?.nuxt.vueApp.config.globalProperties?.$router) ``` The `devtools` object also exposes `devtoolsKit` — the connected Vite DevTools RPC client (mirroring `nuxt.devtools.devtoolsKit` on the server) — for full devframe-native access. Use it to register client RPC functions, call server functions, and more: ```ts const kit = devtoolsClient.value?.devtools.devtoolsKit kit?.client.register({ name: 'my-module:on-update', type: 'event', handler: (payload) => { // ... }, }) ``` This replaces the deprecated `devtools.extendClientRpc()`. ### `onDevtoolsClientConnected()` Similiar to `useDevtoolsClient()` but as a callback style: ```ts import { onDevtoolsClientConnected } from '@nuxt/devtools-kit/iframe-client' onDevtoolsClientConnected(async (client) => { // client is NuxtDevtoolsIframeClient const config = client.devtools.rpc.getServerConfig() // ... }) ``` ## `@nuxt/devtools-kit/host-client` When you have iframe for your devtools view, sometimes you need to communicate with the devtools host (the main app in browser) with a runtime plugin. You can use `@nuxt/devtools-kit/host-client` to do that. ### `useDevtoolsHostClient()` It will return a ref of `NuxtDevtoolsHostClient` object that is initially `undefined` and will be updated when the host is initialized by NuxtDevtools. ```ts import { useDevtoolsHostClient } from '@nuxt/devtools-kit/host-client' export default defineNuxtPlugin({ name: 'my-module:devtools', setup(nuxtApp) { const devtoolsHost = useDevtoolsHostClient() // ... } }) ``` ### `onDevtoolsHostClientConnected()` Similiar to `useDevtoolsHostClient()` but as a callback style: ```ts import { onDevtoolsHostClientConnected } from '@nuxt/devtools-kit/host-client' onDevtoolsHostClientConnected(async (host) => { }) ``` # UI Kit > The deprecated component library for custom DevTools views. ::warning **Deprecated**: `@nuxt/devtools-ui-kit` is no longer recommended for new integrations. This page is kept for existing consumers only. For new integrations, read the [Module Authors Guide](https://nuxt.com/modules/devtools/module-authors) instead. :: DevTools UI Kit lets existing v3-style custom views keep the look and feel of the built-in DevTools UI. The source code can be found under [`packages/devtools-ui-kit`](https://github.com/nuxt/devtools/tree/main/packages/devtools-ui-kit). ## Installation ### Manual Install ```bash npm i @nuxt/devtools-ui-kit ``` ```ts export default defineNuxtConfig({ modules: [ '@nuxt/devtools-ui-kit' ] }) ``` ## Usage Under the hood, `@nuxt/devtools-ui-kit` is an unbundled component library powered by [UnoCSS](https://github.com/unocss/unocss) and [VueUse](https://vueuse.org/). Components are auto imported. In this library, we introduced the `n` attribute for every component to customize the styles and variations. For example, to make a red button: ```html <NButton n="red" /> ``` You can apply the same specifier to any other component, for example: ```html <NCheckbox n="red" /> ``` Apply it to parent components could make a local theme scope ```html <NCard n="green-500"> <!-- both of them are themed green --> <NCheckbox>i accept the terms & conditions</NCheckbox> <NButton>Submit</NButton> </NCard> ``` Please check our [Online Demo](https://ui-kit.devtools.nuxtjs.org/) for more components usages. ## Components Check [all components](https://github.com/nuxt/devtools/blob/main/packages/devtools-ui-kit/src/components). ## Theming Powered by [UnoCSS](https://github.com/unocss/unocss), you can use Tailwind CSS v3 compatible utilities to quickly customize the look and feel of components. It's also possible to override the default theme globally, for example: ```ts // nuxt.config.js export default defineNuxtConfig({ modules: [ '@nuxt/devtools-ui-kit' ], unocss: { shortcuts: { 'n-button-base': 'border n-border-base rounded shadow-sm op80 !outline-none', 'n-button-hover': 'op100 !border-context text-context', 'n-button-active': 'n-active-base bg-context/5', } } }) ``` You can find all the default values and available entries in [src/unocss.ts](https://github.com/nuxt/devtools/blob/main/packages/devtools-ui-kit/src/unocss.ts). # Migrating Modules to v4 > Move your module integration from the Nuxt DevTools v3 API to the Vite DevTools based v4 API. Nuxt DevTools v4 is a [Vite DevTools](https://github.com/vitejs/devtools) integration. The Nuxt-specific hooks and helpers of v3 (`addCustomTab`, `extendServerRpc`, `startSubprocess`, `devtools:customTabs`, …) are replaced by one entry point, [`onDevtoolsReady()`](https://nuxt.com/modules/devtools/utils-kit#ondevtoolsready), which hands you the Vite DevTools node context. From there you register **docks** (tabs), **RPC functions**, **terminals** and **messages** on the same hosts every other Vite tool uses. If you only use Nuxt DevTools in your app, read [Upgrading to v4](https://nuxt.com/modules/devtools/upgrading-to-v4) instead. ## Should I migrate now? Yes. DevTools v4 runs on Nuxt 4.5+ and Nuxt 5, so the new API covers every Nuxt version that can run v4. The v3 API keeps working on v4 through compatibility shims, each of which prints a coded `NDT_DEP_xxxx` warning in the user's terminal with a link to the matching section below. Those shims stay for the whole v4 cycle and are removed in v5. A migrated module requires DevTools v4. On a project still running DevTools v3 (Nuxt 4 without the [override](https://nuxt.com/modules/devtools/upgrading-to-v4#nuxt-45)), the `devtools:ready` hook never fires and your integration simply does not register; nothing breaks. ## At a glance | v3 | v4 | Warning | | -------------------------------------------- | ----------------------------------------------------------- | ------------------------------- | | `addCustomTab()` / `devtools:customTabs` | `ctx.docks.register({ type: 'iframe', … })` | [`NDT_DEP_0005`](#ndt_dep_0005) | | `refreshCustomTabs()` | `ctx.docks.register(entry, true)` or the `update()` handle | [`NDT_DEP_0006`](#ndt_dep_0006) | | `view: { type: 'launch' }` | `ctx.docks.register({ type: 'launcher', … })` | [`NDT_DEP_0005`](#ndt_dep_0005) | | `extendServerRpc()` | `ctx.scope('my-module').rpc.register()` | [`NDT_DEP_0003`](#ndt_dep_0003) | | `nuxt.devtools.rpc.broadcast` / `.functions` | `ctx.scope('my-module').rpc.broadcast()` | [`NDT_DEP_0007`](#ndt_dep_0007) | | `startSubprocess()` / `devtools:terminal:*` | `ctx.terminals.startChildProcess()` | [`NDT_DEP_0004`](#ndt_dep_0004) | | `startSubprocess().getProcess()` | `.getResult()` | [`NDT_DEP_0001`](#ndt_dep_0001) | | `getServerData` RPC | Data Inspector `Nuxt Application` source | [`NDT_DEP_0009`](#ndt_dep_0009) | | `client.devtools.extendClientRpc()` (iframe) | `kit.scope('my-module').rpc.register()` | console warning | | `useDevtoolsClient()` (iframe) | `getDevToolsRpcClient()` from `@vitejs/devtools-kit/client` | — | | `devtools.vscode` option | `devtools.codeServer` | [`NDT_DEP_0008`](#ndt_dep_0008) | The rest of this page walks through a migration in the order you will hit it. The repo's [`module-starter` playground](https://github.com/nuxt/devtools/tree/main/playgrounds/module-starter) is the finished result. ## Step 1: dependencies ```diff [package.json] { "dependencies": { - "@nuxt/devtools-kit": "^3.0.0" + "@nuxt/devtools-kit": "^4.0.0", + "@vitejs/devtools-kit": "^0.8.0" } } ``` `@vitejs/devtools-kit` provides the browser-side RPC client for your iframe and the types for the node context. Keep `@nuxt/devtools` as a dev dependency for your playground. ## Step 2: register your tab as a dock entry {#ndt_dep_0005} `addCustomTab()` and the `devtools:customTabs` hook are deprecated (`NDT_DEP_0005`). Register a dock entry from `onDevtoolsReady()` and put it in the `Nuxt` group so it shows up next to the built-in tabs: ```diff - import { addCustomTab } from '@nuxt/devtools-kit' + import { NUXT_DEVTOOLS_GROUP_ID, onDevtoolsReady } from '@nuxt/devtools-kit' - addCustomTab({ - name: 'my-module', - title: 'My Module', - icon: 'carbon:apps', - view: { type: 'iframe', src: '/__my-module' }, - }) + onDevtoolsReady((ctx) => { + ctx.docks.register({ + id: 'my-module', + title: 'My Module', + icon: 'carbon:apps', + type: 'iframe', + url: '/__my-module', + groupId: NUXT_DEVTOOLS_GROUP_ID, + }) + }) ``` Field mapping: `name` → `id`, `view.src` → `url`, `view.type` → `type`. Icons are Iconify names or image URLs, as before. - **Categories.** Set `category` on the entry (`app`, `analyze`, `server`, `modules`, `documentation`, `advanced`) to pick its section inside the `Nuxt` group. Legacy custom tabs are listed under `modules`; set `category: 'modules'` to sit beside them. - **Lazy launch.** Replace `view: { type: 'launch' }` with a native `launcher` entry, then re-register the same `id` as an `iframe` once the service is up (the second argument forces the replacement): ```ts onDevtoolsReady((ctx) => { ctx.docks.register({ id: 'my-module', title: 'My Module', icon: 'carbon:apps', type: 'launcher', groupId: NUXT_DEVTOOLS_GROUP_ID, launcher: { title: 'Start My Module', description: 'Starts the inspector server on demand.', async onLaunch() { await startInspector() ctx.docks.register({ id: 'my-module', title: 'My Module', icon: 'carbon:apps', type: 'iframe', url: '/__my-module', groupId: NUXT_DEVTOOLS_GROUP_ID, }, true) }, }, }) }) ``` - **Leaving the group.** Omit `groupId` to register a top-level dock next to Vue DevTools and Vite Inspect instead of inside `Nuxt`. - **`vnode` views** have no native equivalent: a dock renders in its own iframe, so it cannot receive a Vue vnode from the Nuxt client. Move the UI into an iframe page, or keep `addCustomTab()` for that tab until you do. ### `refreshCustomTabs()` {#ndt_dep_0006} Deprecated (`NDT_DEP_0006`). There is no tab list to re-collect; change the entry itself. `register()` returns a handle for patching the fields of an entry, and `register(entry, true)` replaces it wholesale (for example to change its `type`, as in the launcher example above): ```ts onDevtoolsReady((ctx) => { const entry = ctx.docks.register({ id: 'my-module', /* … */ }) entry.update({ badge: '3' }) }) ``` ## Step 3: server RPC {#ndt_dep_0003} `extendServerRpc()` is deprecated (`NDT_DEP_0003`). Register functions on the Vite DevTools RPC host. Use `ctx.scope()` so every name is prefixed with your namespace without you repeating it: ```diff - import { extendServerRpc, onDevToolsInitialized } from '@nuxt/devtools-kit' + import { onDevtoolsReady } from '@nuxt/devtools-kit' - onDevToolsInitialized(() => { - const rpc = extendServerRpc('my-module', { - toUpperCase(t: string) { - rpc.broadcast.greeting('world') - return t.toUpperCase() - }, - }) - }) + onDevtoolsReady((ctx) => { + const { rpc } = ctx.scope('my-module') + + rpc.register({ + name: 'to-upper-case', // registered as `my-module:to-upper-case` + type: 'query', + handler(t: string) { + rpc.broadcast({ method: 'greeting', args: ['world'], event: true }) + return t.toUpperCase() + }, + }) + }) ``` `type` is `'query'` for reads, `'action'` for mutations, `'event'` for fire-and-forget and `'static'` for values that never change. Pass `args` / `returns` [Standard Schema](https://standardschema.dev/) validators to type and validate a function at the boundary. ### `nuxt.devtools.rpc` direct access {#ndt_dep_0007} Reading `nuxt.devtools.rpc.broadcast` or writing to `nuxt.devtools.rpc.functions` is deprecated (`NDT_DEP_0007`). Both map onto the scoped RPC host above: ```diff - nuxt.devtools.rpc.broadcast.myEvent.asEvent(payload) + onDevtoolsReady((ctx) => { + ctx.scope('my-module').rpc.broadcast({ method: 'my-event', args: [payload], event: true }) + }) - nuxt.devtools.rpc.functions.myFn = handler + onDevtoolsReady((ctx) => { + ctx.scope('my-module').rpc.register({ name: 'my-fn', handler }) + }) ``` ## Step 4: the iframe client A native dock is rendered by Vite DevTools, not by the Nuxt client, so the `__NUXT_DEVTOOLS__` object that `useDevtoolsClient()` and `onDevtoolsClientConnected()` rely on is **not injected** into it. Connect to Vite DevTools directly instead: ```diff - import { onDevtoolsClientConnected } from '@nuxt/devtools-kit/iframe-client' + import { getDevToolsRpcClient } from '@vitejs/devtools-kit/client' - onDevtoolsClientConnected(async (client) => { - const rpc = client.devtools.extendClientRpc('my-module', { - greeting(t: string) { - console.log(`Hello ${t}`) - }, - }) - const result = await rpc.toUpperCase('hello') - }) + const kit = await getDevToolsRpcClient() + const { rpc } = kit.scope('my-module') + + rpc.register({ + name: 'greeting', // receives the `my-module:greeting` broadcast + type: 'event', + handler(t: string) { + console.log(`Hello ${t}`) + }, + }) + const result = await rpc.call('to-upper-case', 'hello') ``` The user's app is the parent window of every dock iframe, so the host client that v3 exposed as `client.host` is still reachable on it from a same-origin iframe: ```ts import type { NuxtDevtoolsHostClient } from '@nuxt/devtools-kit/types' const host = (window.parent as Window & { __NUXT_DEVTOOLS_HOST__?: NuxtDevtoolsHostClient }).__NUXT_DEVTOOLS_HOST__ host?.nuxt.vueApp.version host?.devtools.close() ``` `host` is `undefined` when your page is opened outside DevTools or served from another origin. `client.devtools.extendClientRpc()` is deprecated and logs a console warning; `useDevtoolsClient()` still works for tabs that are still registered through `addCustomTab()`. ## Step 5: terminals {#ndt_dep_0004} `startSubprocess()` is deprecated (`NDT_DEP_0004`). Spawn processes through the Vite DevTools terminals host; they show up in the shared **Terminals** dock with restart and terminate controls: ```diff - import { startSubprocess } from '@nuxt/devtools-kit' + import { onDevtoolsReady } from '@nuxt/devtools-kit' - const subprocess = startSubprocess( - { command: 'vite', args: ['build', '--watch'] }, - { id: 'my-module:build', name: 'Build', icon: 'ph:terminal-duotone' }, - ) + onDevtoolsReady(async (ctx) => { + const session = await ctx.terminals.startChildProcess( + { command: 'vite', args: ['build', '--watch'], cwd: process.cwd() }, + { id: 'my-module:build', title: 'Build', icon: 'ph:terminal-duotone' }, + ) + }) ``` The session exposes `terminate()`, `restart()`, `getChildProcess()` and `getResult()`, an awaitable `{ stdout, stderr, exitCode }`: ```ts const { exitCode, stderr } = await session.getResult() if (exitCode !== 0) console.error(stderr) ``` `ctx.terminals.startPtySession()` gives you an interactive PTY, and `ctx.terminals.register({ id, title, status, stream })` surfaces output from a process you keep owning yourself. ### Legacy `devtools:terminal:*` hooks Calling `devtools:terminal:register` / `:write` / `:exit` / `:remove` directly (which is what `startSubprocess()` does internally) is bridged onto the Terminals dock **for output and final status only**: - `restartable` / `terminatable` and `onActionRestart` / `onActionTerminate` are ignored: no buttons are shown for a bridged session; - re-registering an id (`clear()` / `restart()`) shows up as a new session rather than resetting the old one in place; - `terminate()` / `restart()` on a `startSubprocess()` handle still work, because that helper owns its process. ### `SubprocessOptions` no longer extends execa `startSubprocess()` moved from `execa` to `tinyexec`. `command`, `args`, `cwd` and `env` are unchanged; every other execa option goes under `nodeOptions` (Node's `SpawnOptions`): ```diff startSubprocess({ command: 'my-command', cwd: '/some/path', - stdio: 'pipe', + nodeOptions: { stdio: 'pipe' }, }) ``` ### `getProcess()` {#ndt_dep_0001} Deprecated (`NDT_DEP_0001`); it returns a plain `ChildProcess | undefined` now. `getResult()` returns the tinyexec result with `.process`, `.kill()` and `.pipe()`: ```diff - const proc = subprocess.getProcess() - proc.stdout.on('data', handler) + const result = subprocess.getResult() + result.process?.stdout?.on('data', handler) ``` ## Step 6: notifications `ctx.messages` is the Vite DevTools messages host. `ctx.messages.add({ message, level, notify })` adds an entry to the **Messages** dock; `notify: true` also shows it as a toast: ```ts onDevtoolsReady((ctx) => { ctx.messages.add({ message: 'My module is ready', level: 'info', notify: true }) }) ``` The `devtools:notify` Nuxt hook forwards to the same host and is not deprecated. ## Other changes ### `getServerData` RPC {#ndt_dep_0009} The read-only Nuxt Options Viewer is replaced by the [Data Inspector](https://nuxt.com/modules/devtools/features#data-inspector), whose `Nuxt Application` source exposes the same data through a live jora workbench. The `getServerData` RPC that fed the old page still answers with the legacy `{ nuxt, nitro, vite: { server, client } }` shape but prints `NDT_DEP_0009` on first use. To expose your own data, install [`@devframes/plugin-data-inspector`](https://npmx.dev/package/@devframes/plugin-data-inspector) and register a source with its `registerDataSource` API. ### `vscode` option {#ndt_dep_0008} The built-in VS Code integration is replaced by the Code Server plugin (`devtools.codeServer`). Supplying `devtools.vscode` prints `NDT_DEP_0008` and the value is ignored. See [Upgrading to v4](https://nuxt.com/modules/devtools/upgrading-to-v4#code-server) for the user-facing details. ### Removed APIs - `@nuxt/devtools-wizard`, `nuxi devtools enable/disable` and the `runWizard` RPC. The one action that remains, `enablePages`, is a direct RPC function with no arguments: `rpc.enablePages()`. The `WizardFunctions`, `WizardActions` and `GetWizardArgs` types are gone. - `client.devtools.popup` (Picture-in-Picture) and the `showPanel` / `minimizePanelInactive` UI settings; Vite DevTools owns the panel. - Global installs: `devtoolsGlobal`, the `-g` install flag and `NuxtDevtoolsInfo.isGlobalInstall`. - The `viteDevTools` module option; Vite DevTools is always on. On Vite 8.3+ Nuxt DevTools configures Vite's top-level `devtools` option, so `vite: { devtools: false }` turns it off. - The static assets RPCs `getStaticAssets`, `getImageMeta`, `getTextAssetContent`, `writeStaticAssets`, `deleteStaticAsset` and `renameStaticAsset`, together with the `AssetInfo`, `AssetEntry`, `AssetType` and `ImageMeta` types. The Assets tab is now [`@devframes/plugin-assets`](https://npmx.dev/package/@devframes/plugin-assets). ### Changed behavior - `client.devtools.open()` / `close()` / `toggle()` still exist and now drive the Vite DevTools panel. ### Peer dependencies `@nuxt/devtools` requires Vite `^8.1.5` (was `>=6.0`) and `@nuxt/kit` 4 or 5. Nitro v2 (`nitropack`, Nuxt 4) and Nitro v3 (`nitro`, Nuxt 5) are both optional peers; the one your user's Nuxt installs is the one that gets used. `unstorage` is an optional peer for the same reason: the Storage tab uses the copy that Nitro brings. # Contribution Guide > Learn how to contribute to the Nuxt DevTools. Hi! We're really excited that you're interested in contributing to Nuxt DevTools! Before submitting your contribution, please read through the following guide. ## Monorepo The [Nuxt DevTools repo](https://github.com/nuxt/devtools) is a monorepo using pnpm workspaces. The package manager used to install and link dependencies must be [pnpm](https://pnpm.io/). After cloning the repo, run in the root folder: ```sh pnpm i pnpm run build ``` ::callout Nuxt DevTools uses pnpm v12. If you are working on multiple projects with different versions of pnpm, it's recommended to enable [Corepack](https://github.com/nodejs/corepack) by running `corepack enable`. :: ## Packages This repo contains the following packages: - `@nuxt/devtools`: the Nuxt DevTools module. Its `client/` directory is the Nuxt app rendering the DevTools UI. - `@nuxt/devtools-kit`: the [utility kit](https://nuxt.com/modules/devtools/utils-kit) for module authors. - `@nuxt/devtools-assets`: the prebuilt client UI, served on demand. - `@nuxt/devtools-onboard`: the entry point Nuxt 5 ships, which loads Nuxt DevTools or offers to install it. - `@nuxt/devtools-ui-kit`: the UI components used by the client, [deprecated](https://nuxt.com/modules/devtools/ui-kit) for third-party integrations. ### DevTools Most of the scripts are forwarded to the root `package.json`. Run `pnpm dev` in the root folder to start the development server (Nuxt DevTools on top of its own client UI). Or you can `cd packages/devtools` and run `pnpm dev` to start the development server. The playgrounds under `playgrounds/` use the published build by default. Set `NUXT_DEVTOOLS_LOCAL=true` to run them against the source code instead. ### UI Kit Normally when you are developing the Nuxt DevTools, the components in the UI Kit already get the HMR capabilities. But if you want to develop the UI Kit itself, you can run `cd packages/devtools-ui-kit` and run `pnpm dev` to start the playground for the UI Kit. ## Checks Before opening a pull request, run: ```sh # ESLint, then the unit tests pnpm test # Type checks and unused code, after `pnpm build` or `pnpm dev:prepare` pnpm typecheck pnpm knip # End-to-end tests, against the dev server and a built app pnpm test:e2e:dev pnpm test:e2e:built ``` ## Trying Local Changes If you want to try your local changes in other Nuxt projects, you can use the `local.ts` module under the root folder. It runs the DevTools client from source in a dev server of its own. For example, if you clone this repo to `/users/me/nuxt-devtools`, update your `nuxt.config.ts`: ```diff [nuxt.config.ts] export default defineNuxtConfig({ modules: [ - '@nuxt/devtools', + '/users/me/nuxt-devtools/local', ] }) ``` On the module side, the source TypeScript files are used directly, so you don't need to build every time. However, due to Node.js module caching, you need to restart your app to see the changes on the module side. ## Documentation The documentation lives in the [`docs/`](https://github.com/nuxt/devtools/tree/main/docs) directory and is published on [nuxt.com/modules/devtools](https://nuxt.com/modules/devtools). Keep images next to the pages in `docs/images/` and link them with a relative path, such as `![Overview tab](./images/overview.webp)`. The DevTools screenshots are generated from the [`docs` playground](https://github.com/nuxt/devtools/tree/main/playgrounds/docs), a small shop with pages, components, server routes, a task and a storage mount. After a UI change, regenerate them with: ```sh pnpm build pnpm exec playwright install chromium pnpm docs:screenshots ``` Each screenshot is a step in [`tests/docs-screenshots/screenshots.spec.ts`](https://github.com/nuxt/devtools/blob/main/tests/docs-screenshots/screenshots.spec.ts): add one there to document a new tab. The `docs-screenshots` workflow, run from the Actions tab, does the same on CI and opens a pull request with the updated images. To preview your changes, run the [nuxt.com](https://github.com/nuxt/nuxt.com) dev server with `NUXT_DEVTOOLS_PATH` set to your local clone of this repo. Pull requests changing `docs/` can also be previewed on [nuxt.com](http://nuxt.com) once a maintainer adds the `preview:enabled` label. # Nuxt Fonts > Plug-and-play web font optimization and configuration for Nuxt apps. ## Introduction Write a `font-family` declaration and Nuxt Fonts finds the font, serves it from your own origin, and generates fallback metrics to reduce layout shift. There's nothing to import and no component to wrap your text in. ::video ```yaml [props] src: https://nuxt.com/modules/fonts/images/demo.mp4 poster: /modules/fonts/images/demo-poster.jpg controls: true preload: none width: '1440' height: '1080' ``` :: - **No configuration to start**: add the module and keep writing CSS. Every option is a default you can change, not a step you have to take. - **Eight built-in providers**: `local`, `google`, `googleicons`, `bunny`, `fontshare`, `fontsource`, `npm` and `adobe`, plus any [provider](https://nuxt.com/modules/fonts/providers) you write. - **Self-hosted in production**: fonts are downloaded at build time and served from your own origin with long-lived cache headers. - **Less layout shift**: a local system font is scaled to match each web font's metrics, so text barely moves when the real file arrives. - **Smaller files when you ask**: the [`glyphs`](https://nuxt.com/modules/fonts/configuration#glyphs) option subsets a family down to the characters you actually render. ## Automatic Installation To get started, add `@nuxt/fonts` to your project: ```bash npx nuxt module add fonts ``` ## Manual Installation Or you can install it manually. ::code-group ```bash [pnpm] pnpm i @nuxt/fonts ``` ```bash [yarn] yarn add @nuxt/fonts ``` ```bash [npm] npm i @nuxt/fonts ``` :: Then, add it to the `modules` in your `nuxt.config`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/fonts', ] }) ``` ## Usage You can now get started by adding a `font-family` declaration to your CSS. ```vue <template> <div> Hello Nuxt Fonts! </div> </template> <style scoped> div { font-family: Roboto, sans-serif; } </style> ``` That's it! Nuxt Fonts will detect this and you should immediately see the web font loaded in your browser. [Read more about how it works](https://nuxt.com/modules/fonts/advanced#how-it-works). ::note{to="https://nuxt.com/modules/fonts/configuration"} To keep downloads small, `@nuxt/fonts` loads the `400 700` weight range by default rather than every weight a family ships. The *configuration docs* cover how to change that, along with every other option. :: ## Troubleshooting If an error occurs during installation: - Ensure that you are using the latest LTS version of Node.js ([release schedule](https://github.com/nodejs/release#release-schedule)) - Try to upgrade to latest versions: ::code-group ```bash [pnpm] pnpm up @nuxt/fonts ``` ```bash [yarn] yarn upgrade @nuxt/fonts ``` ```bash [npm] npm up @nuxt/fonts ``` :: - Try recreating your lock-file: ```bash npx nuxt upgrade --force ``` - Check your network connection. Your machine might be having a hard time communicating with the font providers. - If none of the above worked, please [open an issue](https://github.com/nuxt/fonts/issues) and include the error trace, OS, Node version and the package manager used for installing. - If you're missing certain fonts or their variations, please confirm the module is [configured to load](https://nuxt.com/modules/fonts/configuration#styles) the fonts you expected to see. # Configuration > Nuxt Fonts works out of the box with zero config, but you can always add some configurations for finer-grained control. You do not need to configure Nuxt Fonts but you can do so for finer-grained control, with the `fonts` property in your `nuxt.config`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { // Options } }) ``` ## Font options These options define how fonts will be downloaded, including the weights, styles, subsets, etc. These options can be used as a default behaviour with [`defaults`](#defaults) or a separate behaviour for a specific font with [`families`](#families). ### `defaults` This defines the default font options for all fonts. Example of `defaults`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { defaults: { weights: ['400 700'], styles: ['normal', 'italic'], subsets: [ 'cyrillic-ext', 'cyrillic', 'greek-ext', 'greek', 'vietnamese', 'latin-ext', 'latin', ] }, } }) ``` #### `weights` Default: `['400 700']` Defines the font-weights that should be downloaded for a font. ::tip To specify a variable font weight, define the range as you would in plain CSS (for example, `100 900`). See [variable fonts](https://nuxt.com/modules/fonts/usage#variable-fonts) for how each provider handles a range. :: The [`local` provider](https://nuxt.com/modules/fonts/providers#local) also accepts named weights, in either the hyphenated or unhyphenated spelling, so `weights: ['medium', 'semibold']` is equivalent to `weights: [500, 600]`. Remote providers expect numeric weights. #### `styles` Default: `['normal', 'italic']` Defines the styles that should be downloaded for a font. #### `subsets` Default: `['cyrillic-ext', 'cyrillic', 'greek-ext', 'greek', 'vietnamese', 'latin-ext', 'latin']` Defines the subsets that should be downloaded for a font. Characters outside the downloaded subsets render in a fallback font. If your content uses another script, such as Arabic, Hebrew, or Devanagari, add its subset here or for a single font in [`families`](#families): ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'Cairo', subsets: ['arabic', 'latin', 'latin-ext'] }, ], } }) ``` #### `glyphs` Default: *None* Reduces every font file that is emitted to the glyphs needed to render these characters. Accepts a string of text, an array of characters, or [`unicode-range`](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/unicode-range) values, and can be overridden for a single font with [`families`](#families). ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { defaults: { glyphs: 'Handgloves & 0123', }, } }) ``` Unicode ranges use the same syntax as the `unicode-range` descriptor, and can be mixed with characters: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { defaults: { glyphs: ['U+0020-007E', 'U+2018-201D', 'U+2026'], }, } }) ``` A range is expanded to the characters it covers, so keep them tight. Ranges covering more than 65,536 codepoints in total are ignored with a warning. Where a provider can subset server-side, such as Google Fonts, the characters are passed through to it so the full file is never downloaded. Every other font file is subsetted after download, which needs the [`subset-font`](https://github.com/papandreou/subset-font) package. We will offer to install it the first time you run Nuxt with `glyphs` set, or you can add it yourself: ```bash [Terminal] npm install --save-dev subset-font ``` It is not a dependency of Nuxt Fonts, as the harfbuzz WebAssembly it loads is several megabytes and only projects that subset a font need it. Where we cannot ask, such as in CI, we warn instead, and the build fails before downloading any font that needs subsetting locally. ::warning Subsetting modifies the font files you ship. Check that the licence of each font allows it. :: #### `variableAxis` Default: *None* Chooses the values a variable font is shipped at, for every family. Each OpenType axis tag takes an array of values: a number or string, such as `CASL: [1]`, pins the axis to a single value, and a `[min, max]` pair or `{ min, max }` object, such as `MONO: [{ min: 0, max: 1 }]`, narrows it to an inclusive range. This can be overridden for a single font with [`families`](#families). ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { defaults: { variableAxis: { CASL: [1], MONO: [{ min: 0, max: 1 }], }, }, } }) ``` Providers that can instance a font server-side, such as Google Fonts, serve the file already resolved at those values. Otherwise the axis is applied to the downloaded file with [`subset-font`](https://github.com/papandreou/subset-font), which needs a glyph list to subset to: either the family's `glyphs`, or the characters the face's `unicode-range` declares. Without either, the file is emitted untouched and a `font-variation-settings` descriptor renders the font at the requested values instead. `wght` and `ital` are left alone, because `@font-face` descriptors already express them. ::warning Applying an axis is a subsetting pass, so it modifies the font files you ship. Check that the licence of each font allows it. :: ::note This option is experimental. Provider support and the option shape may change. :: #### `fallbacks` Default: ```ts { 'serif': ['Times New Roman', 'Georgia', 'Noto Serif'], 'sans-serif': ['Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'], 'monospace': ['Courier New', 'Roboto Mono', 'Noto Sans Mono'], 'cursive': ['Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'], 'fantasy': ['Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'], 'system-ui': ['Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'], 'ui-serif': ['Times New Roman', 'Georgia', 'Noto Serif'], 'ui-sans-serif': ['Segoe UI', 'Helvetica Neue', 'Arial', 'Noto Sans'], 'ui-monospace': ['Courier New', 'Roboto Mono', 'Noto Sans Mono'], 'ui-rounded': [], 'emoji': [], 'math': [], 'fangsong': [], } ``` If you use a generic font family like `Roboto, sans-serif`, we will 'translate' that generic family name into one or more font families when generating fallback metrics. You can customize which families we use. (One or two works best.) #### `preload` Default: *automatic* Controls which `@font-face` declarations get a `<link rel="preload">` in the initially rendered HTML. Preload hints are only rendered in production builds. By default we preload the highest priority font face for a family, as long as it has a remote or local URL and does not define a `unicode-range` (in other words, subsetted fonts like those from Google Fonts are not preloaded). Set `preload: true` to preload a single font face for every family. It picks a face from the first configured [`subsets`](#subsets) entry (otherwise one covering Basic Latin), prefers upright over italic, and then prefers the weight closest to 400. Pass an object with any of `subsets`, `styles` and `weights` to preload every font face that matches all of the given values. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { defaults: { // preload only the matching font faces preload: { subsets: ['latin'], styles: ['normal'], weights: [400] }, // or preload one upright font face near weight 400 in the first configured subset (or Basic Latin) // preload: true, // or opt out entirely // preload: false, // or decide for each font face // preload: (family, font) => font.weight === 400, }, } }) ``` A family reduced to a set of [`glyphs`](#glyphs) is not split into named subsets, so it is not excluded by a `subsets` filter. ### `families` This is an array which defines the font options for specific fonts. You can use any properties from [defaults](#defaults), while there are some additional properties: Example of `families`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ // do not resolve this font with any provider from `@nuxt/fonts` { name: 'Custom Font', provider: 'none' }, // only resolve this font with the `google` provider { name: 'My Font Family', provider: 'google' }, // specify specific font data - this will bypass any providers { name: 'Other Font', src: 'https://example.com/font.woff2', weight: 'bold' }, // entries sharing a name are combined, so each `src` becomes its own font face { name: 'Brand Font', src: '/brand-regular.woff2', weight: 400 }, { name: 'Brand Font', src: '/brand-black.woff2', weight: 900 }, ] } }) ``` #### `name` Default: *None* Defines the name of the font that should be given the option. This is required for every item in the `families` array. #### `global` Default: `false` Defines whether to inject `@font-face` regardless of usage in project. This is the escape hatch for fonts Nuxt Fonts can't detect by scanning your CSS: see [when a font isn't picked up](https://nuxt.com/modules/fonts/usage#when-a-font-isnt-picked-up). The `@font-face` rule and any preload hints come from a single stylesheet loaded on every page. Font fallback metrics work by rewriting the `font-family` declaration that uses the family, so they're generated wherever Nuxt Fonts can see the family being used in your CSS, and not for the usage it can't see. #### `provider` Default: *None* Defines the provider that is used for the given font. You can choose any providers from `none`, `google`, `googleicons`, `bunny`, `fontshare`, `fontsource`, `adobe`, `npm`, `local`. #### `src` Default: *None* Defines the src that should be used for the given font. If this is defined, then no other providers will be used for the given font family. #### `preload` Default: *inherited from [`defaults.preload`](#preload)* Overrides the preload behaviour for this font family. The option is used wherever the family is preloaded, which is every route it is used on unless a route rule names the family, in which case it is preloaded on the routes that match the rule, with the option the rule gives for it, and on no others. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'Roboto', preload: { subsets: ['latin'] } }, ] } }) ``` A family named in a route rule is preloaded on the routes that match it, and on no others. The rule uses the same patterns as any other route rule, so `/inbox/**` matches the inbox route and everything below it. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], routeRules: { '/inbox/**': { fonts: { preload: { 'JetBrains Mono': { subsets: ['latin'] } } } }, } }) ``` Each family is preloaded with the option given for it, which takes the same shape as the [`preload`](#preload) option of a family. A family named in more than one rule is preloaded with the faces the first of them asks for. The option of a family belongs either here or in the [`preload`](#preload) option of the family, not in both. A family named in a rule is not preloaded on any other route, so an option set for it in `fonts.families` has no effect. The font files of a family restricted to route rules are preloaded by the server, as their URLs are only known once the fonts have been resolved. The preload is therefore only rendered in the HTML of the routes that match. #### `display` Default: *None* (`swap` when the resolved font data does not specify one) Defines the `font-display` descriptor of every `@font-face` generated for the given font family, overriding any value the provider returns. #### `unicodeRange` Default: *None* Defines the `unicode-range` descriptor of every `@font-face` generated for the given font family, overriding any value the provider returns. This can be used to load a font for a limited set of characters only. A family with a `unicode-range` counts as subsetted, so it is not preloaded unless you set [`preload`](#preload) for it explicitly. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'Rowdies', provider: 'google', display: 'optional', unicodeRange: ['U+0040'] }, ] } }) ``` #### `ascentOverride`, `descentOverride`, `lineGapOverride` and `sizeAdjust` Default: *None* Defines the [font metric override descriptors](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/ascent-override) of every `@font-face` generated for the given font family. These apply to the family itself; the metrics of the fallback fonts we generate for it are calculated for you. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'My Font', src: '/my-font.woff2', descentOverride: '20%' }, ] } }) ``` #### `glyphs` Default: *inherited from [`defaults.glyphs`](#defaults)* Reduces every font file emitted for this family to the glyphs needed to render these characters, whichever provider served it. A `unicode-range` covering those characters is generated for you. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'Rowdies', provider: 'google', glyphs: 'Nuxt Fonts' }, { name: 'Satoshi', glyphs: ['U+0020-007E', 'U+2018-201D', 'U+2026'] }, ] } }) ``` #### `variableAxis` Default: *inherited from [`defaults.variableAxis`](#defaults)* Chooses the values this family's variable font is shipped at, with an array of values for each OpenType axis tag. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'Recursive', variableAxis: { CASL: [1], MONO: [{ min: 0, max: 1 }] } }, ] } }) ``` ## Provider Options Providers can have their own options. These options are passed directly to different font providers. Please refer to the [provider documentation](https://nuxt.com/modules/fonts/providers) for more information. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { google: {}, local: {}, npm: {}, adobe: { id: ['fontkitId1', 'fontkitId2'], } } }) ``` ## `providers` This defines the behavior of the providers used. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { providers: { // You can pass a new custom provider - see more in the providers documentation // for what this file should look like custom: '~/providers/custom', // Or you can disable a built-in provider google: false, } } }) ``` ## `assets` This defines the behavior of the assets used. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { assets: { // The directory font files are served from. prefix: 'fonts' } } }) ``` In a Vite production build, `prefix` names a directory inside `app.buildAssetsDir`, so the default resolves to `/_nuxt/fonts`. In development, and with the webpack and rspack builders, it is a public path and defaults to `/_fonts`. See [where fonts are served from](https://nuxt.com/modules/fonts/advanced#where-fonts-are-served-from). ## `cache` Font metadata and downloaded font files are cached between builds in `node_modules/.cache/nuxt/fonts/meta`. You can change where that happens, provide your own [unstorage](https://unstorage.unjs.io/) instance, or turn persistent caching off (in which case an in-memory cache is used). ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { // a directory, resolved relative to your project root cache: '.cache/fonts', // or your own storage // cache: createStorage({ driver: redisDriver({ base: 'fonts' }) }), // or no persistent cache at all // cache: false, } }) ``` ## `priority` You can customize the order in which providers are checked. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { priority: ['bunny', 'google'], } }) ``` ## `provider` In some cases you may wish to use only one font provider. This is equivalent to disabling all other font providers, so a per-family `provider` other than the one configured here will not be used. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { provider: 'google' } }) ``` ## `devtools` Default: `true` Adds a **Fonts** panel to [Nuxt DevTools](https://nuxt.com/modules/devtools), listing every family resolved for your app: the provider that served it, the faces generated for it, where each is used, and any warnings raised while resolving it. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { // Disable the Nuxt DevTools integration devtools: false } }) ``` ## `processCSSVariables` You can enable support for processing CSS variables for font family names. Available options: `true`, `false`, `font-prefixed-only`, or a custom prefix. Default is `font-prefixed-only`. Note that `true` might cause some performance impacts. Passing a custom string processes only the CSS variables matching that prefix, so `'my-app'` will process `--my-app-*` variables and nothing else. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { // You can enable support for processing CSS variables for font family names. processCSSVariables: true } }) ``` ::tip If you previously set `processCSSVariables` to `true` for Tailwind v4 support, it is no longer needed or recommended in v0.11.0 and later. :: ## `throwOnError` Defines what happens when no provider can resolve a font, or a font file cannot be downloaded. It defaults to `false` while developing (so a flaky provider does not block you) and `true` when building (so you don't ship a build with missing fonts). A provider that fails does not fail the build on its own: resolution carries on to the next provider, and an error is only raised once none of them can serve the family. Font metadata requests and font file downloads are both retried a few times with backoff before that applies, so it only takes effect for persistent failures. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { // continue the build with a warning instead of failing throwOnError: false } }) ``` ::tip If your builds fail intermittently with a `404` from `fonts.gstatic.com`, consider [caching font downloads between builds](https://nuxt.com/modules/fonts/advanced#caching-fonts-between-builds) or switching to a provider with stable URLs, such as `bunny`. :: ## `experimental` Defines whether to enable experimental features. All of them are `false` by default. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { experimental: { // Stops adding `local()` sources to generated `@font-face` rules. Default is `false`. disableLocalFallbacks: true } } }) ``` # Usage > Use Nuxt Fonts with any CSS framework or library. ## Pure CSS You can use Nuxt Fonts with pure CSS by adding `font-family` declarations to your CSS: ```css [assets/css/fonts.css] div { font-family: 'Inter', sans-serif; } ``` ::note Note that Nuxt Fonts will only generate CSS for the first font in a `font-family` declaration. Subsequent fonts in the list will be used to generate metrics for font fallbacks. :: ::warning Inline styles with `font-family` within your `<template>` don't work; only CSS stylesheets are processed. See [when a font isn't picked up](#when-a-font-isnt-picked-up). :: ## When a font isn't picked up Nuxt Fonts resolves fonts by scanning the CSS your project generates for `font-family` declarations. A font that's never named in that CSS is never resolved, so no `@font-face` rule is emitted and the browser falls back to a system font. Common places a font stays invisible to the scan: - a `style` attribute in a template, or any other inline style - a `font-family` inside an SVG, whether served from `public/` or imported as an asset - a Tailwind v4 `--font-*` theme variable declared with `@theme inline` that no utility uses, which Tailwind drops before Nuxt Fonts sees it - a font named only at runtime, such as a family a user chooses and you apply through a CSS variable - a font set by CSS-in-JS, a third-party stylesheet, or a framework preset that isn't part of your own CSS For all of these, declare the family with `global: true` and Nuxt Fonts emits its `@font-face` rules whether or not it finds a use for them: ```ts [nuxt.config.ts] export default defineNuxtConfig({ fonts: { families: [ { name: 'Anton', provider: 'google', global: true }, ], }, }) ``` You can then use `font-family: 'Anton'` anywhere, including in places Nuxt Fonts never scans. ::note Global families are injected into a single stylesheet loaded on every page, so keep the list short: every visitor downloads the CSS for each one. :: ## Variable fonts To use a variable font, refer to it by its plain family name and request a weight range. The variable version of a family doesn't have a separate name, so `font-family: 'Inter'` resolves to a variable font from any provider that has one: ```css [assets/css/fonts.css] body { font-family: 'Inter', sans-serif; } ``` The default [`weights`](https://nuxt.com/modules/fonts/configuration#weights) of `['400 700']` is already a range. Widen it to make more weights available: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'Inter', weights: ['100 900'] }, ], }, }) ``` Providers handle a weight range differently: - `google` and `fontsource` serve a variable font when the family has one. Google Fonts narrows it to the requested range; Fontsource serves the family's full weight axis. - `fontshare` serves a variable font when the range covers the family's full weight axis, and a static file for each weight in the range otherwise. - `bunny` and `adobe` serve static files only: the lightest weight in the range, the weight closest to `400`, and the heaviest. - `local` uses a file whose name marks it as variable, and otherwise static files for each end of the range. See [variable fonts with the `local` provider](https://nuxt.com/modules/fonts/providers#variable-fonts). For axes other than weight, such as `opsz` or `slnt`, use [`variableAxis`](https://nuxt.com/modules/fonts/configuration#variableaxis). ### Names such as `Inter Variable` or `InterVariable` Some distributions give a variable font its own family name, such as `InterVariable` in the CSS from Inter's website, or `Inter Variable` in `@fontsource-variable/inter`. Remote providers don't recognise these names. A `font-family` that uses one isn't resolved, no warning is logged, and the browser falls back to the next font in the stack. Use the plain family name unless you're serving the font yourself: - The [`npm` provider](https://nuxt.com/modules/fonts/providers#npm) resolves `Inter Variable` when `@fontsource-variable/inter` is installed. - The [`local` provider](https://nuxt.com/modules/fonts/providers#local) matches `InterVariable.woff2` to both `font-family: 'InterVariable'` and `font-family: 'Inter'`. Because only the first family in a `font-family` list is resolved, a stack such as `'Inter var', 'Inter', sans-serif` doesn't load `Inter` either. Put the family you want resolved first. ## Tailwind CSS With Tailwind CSS you specify fonts using a CSS variable: ```css [main.css] @theme { --font-display: "Inter", "sans-serif"; } ``` Overriding Tailwind's default font family works too, including via `@theme inline`, which compiles `--font-sans` down to `--default-font-family`: ```css [main.css] @theme { --default-font-family: "Inter", sans-serif; } ``` ::note Nuxt Fonts reads the CSS Tailwind generates, so a font is only picked up if it ends up there. A `--font-*` theme variable that is never used by a utility (and is declared with `@theme inline`) is removed by Tailwind before Nuxt Fonts sees it. Use the utility at least once, declare it with `@theme static`, or set the font in `fonts.families` with [`global: true`](#when-a-font-isnt-picked-up). :: ::tip If you previously set `processCSSVariables` to `true` for Tailwind v4 support, it is no longer needed or recommended for v0.11.0 and above. :: ## UnoCSS You can use Nuxt Fonts with UnoCSS by setting `fontFamily` in your configuration file: ```js [uno.config.js] import { defineConfig } from 'unocss' export default defineConfig({ theme: { fontFamily: { inter: 'Inter', }, }, }) ``` If you're using the Wind4 (Tailwind4) preset, set `font` instead of `fontFamily`. ```js font: { inter: 'Inter', }, ``` # Providers > Font providers are designed to be pluggable and extensible, so no matter your setup you should be able to use an existing provider or write your own. ## Built-In Providers Nuxt Fonts ships with a number of built-in providers. ### `local` The local provider deeply scans your `public/` directories (including of your layers) for font files (supporting `ttf`, `woff`, `woff2`, `eot` or `otf` extensions). Directories that modules add to `nitro.publicAssets` are not scanned; add them to [`local.dirs`](#fonts-installed-from-npm) to use the fonts within. Then, when you use a `font-family` in your CSS, we check to see whether it matches one of these files. By default, we expect the font file to be the `400` weight, `normal` style, and `latin` subset. To indicate different weights and styles, please include that information in the filename. For example `comic-sans-ms.woff2` (400/normal/latin; defaults), `comic-sans-ms-700-italic-cyrillic.woff2` (700/italic/cyrillic), or for variable fonts `comic-sans-ms-100-900.woff2` (100 900/normal/latin). Named weights are accepted in place of a weight number: `comic-sans-ms-bold.woff2`. `font-family` values with spaces and/or caps will lookup hyphenated lowercase filenames as well, eg `font-family: 'Comic Sans MS'` will look for `comic-sans-ms.woff2` as well as `Comic\ Sans\ MS.woff2` on disk. #### Named weights The following names are recognised, both in filenames and in the `weights` you configure. Hyphenated and unhyphenated spellings are equivalent, so `comic-sans-ms-semibold.woff2` and `comic-sans-ms-semi-bold.woff2` both resolve to `600`. `normal` and `regular` are only read from your configuration. A file is `400` unless its name says otherwise, and a trailing word is dropped when looking for the family, so `comic-sans-ms-regular.woff2` resolves `font-family: 'Comic Sans MS'` at `400` regardless. | Weight | Names | | ------ | -------------------------------------------------------- | | `100` | `thin`, `hairline` | | `200` | `extra-light`, `extralight`, `ultra-light`, `ultralight` | | `300` | `light` | | `400` | `normal`, `regular` | | `500` | `medium` | | `600` | `semi-bold`, `semibold`, `demi-bold`, `demibold` | | `700` | `bold` | | `800` | `extra-bold`, `extrabold`, `ultra-bold`, `ultrabold` | | `900` | `black`, `heavy` | #### Variable fonts A filename can declare a variable weight range as two CSS weights, separated by a space, hyphen or dot: `my-font-100 900.woff2`, `my-font-100-900.woff2` or `my-font.100-900.woff2` are all read as covering `100` to `900`. Both bounds have to be recognised weights (a multiple of 100 between 100 and 900) in ascending order, so `my-font-300-234987akd.woff2` is still read as a single `300` weight. Such a file will be used for any weight or weight range inside the declared range, so `my-font-100-900.woff2` matches `weights: [400]`, `weights: ['400 700']` and `weights: ['100 900']` alike. When no file covers the whole of a requested range, the endpoints of that range are looked up as static weights instead, so a project shipping `my-font.woff2` and `my-font-bold.woff2` resolves the default `weights: ['400 700']` as two separate `@font-face` rules. Files marked as variable without a range (`my-font-variable.woff2`, `my-font-vf.woff2`, or a camel-cased `MyFontVariable.woff2` / `MyFontVF.woff2`) are treated as covering the full `100` to `900` range. The marker is also treated as part of the family name, so `faro-variable.woff2` matches both `font-family: 'Faro Variable'` and `font-family: 'Faro'`, and `CalSansVF.woff2` matches both `font-family: 'CalSansVF'` and `font-family: 'CalSans'`. ::warning{to="https://nuxt.com/modules/fonts/configuration#font-options"} Please note that the local provider will only attempt to load styles and weights that were configured in the *Font Options*. :: #### Fonts installed from npm Some fonts are distributed as npm packages containing bare font files, with no stylesheet for the [`npm` provider](#npm) to read. If one of these is installed, it is scanned automatically and needs no configuration: ```ts export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ { name: 'Geist', provider: 'local' }, ], } }) ``` Filenames still drive weight, style and subset detection, so `Geist-Bold.woff2` resolves `font-family: Geist` at weight `700`. Matched fonts are emitted as build assets and served from your own origin. For a package that is not scanned automatically, add it with `packages`, or point at a directory directly with `dirs` (relative paths are resolved from your project root): ```ts export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { local: { packages: ['my-font-package'], dirs: ['assets/fonts'], }, } }) ``` ### `google` [Google Fonts](https://fonts.google.com/) is one of the best known public font APIs. ### `googleicons` [Google Fonts Icons](https://fonts.google.com/icons) adds support for Material Symbols and Material Icons. ### `bunny` [Bunny Fonts](https://fonts.bunny.net/) is provided by [bunny.net](https://bunny.net/) and is a drop-in Google Fonts compatible API, focusing on privacy. ### `fontshare` [Fontshare](https://www.fontshare.com/) is a free font service with 100+ professional-grade fonts from the Indian Type Foundry (ITF). ::warning{to="https://www.fontshare.com/licenses/itf-ffl"} You should read their terms in full before using a font through `fontshare`. :: ### `fontsource` [Fontsource](https://fontsource.org/docs/getting-started/introduction) is a collection of open-source fonts that are designed for self-hosting in web applications. ### `npm` The npm provider resolves fonts from npm packages installed in your project's `node_modules`. It automatically detects known font packages (such as [`@fontsource/*`](https://fontsource.org/), [`@fontsource-variable/*`](https://fontsource.org/), and [`cal-sans`](https://github.com/calcom/font)) from your `package.json` dependencies and devDependencies. By default the stylesheet shipped with the package is read from disk (`remote: false`) rather than from a CDN, and the font files it references are read from the installed package and served from your own origin. Only packages that ship a CSS entrypoint (such as `@fontsource/*` or `cal-sans`) can be used with this provider. Packages that ship bare font files are handled by the [`local` provider](#local) instead. ::note{to="https://nuxt.com/modules/fonts/configuration#provider"} Setting a top-level `provider` disables every other provider, including `npm`, so a per-family `provider: 'npm'` will not be used. :: ```ts export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { families: [ // Automatically resolved if @fontsource/roboto is in your package.json { name: 'Roboto', provider: 'npm' }, // Variable fonts are also supported { name: 'Inter Variable', provider: 'npm' }, ], } }) ``` ### `adobe` [Adobe Fonts](https://fonts.adobe.com/) is a font service for both personal and commercial use included with Creative Cloud subscriptions. To configure the Adobe provider in your Nuxt app, you must provide a Project ID or array of Project IDs corresponding to the Web Projects you have created in Adobe Fonts. ```ts export default defineNuxtConfig({ modules: ['@nuxt/fonts'], fonts: { adobe: { id: ['<some-id>', '<another-kit-id>'], }, } }) ``` ::warning We currently can't provide non-latin subsets for Adobe Fonts as they do not have a public API for that. Please use other providers in such cases. :: ::warning{to="https://www.adobe.com/legal/terms.html"} You should read their terms in full before using a font through `adobe`. :: ## Custom Providers The core of the provider API in Nuxt Fonts has been extracted to a common library that can be used by any framework - [`unifont`](https://github.com/unjs/unifont). The provider API is likely to evolve in the next few releases of `unifont`, but at the moment it looks like this: ```ts import { defineFontProvider } from 'unifont' export default defineFontProvider('some-custom-provider', async (options) => { // do some setup return { async resolveFont (fontFamily, options) { if (fontFamily === 'My Font Family') { return { fonts: [ { src: [ { url: 'https://cdn.org/my-font.woff2', format: 'woff2' }, ], weight: 400, style: 'normal', } ] } } } } }) ``` Module authors can also add their own providers (or remove existing ones) in the `fonts:providers` hook which is called by Nuxt Fonts after all modules have run. ```ts nuxt.hook('fonts:providers', providers => { delete providers.adobe providers['custom-provider'] = defineFontProvider('custom-provider', async () => { /** some setup */ return { async resolveFont (fontFamily, options) { /** resolve font faces */ } } }) }) ``` # Upgrade Guide > Breaking changes and migration steps when upgrading to the latest version of @nuxt/fonts. ## Upgrading to v1 v1 follows v0.14, as v0.15 was never released. If you are on v0.14, everything in this section applies to you. ### Breaking Changes #### The default `400 700` weight range is applied [`weights`](https://nuxt.com/modules/fonts/configuration#weights) has always been documented as defaulting to `['400 700']`, but the default was set on the wrong option and never reached the resolver, so families without an explicit `weights` were resolved at weight `400` alone. The documented default now takes effect. A family you did not set `weights` on will resolve a bold face as well as a regular one, so you will see additional `@font-face` rules and an additional file downloaded per family. Text that silently fell back to a synthesised bold will now render in the real one. Set `weights` explicitly to go back to a single weight: ```ts export default defineNuxtConfig({ fonts: { defaults: { weights: [400], }, }, }) ``` #### Fonts are served from `/_nuxt/fonts` in Vite builds Production builds with the Vite builder now emit font files as Vite build assets under `app.buildAssetsDir`, so they are served from `/_nuxt/fonts/<hash>.woff2` rather than `/_fonts/<hash>.woff2`. Vite owns their URLs, which means `app.cdnURL`, a relative `app.baseURL` and `experimental.renderBuiltUrl` now apply to fonts as they do to every other built asset. `app.cdnURL` in particular was previously never applied. The location of a font file is an implementation detail rather than a stable URL contract, so this may change again. If you have hard-coded `/_fonts/` anywhere (a CDN rule, a CSP directive, a cache header, a test), point it at your build assets directory instead. Fonts are still served from `/_fonts` in development and with the webpack and rspack builders; see [where fonts are served from](https://nuxt.com/modules/fonts/advanced#where-fonts-are-served-from). #### Fonts served from a CDN need CORS headers If you set `app.cdnURL`, your fonts are now fetched from that CDN rather than from your own origin. Browsers always fetch fonts in CORS mode, so the CDN must respond with an `Access-Control-Allow-Origin` header that covers your site. Without it, fonts that previously loaded fail silently and the browser falls back to the next family in the stack. Preload links for fonts carry the `crossorigin` attribute so the preloaded response is reused rather than fetched twice. #### `assets.prefix` is relative to `buildAssetsDir` in Vite builds [`assets.prefix`](https://nuxt.com/modules/fonts/configuration#assets) now names a directory inside `app.buildAssetsDir` in Vite production builds, and defaults to `fonts`. Setting `prefix: '/my-fonts'` serves fonts from `/_nuxt/my-fonts` rather than from `/my-fonts`. It remains a public path in development and with the webpack and rspack builders. #### `global: true` families get font fallback metrics A family declared with [`global: true`](https://nuxt.com/modules/fonts/configuration#global) previously got no fallback metrics at all, which made the fonts most likely to cause layout shift the ones Nuxt Fonts did least about. Fallbacks are now generated wherever Nuxt Fonts can see the family being used, so `font-family: 'Anton'` in your CSS becomes `font-family: 'Anton', 'Anton Fallback: Arial', ...` with the matching metric-override rules alongside it, exactly as it would for a family that wasn't declared globally. Usage Nuxt Fonts can't scan (an SVG, or a family chosen at runtime) is unaffected and continues to rely on the global stylesheet alone. The `@font-face` rule for the family itself still comes from the global stylesheet and isn't duplicated, and preload hints are unchanged. If you don't want the fallback rules, set [`fallbacks`](https://nuxt.com/modules/fonts/configuration#fallbacks) to `[]` for the family. #### Font metadata is cached per project Font metadata and downloaded font files are now cached in `node_modules/.cache/nuxt/fonts/meta` relative to your project root rather than to the directory you run Nuxt from. The first build after upgrading will re-resolve and re-download fonts, and you can now configure the location with the new [`cache`](https://nuxt.com/modules/fonts/configuration#cache) option. #### Injected `@font-face` rules are minified with lightningcss Generated `@font-face` declarations were previously minified with esbuild unless you had opted into `css.lightningcss`. They are now minified with lightningcss, so the exact serialisation of the CSS we inject may differ (for example `local(Font Name)` rather than `local("Font Name")`). This is cosmetic, but it will show up in snapshot tests. lightningcss is an optional peer dependency, as its native binary is several megabytes. Nuxt v4.5 (with Vite 8) brings this in, but if you are using an earlier version of Nuxt, you will need to install it manually to enable css minification. ```bash [Terminal] npm install --save-dev lightningcss ``` #### Font failures fail production builds [`throwOnError`](https://nuxt.com/modules/fonts/configuration#throwonerror) now defaults to `true` outside of dev mode, so rather than producing a build with a missing font, the build fails when no provider can serve a family, when a font file cannot be downloaded after retries, or when you set `provider` on a family and that provider does not contain it. A single provider failing is not enough on its own, as resolution carries on to the next one. A family that cannot be found by any provider still warns rather than failing, as does an unknown provider name, so fonts you declare yourself in CSS are unaffected. Set `throwOnError: false` to restore the previous behaviour. #### Family-level `@font-face` descriptors now apply to provider fonts `display` and `unicodeRange` set on a family were previously only honoured for families you declared manually with `src`, and were silently dropped for fonts resolved from a provider. They now apply in both cases. Descriptors other than `display`, `weight` and `style` (such as `stretch`, `featureSettings` and `variationSettings`) are also no longer dropped from manually declared families. If you set any of these options on a family and (perhaps unknowingly) relied on them being ignored, your generated CSS will change. Note that setting `unicodeRange` on a family marks it as subsetted, so it is no longer preloaded by default; set [`preload`](https://nuxt.com/modules/fonts/configuration#preload) explicitly if you still want a preload link for it. #### The `local` provider only scans layer `public/` directories The [`local` provider](https://nuxt.com/modules/fonts/providers#local) previously scanned every directory in `nitro.publicAssets`, including those that modules register for their own files. It now scans only the `public/` directory of your project and of each layer. If you relied on fonts in another `nitro.publicAssets` directory, add it to [`local.dirs`](https://nuxt.com/modules/fonts/providers#fonts-installed-from-npm): ```ts export default defineNuxtConfig({ fonts: { local: { dirs: ['assets/fonts'], }, }, }) ``` ### Deprecations #### `experimental.processCSSVariables` `fonts.experimental.processCSSVariables` has moved to `fonts.processCSSVariables`. The old location still works and now warns. ```ts export default defineNuxtConfig({ fonts: { processCSSVariables: true, }, }) ``` #### `fonts:public-asset-context` Use [`fonts:resolved`](https://nuxt.com/modules/fonts/advanced#fontsresolved) instead. It reports each family as it is resolved, with the URL every font file is served from and a `readFont()` to read its contents, so there is no need to reconstruct paths from the asset context. The old hook still works. ### New Features #### `@font-face` rules are injected with the webpack and rspack builders Font injection now works with `@nuxt/webpack-builder` and `@nuxt/rspack-builder`. Previously our CSS transform ran after `css-loader` had already turned your stylesheets into JavaScript modules, so no `@font-face` rule was ever injected, and a family declared with `global: true` failed the client build. There's nothing to change on your side, but a build that silently shipped no web fonts now downloads them and serves them from `/_fonts`. See [builder support](https://nuxt.com/modules/fonts/advanced#builder-support). #### Subsetting fonts to the glyphs you use [`glyphs`](https://nuxt.com/modules/fonts/configuration#glyphs) reduces every font file we emit to the characters needed to render the text you give it, which can dramatically cut the bytes shipped for icon fonts and single-language sites. ```ts export default defineNuxtConfig({ fonts: { defaults: { glyphs: 'Handgloves & 0123', }, }, }) ``` Where a provider can subset server-side the characters are passed through to it, so the full file is never downloaded. Every other file is subsetted after download, which needs the [`subset-font`](https://github.com/papandreou/subset-font) package; we will offer to install it the first time you run Nuxt with `glyphs` set, or warn where we cannot ask, such as in CI, and fail the build before downloading any font that needs subsetting locally. #### Variable font axes [`variableAxis`](https://nuxt.com/modules/fonts/configuration#variableaxis) chooses the values a variable font is shipped at, for any OpenType axis rather than just `wght`. Pin an axis to a single value, or narrow it to a range. ```ts export default defineNuxtConfig({ fonts: { defaults: { variableAxis: { CASL: [1], MONO: [{ min: 0, max: 1 }], }, }, }, }) ``` #### Named font weights [`weights`](https://nuxt.com/modules/fonts/configuration#weights) accepts CSS keywords as well as numbers, and the [`local` provider](https://nuxt.com/modules/fonts/providers#local) recognises them in filenames, so `MyFont-Bold.woff2` is picked up as weight `700`. Remote providers still expect numeric weights. ```ts export default defineNuxtConfig({ fonts: { defaults: { weights: ['medium', 'semibold'], }, }, }) ``` #### Scanning additional local font directories The [`local` provider](https://nuxt.com/modules/fonts/providers#local) can scan directories beyond `public/`, and reports the filenames it looked for when a family cannot be resolved. ```ts export default defineNuxtConfig({ fonts: { local: { dirs: ['assets/fonts'], }, }, }) ``` #### Preloading fonts by subset The `preload` option (on `defaults` and on individual `families`) now accepts `{ subsets: [...] }` or a filter function, so you can preload just the subsets your app needs. ```ts export default defineNuxtConfig({ fonts: { defaults: { preload: { subsets: ['latin'] }, }, }, }) ``` See [`preload`](https://nuxt.com/modules/fonts/configuration#preload) for the full set of values. #### Configurable cache You can now point the font cache at a directory of your choice, pass your own [unstorage](https://unstorage.unjs.io/) instance, or disable persistent caching with `cache: false`. ```ts export default defineNuxtConfig({ fonts: { cache: '.cache/fonts', }, }) ``` #### Custom CSS variable prefixes [`processCSSVariables`](https://nuxt.com/modules/fonts/configuration#processcssvariables) now accepts a custom prefix, so `processCSSVariables: 'my-app'` will process `--my-app-*` variables only. #### Reusing resolved fonts from another module The new [`fonts:resolved`](https://nuxt.com/modules/fonts/advanced#fontsresolved) hook reports each family as it is resolved, with the URL every font file is served from and a `readFont()` to read its contents. Modules that need the fonts your app already uses, to render an OG image or a PDF, can reuse them rather than resolving and downloading their own. ```ts nuxt.hook('fonts:resolved', (font) => { for (const file of font.files) { // file.url, await file.readFont() } }) ``` ## Upgrading to v0.14 ### Breaking Changes #### Default font format is now `woff2` only Previously, font providers could return multiple formats (e.g., `woff2`, `woff`, `truetype`). The default behavior now only resolves `woff2` format fonts, which is universally supported in all modern browsers. This means your rendered `@font-face` declarations will typically have fewer `src` entries, reducing overall CSS size. The Google Fonts provider previously also downloaded a legacy-format copy of each font that contained every script the font supports, regardless of the configured [`subsets`](https://nuxt.com/modules/fonts/configuration#subsets). Text in a script outside those subsets, such as Arabic, now renders in a fallback font. To keep it in your font, add its subset: ```ts export default defineNuxtConfig({ fonts: { families: [ { name: 'Cairo', subsets: ['arabic', 'latin', 'latin-ext'] }, ], }, }) ``` If you need to support legacy browsers that require other formats, you can configure this in your `nuxt.config.ts`: ```ts export default defineNuxtConfig({ fonts: { defaults: { formats: ['woff2', 'woff', 'ttf'], }, }, }) ``` The available format values are: `'woff2'`, `'woff'`, `'ttf'`, `'otf'`, `'eot'`. ### New Features #### Font format resolution You can now control which font formats are resolved via the new `defaults.formats` option. This defaults to `['woff2']`. ```ts export default defineNuxtConfig({ fonts: { defaults: { formats: ['woff2'], }, }, }) ``` #### Provider-specific font family options You can now pass provider-specific options when configuring individual font families using the new `providerOptions` property: ```ts export default defineNuxtConfig({ fonts: { families: [ { name: 'My Font', provider: 'google', providerOptions: { google: { experimental: { variableAxis: { wdth: [['75', '100']], }, }, }, }, }, ], }, }) ``` #### `throwOnError` option You can now configure whether font resolution errors should throw or just warn: ```ts export default defineNuxtConfig({ fonts: { throwOnError: true, // default: false }, }) ``` # Advanced > Find out how Nuxt Fonts works behind the scenes to optimize fonts in your project. ## How it works Nuxt Fonts processes all your CSS and does the following things automatically when it encounters a `font-family` declaration. 1. **Resolves fonts used in CSS**. It starts by looking in your `public/` directory for font files that match the name, like `Roboto.woff2`, `RobotoBold.ttf`, etc. Then it moves on to web font providers like `google`, `bunny` and `fontshare`, which we query through [unifont](https://unifont.dev). Once a provider is found (in this case, probably [Google Fonts](https://fonts.google.com)), we move on to the next step. 2. **Generates and injects `@font-face` rules for you.** We'll generate rules to point your browser to the right source files. They'll be injected into the same CSS where you use the `font-family`. ```css /* If you write something like this: */ :root { font-family: Poppins; } ``` ```css /* Then Nuxt fonts will add declarations that look like this at the beginning of the CSS file: */ @font-face { font-family: 'Poppins'; src: local("Poppins"), url("/_nuxt/fonts/<hash>.woff2") format(woff2); font-display: swap; unicode-range: U+0000-00FF,U+0131, /* ... */; font-weight: 400; font-style: normal; } /* ... plus more font-face declarations for other unicode ranges/weights */ ``` 3. **Proxies and caches font requests.** Rather than using the original source URLs (to remote servers), we generate rewrites under our own asset directory. When accessed by your browser, we download the font from the remote server and cache it locally. 4. **Creates font fallback metrics.** If we have access to the font metrics (ascent, descent, line gap, character width, etc.) then we can generate a fallback `@font-face` declaration as well, using [fontaine](https://github.com/unjs/fontaine). The idea is that we 'morph' a local system font (like Arial or Times New Roman) to be as close as possible to the size of the web font, to decrease layout shift ([read more about CLS](https://web.dev/articles/cls)). ```css :root { /* This will generate fallbacks for local versions of Helvetica and Arial, adjusted to match Roboto's metrics. */ font-family: Roboto, Helvetica, Arial; /* If you provide a generic family (like serif or sans-serif), we will use a system font from that family. */ font-family: Merriweather, serif; } ``` 5. **Include fonts in build.** When you build your project, we'll copy across all the fonts used in your project so you don't need to make any external requests when loading your site. (Any that haven't already been cached in development are downloaded at build time.) Font file names are hashed and Nuxt will serve them with long-lived cache headers. ## What Nuxt Fonts is built on Most of the pipeline above lives in framework-agnostic packages that you can use without Nuxt: - [unifont](https://unifont.dev) resolves font metadata from a provider. It ships the `google`, `googleicons`, `adobe`, `bunny`, `fontshare`, `fontsource` and `npm` providers, and you can write your own against the same interface. Source at [unjs/unifont](https://github.com/unjs/unifont). - [fontaine](https://github.com/unjs/fontaine) generates the adjusted fallback `@font-face` rules that cut layout shift while a web font loads. - [fontless](https://github.com/unjs/fontaine) is the same resolve-and-inject pipeline as a Vite plugin, for projects that aren't built on Nuxt. ## Builder support Nuxt Fonts works with the Vite, webpack and rspack builders. In each case we inject `@font-face` rules into your stylesheets and download the fonts at build time; where they are then served from differs, and is covered in [Where fonts are served from](#where-fonts-are-served-from). With webpack and rspack, our CSS transform runs immediately before `css-loader`, so it sees your styles after any preprocessor (such as Sass) and PostCSS have run. A family named through a preprocessor variable is still detected, but a `font-family` value produced by a later loader in the chain is not. ## Where fonts are served from The location of a font file is an implementation detail rather than a stable URL contract. Don't hard-code these paths; link to fonts through the CSS we generate, or read them from the [`fonts:resolved`](#fontsresolved) hook. | Build | Path | `app.baseURL` | `app.cdnURL` | Relative `app.baseURL` | | --------------------- | --------------------------------------------------------------- | ------------- | -------------- | ---------------------- | | Vite production build | `<buildAssetsDir>/fonts/<hash>.woff2`, that is `/_nuxt/fonts/…` | Applied | Applied | Applied | | Development | `/_fonts/<hash>.woff2` | Applied | Not applicable | Not applicable | | Webpack, rspack | `/_fonts/<hash>.woff2` | Applied | Not applied | Not applied | In a Vite production build the fonts are handed to Vite's asset pipeline, so Vite rewrites their URLs exactly as it does for any other asset it emits: `app.baseURL`, `app.cdnURL`, a relative base and `experimental.renderBuiltUrl` all apply. Bundled stylesheets get URLs relative to the stylesheet (`url(./fonts/…)`), because that is what `@nuxt/vite-builder` asks Vite to do for non-JS assets; server-inlined `<style>` blocks get an absolute URL resolved at runtime. Webpack and rspack have no equivalent asset pipeline for the CSS we generate, so fonts stay on a public path with long-lived cache headers, and only `app.baseURL` is applied to them. ## Caching fonts between builds Font metadata and downloaded font files are cached on disk in `node_modules/.cache/nuxt/fonts/meta`, relative to your project root. If you restore that directory in CI, no fonts are downloaded and no provider requests are made at all. ```yaml [.github/workflows/build.yml] - uses: actions/cache@v4 with: path: node_modules/.cache/nuxt/fonts key: nuxt-fonts-${{ hashFiles('nuxt.config.ts') }} restore-keys: nuxt-fonts- ``` A few things to check if the cache doesn't seem to take effect: - The cache step must run **after** you install dependencies. Some package managers remove unknown directories inside `node_modules`, which will delete a cache restored before install. - `actions/setup-node` with `cache: 'pnpm'` (or `npm`/`yarn`) caches the package manager store, not `node_modules`, so it does not cover fonts. - With an exact `key` and no `restore-keys`, a config change means a cache miss and a full re-download. - In Docker builds, `node_modules/.cache` lives inside the build container. You need a cache mount or a persistent volume for it to survive: ```dockerfile [Dockerfile] RUN --mount=type=cache,target=/app/node_modules/.cache npm run build ``` If a path inside `node_modules` is awkward to cache, move it with the [`cache`](https://nuxt.com/modules/fonts/configuration#cache) option. ## Module Hooks Nuxt Fonts exposes hooks for module authors to extend functionality. ### `fonts:providers` Add or modify font providers after all modules have loaded. ```ts nuxt.hook('fonts:providers', (providers) => { providers['my-provider'] = defineFontProvider(/* ... */) }) ``` ### `fonts:public-asset-context` ::warning Deprecated. Use [`fonts:resolved`](#fontsresolved) instead. :: Access the font resolution context. ```ts nuxt.hook('fonts:public-asset-context', (context) => { // context.renderedFontURLs: Map<filename, sourceUrl> // Populated during vite build }) ``` ### `fonts:resolved` Called when a font family is resolved, with the `@font-face` data generated for it. This covers families found in your CSS and families declared with `global: true` in `fonts.families`, so modules that render fonts outside the browser (e.g., OG image generation) can reuse them instead of resolving them again. ```ts const files = new Map<string, () => Promise<Buffer>>() nuxt.hook('fonts:resolved', (font) => { // font.fontFamily: 'Poppins' // font.fonts: FontFaceData[], with each `src` URL as served (with `app.baseURL`) for (const file of font.files) { // file.url: '/_nuxt/fonts/<hash>.woff2', matching a `src` URL in `font.fonts` files.set(file.url, file.readFont) } }) nuxt.hook('nitro:build:before', async () => { for (const readFont of files.values()) { const contents = await readFont() // the file as served, subsetted if you set `glyphs` } }) ``` Listeners run while your stylesheets are transformed, so keep them fast: collect what you need and call `readFont()` later, as above. In a production build, every family is resolved before Nitro builds. In development, families found in your CSS are resolved as the stylesheets that use them are requested. # Contributing > We can never thank you enough for your contributions. ::warning It is highly recommended to read [the Nuxt contribution guide](https://nuxt.com/docs/community/contribution) before making any contributions. :: ## How to contribute? - Clone [nuxt/fonts](https://github.com/nuxt/fonts) repository - Enable Corepack using `corepack enable` - Install dependencies using pnpm install - Run `pnpm dev:prepare` to generate type stubs. - Use `pnpm dev` to start the playground in development mode. - Apply your changes - Add tests into the `test/` directory and run pnpm test to make sure they pass. - Check the code style with `pnpm lint` - Before creating a PR, make sure to run `pnpm build` and that no errors are reported. ### Documentation The documentation lives in the [`docs/`](https://github.com/nuxt/fonts/tree/main/docs) directory and is published on [nuxt.com/modules/fonts](https://nuxt.com/modules/fonts). Keep images and videos next to the pages in `docs/images/` and link them with a relative path, such as `![Fonts tab](./images/fonts-tab.webp)` or `::video{src="./images/demo.mp4" controls}`. To preview your changes, run the [nuxt.com](https://github.com/nuxt/nuxt.com) dev server with `NUXT_FONTS_PATH` set to your local clone of this repo. Pull requests changing `docs/` can also be previewed on [nuxt.com](http://nuxt.com) once a maintainer adds the `preview:enabled` label. # Nuxt Hints > Real-time feedback on your Nuxt app performance, accessibility, and security, right in your browser. ## Introduction Nuxt Hints integrates directly into the [Nuxt DevTools](https://nuxt.com/modules/devtools), giving you actionable insights to improve your web vitals, fix hydration mismatches, and audit third-party scripts without ever leaving your development environment. ![Nuxt Hints homepage in the Nuxt DevTools](https://nuxt.com/modules/hints/images/devtools-homepage.png) - **Rich DevTools UI**: a dedicated tab in Nuxt DevTools to visualize issues, inspect elements, and get recommendations. - **[Web Vitals](https://nuxt.com/modules/hints/web-vitals)**: real-time metrics for LCP, INP, and CLS with detailed attribution and element-specific optimization tips. - **[Hydration mismatch debugging](https://nuxt.com/modules/hints/hydration)**: side-by-side diffing of server-rendered and client-hydrated HTML to pinpoint the exact cause of mismatches. - **[Third-party script auditing](https://nuxt.com/modules/hints/third-party-scripts)**: a dashboard to monitor performance, identify render-blocking scripts, and get security recommendations. - **[Unused component detection](https://nuxt.com/modules/hints/lazy-load)**: detects statically imported components that aren't rendered during SSR or hydration and suggests lazy-loading them to reduce bundle size. - **[HTML validation](https://nuxt.com/modules/hints/html-validate)**: every server-rendered page is checked with [`html-validate`](https://html-validate.org/). ## Automatic Installation To get started, add `@nuxt/hints` to your project: ```bash npx nuxt add hints ``` The module is now installed and added to your `nuxt.config.ts`. ## Manual Installation Or you can install it manually. ::code-group ```bash [pnpm] pnpm i @nuxt/hints ``` ```bash [yarn] yarn add @nuxt/hints ``` ```bash [npm] npm i @nuxt/hints ``` :: Then, add it to the `modules` in your `nuxt.config`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/hints', ], }) ``` ::note Nuxt Hints only runs during development (`nuxt dev`). It adds nothing to your production build. :: ## Usage Start your Nuxt app, open the Nuxt DevTools and click the **Hints** icon to get started. The homepage is a central hub to see a summary of all detected issues at a glance. From there, every feature has its own panel: - **Hover to highlight**: hover over an issue in the DevTools to highlight the corresponding element on your page. - **Click to inspect**: click to open the component source file directly in your code editor. ## Console Warnings Nuxt Hints also prints clear, concise messages in your browser console that guide you to best practices and performance improvements: ``` [@nuxt/hints:performance] LCP Element should not have `loading="lazy"` Learn more: https://web.dev/optimize-lcp/#optimize-the-priority-the-resource-is-given ``` ``` [@nuxt/hints] Third-party script "https://cdn.example.com/script.js" is missing crossorigin attribute. Consider adding crossorigin="anonymous" for better security and error reporting. ``` ::tip{to="https://nuxt.com/modules/hints/configuration"} If you feel overwhelmed by logs, you can disable some features or only their console output, and fix things step by step. :: # Configuration > Configure Nuxt Hints and enable or disable each feature. Configure the module in your `nuxt.config.ts` under the `hints` key: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: ['@nuxt/hints'], hints: { devtools: true, // Enable or configure individual features // if you feel overwhelmed by logs, you can disable some features and fix things step by step. features: { // Defaults to true for each feature hydration: true, }, }, }) ``` ## `devtools` - Type: `boolean` - Default: `true` Adds the **Hints** tab to the Nuxt DevTools. ## `features` Each feature is enabled by default and accepts either a **boolean** or an **object** for finer control: | Key | Description | | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | [`hydration`](https://nuxt.com/modules/hints/hydration) | Detect and inspect hydration mismatches | | [`lazyLoad`](https://nuxt.com/modules/hints/lazy-load) | Flag unused imported components that could be lazy-loaded | | [`webVitals`](https://nuxt.com/modules/hints/web-vitals) | Track LCP, INP, and CLS metrics | | [`thirdPartyScripts`](https://nuxt.com/modules/hints/third-party-scripts) | Audit third-party scripts for performance and security | | [`htmlValidate`](https://nuxt.com/modules/hints/html-validate) | Validate server-rendered HTML with [html-validate](https://html-validate.org/) | When using the object syntax, the following flags are available: ```ts { logs: true, // Print warnings to the browser console devtools: true, // Show this feature in the DevTools panel options: { ... }, // Feature-specific options } ``` `logs` and `devtools` default to `true` when omitted. For example, to keep the hydration panel in the DevTools without the console warnings: ```ts [nuxt.config.ts] export default defineNuxtConfig({ hints: { features: { hydration: { logs: false }, }, }, }) ``` The `options` accepted by each feature are described on its own page: [Web Vitals](https://nuxt.com/modules/hints/web-vitals#options), [Third-Party Scripts](https://nuxt.com/modules/hints/third-party-scripts#options) and [HTML Validation](https://nuxt.com/modules/hints/html-validate#options). # Web Vitals > Drill down into Core Web Vitals metrics with detailed attribution for LCP, INP, and CLS. Drill down into Core Web Vitals metrics. See detailed attribution for LCP, INP, and CLS, inspect the problematic elements, and get context-aware advice. ![Web Vitals panel in the Nuxt DevTools](https://nuxt.com/modules/hints/images/devtools-webvitals.png) ## How it works Nuxt Hints uses [`web-vitals`](https://github.com/GoogleChrome/web-vitals) to gather Core Web Vitals metrics and automatically logs any metrics that need improvement. It listens for INP, LCP, and CLS and provides detailed attribution for each. When the LCP element is an image, Nuxt Hints also checks it and warns when it is lazy-loaded or not served in a modern format like `webp` or `avif`. ## Options ```ts [nuxt.config.ts] export default defineNuxtConfig({ hints: { features: { webVitals: { options: { trackedMetrics: ['LCP', 'CLS'], ignoreDomains: ['images.example.com'], }, }, }, }, }) ``` ### `trackedMetrics` - Type: `Array<'LCP' | 'INP' | 'CLS'>` - Default: `['LCP', 'INP', 'CLS']` The metrics to track. ### `ignoreDomains` - Type: `string[]` - Default: `[]` Skip the LCP image checks for images served from these domains and their subdomains. # Hydration > Debug hydration mismatches with a side-by-side diff of the server-rendered and client-side HTML. Debug hydration mismatches with a powerful side-by-side diff viewer. See the exact differences between the server-rendered HTML and the client-side result. ![Hydration panel in the Nuxt DevTools](https://nuxt.com/modules/hints/images/devtools-hydration.png) ## How it works The module hooks into Vue's hydration process to compare the server-rendered DOM with the client-side DOM. When a mismatch is detected, it captures the pre- and post-hydration HTML for inspection. # Third-Party Scripts > Audit the loading time, render-blocking status, and security attributes of every third-party script. Analyze all third-party scripts on your page. The dashboard shows loading times, render-blocking status, and security attributes, helping you identify and mitigate performance bottlenecks. ![Third-party scripts panel in the Nuxt DevTools](https://nuxt.com/modules/hints/images/devtools-thirdparties.png) ## How it works Using a combination of a Nitro plugin and client-side observers, Nuxt Hints tracks every script loaded on the page, measuring its performance and analyzing its attributes. Scripts served from your own origin and scripts injected by browser extensions are ignored. ## Options ```ts [nuxt.config.ts] export default defineNuxtConfig({ hints: { features: { thirdPartyScripts: { options: { ignoredDomains: ['cdn.example.com'], }, }, }, }, }) ``` ### `ignoredDomains` - Type: `string[]` - Default: `[]` Additional domains, and their subdomains, to ignore when detecting third-party scripts. ### `ignoredSchemes` - Type: `string[]` - Default: `[]` Additional URL schemes to ignore. Browser extension schemes (`chrome-extension`, `moz-extension`, `safari-extension` and `ms-browser-extension`) are always ignored. # Lazy Load > Find statically imported components that never render during SSR or hydration, and lazy-load them. Identify statically imported components that were never rendered during SSR or initial hydration. The module suggests converting them to lazy-loaded components to reduce your users' initial bundle size. ![Lazy load panel in the Nuxt DevTools](https://nuxt.com/modules/hints/images/devtools-lazy-load.png) ## How it works At build time, a Vite plugin analyzes your component imports to identify statically imported `.vue` components. At runtime, the module tracks which of those components actually render during SSR and initial hydration. After the page finishes loading, any imported component that was never rendered is flagged as a candidate for lazy loading. Suggestions are reported to the console and sent to the DevTools UI via Server-Sent Events, where you can review them per route and dismiss entries you've already addressed. ## Fixing a suggestion Either prefix the component with `Lazy`, or use `defineAsyncComponent`, so it is only downloaded when needed: ```vue <template> <LazyHeavyComponent v-if="show" /> </template> ``` ::note{to="https://nuxt.com/docs/4.x/directory-structure/app/components#dynamic-imports"} Read more about lazy-loading components in the Nuxt documentation. :: # HTML Validation > Validate every server-rendered page with html-validate. Nuxt Hints includes a built-in HTML validation feature powered by [`html-validate`](https://html-validate.org/). On every server-rendered response, the module intercepts the HTML output and runs it through `html-validate`. ![HTML validation panel in the Nuxt DevTools](https://nuxt.com/modules/hints/images/devtools-html-validate.png) ## Options The `options` are an [`html-validate` configuration](https://html-validate.org/usage/index.html#configuration), merged with the defaults of Nuxt Hints. By default, the `html-validate:standard`, `html-validate:document` and `html-validate:browser` presets are enabled, with a few rules turned off because they conflict with Nuxt defaults. ```ts [nuxt.config.ts] export default defineNuxtConfig({ hints: { features: { htmlValidate: { options: { rules: { 'no-redundant-role': 'off', }, }, }, }, }, }) ``` # Contributing > We can never thank you enough for your contributions. ::warning It is highly recommended to read [the Nuxt contribution guide](https://nuxt.com/docs/community/contribution) before making any contributions. :: ## Development - Clone the [nuxt/hints](https://github.com/nuxt/hints) repository - Enable Corepack using `corepack enable` - Apply your changes, then check them with the commands below ```bash # Install dependencies pnpm install # Generate type stubs pnpm run dev:prepare # Develop with the playground pnpm run dev # Build the playground pnpm run dev:build # Run ESLint pnpm run lint # Run Vitest pnpm run test pnpm run test:watch ``` ## Documentation The documentation lives in the [`docs/`](https://github.com/nuxt/hints/tree/main/docs) directory and is published on [nuxt.com/modules/hints](https://nuxt.com/modules/hints). Keep images next to the pages in `docs/images/` and link them with a relative path, such as `![Hints tab](./images/devtools-homepage.png)`. To preview your changes, run the [nuxt.com](https://github.com/nuxt/nuxt.com) dev server with `NUXT_HINTS_PATH` set to your local clone of this repo. Pull requests changing `docs/` can also be previewed on [nuxt.com](http://nuxt.com) once a maintainer adds the `preview:enabled` label. # Nuxt ESLint > All-in-one ESLint integration for Nuxt, generating a project-aware ESLint flat config. ## Introduction All-in-one ESLint integration for Nuxt. It generates a project-aware [ESLint flat config](https://eslint.org/docs/latest/use/configure/configuration-files) and provides the ability to optionally run ESLint check alongside the dev server. ::tip This module is designed for the [new ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files) which is the [default format since ESLint v9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/). It supports ESLint v9 and v10. We recommend you to use the latest version of ESLint to get the best experience. :: ::warning The legacy `.eslintrc` config is **not supported** by this module. We highly recommend you to migrate over the flat config to be future-proof. :: ::note The source code of the module is in [`packages/module`](https://github.com/nuxt/eslint/tree/main/packages/module) on GitHub. :: ## Features - [ESLint flat config](https://eslint.org/docs/latest/use/configure/configuration-files), composable, customizable and future-proof. - Project-aware Nuxt-specific settings, also supports [layers](https://nuxt.com/docs/getting-started/layers). - [Nuxt DevTools](https://nuxt.com/modules/devtools) integration powered by [`@eslint/config-inspector`](https://github.com/eslint/config-inspector). - [Extensible with other modules](https://nuxt.com/modules/eslint/recipes#integrate-with-other-modules). - Optional [dev server checker](https://nuxt.com/modules/eslint/recipes#dev-server-checker) integration. ## Quick Setup Run the following command to add the `@nuxt/eslint` module to your project: ```bash [Terminal] npx nuxt add eslint ``` Once you start your Nuxt app, a `eslint.config.mjs` file will be generated under your project root. You can customize it as needed. ::note If you are using TypeScript, you need to install the `typescript` in your project: :::code-group ```bash [yarn] yarn add --dev typescript ``` ```bash [npm] npm install -D typescript ``` ```bash [pnpm] pnpm add -D typescript ``` ```bash [bun] bun add -D typescript ``` ::: :: ### Manual Setup ::code-group ```bash [yarn] yarn add --dev @nuxt/eslint eslint typescript ``` ```bash [npm] npm install -D @nuxt/eslint eslint typescript ``` ```bash [pnpm] pnpm add -D @nuxt/eslint eslint typescript ``` ```bash [bun] bun add -D @nuxt/eslint eslint typescript ``` :: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { // options here } }) ``` And create an `eslint.config.mjs` file under your project root, with the following content: ```js [eslint.config.mjs] // @ts-check import withNuxt from './.nuxt/eslint.config.mjs' export default withNuxt( // your custom flat configs go here, for example: // { // files: ['**/*.ts', '**/*.tsx'], // rules: { // 'no-console': 'off' // allow console.log in TypeScript files // } // }, // { // ... // } ) ``` `withNuxt` will take the rest arguments of flat configs and append them after Nuxt flat config items. You can either use the [Nuxt DevTools](https://nuxt.com/modules/devtools) panel to inspect the resolved ESLint flat config, or manually run [`npx @eslint/config-inspector`](https://github.com/eslint/config-inspector). # Recipes > Editor setup, formatting, config customizations, the dev server checker and other recipes for the ESLint module. ## VS Code ESLint v9.x support was added in the [ESLint VS Code extension (`vscode-eslint`)](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) v3.0.10. In versions of `vscode-eslint` prior to v3.0.10, the new configuration system is not enabled by default. To enable support for the new configuration files, edit your `.vscode/settings.json` file and add the following: ```json [.vscode/settings.json] { // Required in vscode-eslint < v3.0.10 only "eslint.useFlatConfig": true } ``` ## NPM Scripts Add the below to lint commands to your `package.json` script section: ```json { "scripts": { ... "lint": "eslint .", "lint:fix": "eslint . --fix", ... }, } ``` Run the `npm run lint` command to check if the code style is correct or run `npm run lint:fix` to automatically fix issues. ::warning Your `eslint.config.mjs` imports the config generated in `.nuxt/`, which only exists after Nuxt has run once. In CI or on a fresh clone, run `nuxt prepare` before ESLint, for example `"lint": "nuxt prepare && eslint ."`. :: ## Prettier This module does not enable stylistic/formatting rules by default. You can use Prettier alongside directly. ## ESLint Stylistic If you prefer to use ESLint for formatting, we also directly integrate with [ESLint Stylistic](https://eslint.style/) to make it easy. You can opt in by setting `config.stylistic` to `true` in the `eslint` module options. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { stylistic: true // <--- } } }) ``` You can also pass an object to customize the rules: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { stylistic: { indent: 'tab', semi: true, // ... } } } }) ``` Learn more about all the available options in the [ESLint Stylistic documentation](https://eslint.style/guide/config-presets#configuration-factory). When stylistic rules are enabled, the keys of your `nuxt.config` are also sorted in a recommended order by the [`nuxt/nuxt-config-keys-order`](https://nuxt.com/modules/eslint/plugin#nuxtnuxt-config-keys-order) rule. Set `config.nuxt.sortConfigKeys` to `false` to disable it, or to `true` to enable it without stylistic rules. ## Formatters ESLint only lints JavaScript, TypeScript and Vue files. To also format CSS, HTML, GraphQL and other files with ESLint, enable `config.formatters`. It uses [`eslint-plugin-format`](https://github.com/antfu/eslint-plugin-format) with Prettier under the hood, which you need to install: ```bash npm i -D eslint-plugin-format ``` ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { formatters: true // <--- } } }) ``` `true` formats CSS, Less, SCSS, HTML and GraphQL files, plus XML and SVG when `@prettier/plugin-xml` is installed. Markdown is not formatted by default, as many Nuxt projects use MDC syntax with Nuxt Content. Pass an object to pick the file types: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { formatters: { css: true, html: true, markdown: 'prettier', // or 'dprint' } } } }) ``` ## Type-Aware Rules TypeScript support is enabled automatically when `typescript` is installed in your project. To enable [type-aware rules](https://typescript-eslint.io/getting-started/typed-linting/), set `config.typescript.tsconfigPath` to your `tsconfig.json`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { typescript: { tsconfigPath: './tsconfig.json' } } } }) ``` Type-aware rules are slower, as ESLint needs to run the TypeScript compiler. The TypeScript rules use the strict preset by default; set `config.typescript.strict` to `false` to use the recommended one. ## Config Customizations `withNuxt()` returns a chainable [`FlatConfigComposer` instance](https://github.com/antfu/eslint-flat-config-utils#composer) from [`eslint-flat-config-utils`](https://github.com/antfu/eslint-flat-config-utils) which allows you to manipulate the ESLint flat config with ease. ```js [eslint.config.mjs] // @ts-check import withNuxt from './.nuxt/eslint.config.mjs' export default withNuxt( // ...Custom flat configs append after nuxt's configs ) .prepend( // ...Prepend some flat configs in front ) // Override some rules in a specific config, based on their name .override('nuxt/typescript/rules', { rules: { // ...Override rules, for example: '@typescript-eslint/no-explicit-any': 'off' } }) // ...you can chain more operations as needed ``` You can learn more about the available methods with the types and JSDocs of the instance. To find the names of all the config items, use the [Config Inspector](#config-inspector). ## Config Inspector This module ships [ESLint Config Inspector](https://github.com/eslint/config-inspector) in your [Nuxt DevTools](https://nuxt.com/modules/devtools). You can inspect the resolved ESLint flat config there: ![ESLint Config Inspector in Nuxt DevTools](https://nuxt.com/modules/eslint/images/devtools-inspector.png) By default, the inspector starts when you click **Launch** in its DevTools tab. Set `config.devtools.enabled` to `true` to start it with the dev server, or to `false` to disable the integration. You can also set the port it runs on with `config.devtools.port`. ## Dev Server Checker Usually you don't need this setting as most IDEs are capable of running ESLint directly to inform you of issues. It's also possible to set up a [pre-commit hook with `lint-staged`](https://github.com/lint-staged/lint-staged) to guard your codebase before committing. That said, if you are working on a team using a variety of IDEs you may still want to run the ESLint checker alongside the dev server (which might slow down the dev server) to ensure issues are raised no matter the environment. You can enable it by setting `checker` to `true` in the `eslint` module options. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { checker: true // <--- } }) ``` You will need to install extra dependencies `vite-plugin-eslint2` for Vite, or `eslint-webpack-plugin` if you are using the [Webpack builder](https://nuxt.com/docs/getting-started/configuration#with-webpack). ```bash # For Vite npm i -D vite-plugin-eslint2 # For Webpack npm i -D eslint-webpack-plugin ``` This enables a similar experience to using [`@nuxtjs/eslint-module`](https://github.com/nuxt-modules/eslint). The checker runs in flat config mode by default. If you are still on ESLint v9 with a legacy `.eslintrc` config, you can run it in legacy mode by setting `configType` to `eslintrc`. ESLint v10 no longer supports `.eslintrc`. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { checker: { configType: 'eslintrc' // <--- (consider migrating to flat config if possible) } } }) ``` ## Custom Config Presets By default, this module installs the JS, TS and Vue plugins with their recommended rules. This might already be covered by your config presets, so in that case you can disable the default setup by setting the `standalone` option to `false`. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { standalone: false // <--- } } }) ``` This ensures the module only generates Nuxt-specific rules so that you can merge it with your own config presets. For example, with [`@antfu/eslint-config`](https://github.com/antfu/eslint-config): ```js [eslint.config.mjs] // @ts-check import antfu from '@antfu/eslint-config' import withNuxt from './.nuxt/eslint.config.mjs' export default withNuxt( antfu({ // ...@antfu/eslint-config options }), // ...your other rules ) ``` ## Auto-Init Upon server start, the module will look for an `eslint.config.*` file and generate an `eslint.config.mjs` if it doesn't exist for easier usage. You can opt out of this by setting `config.autoInit` to `false` in the `eslint` module options. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { autoInit: false // <--- disable auto-init } } }) ``` ## Generated Config Location The module generates its flat config at `.nuxt/eslint.config.mjs` by default. You can change the path with `config.configFile`, relative to your project root. If you run ESLint from a different directory than your Nuxt project, set `config.rootDir` so the paths in the generated config resolve correctly. ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ '@nuxt/eslint' ], eslint: { config: { configFile: './eslint.nuxt.config.mjs' } } }) ``` ## Integrate with Other Modules Other Nuxt modules can add flat config items to the generated config with the `eslint:config:addons` hook. Each addon provides the imports it needs and the config items, as code strings: ```ts [my-module/module.ts] import { defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup(options, nuxt) { nuxt.hook('eslint:config:addons', (addons) => { addons.push({ name: 'my-module', async getConfigs() { return { imports: [ { from: 'eslint-plugin-my-module', name: 'default', as: 'myModulePlugin' } ], configs: [ `{ name: 'my-module/rules', plugins: { 'my-module': myModulePlugin }, rules: { 'my-module/some-rule': 'error' } }` ] } } }) }) } }) ``` # @nuxt/eslint-config > Shared, unopinionated but customizable ESLint flat config for Nuxt projects. Shared ESLint config for Nuxt projects. Unopinionated by default, but customizable. ::tip We recommand to use directly the [ESLint Module](https://nuxt.com/modules/eslint) that will provide project-aware ESLint config and Nuxt DevTools integration on top of this config. :: ::note The source code of this package is in [`packages/eslint-config`](https://github.com/nuxt/eslint/tree/main/packages/eslint-config) on GitHub. :: ## Configurations Since v1.0, this package only provides the flat config format and requires ESLint v9 or v10. The entry `@nuxt/eslint-config` provides a factory function to create a project-aware ESLint config for Nuxt projects. It is unopinionated by default but customizable by passing options to the factory function. Used by [`@nuxt/eslint`](https://nuxt.com/modules/eslint) module to generate project-aware ESLint config. 1. Install this package and `eslint` in your `devDependencies`. ::code-group ```bash [yarn] yarn add --dev @nuxt/eslint-config eslint ``` ```bash [npm] npm install --save-dev @nuxt/eslint-config eslint ``` ```bash [pnpm] pnpm add -D @nuxt/eslint-config eslint ``` ```bash [bun] bun add -D @nuxt/eslint-config eslint ``` :: 2. Import the config factory function from `@nuxt/eslint-config` entry in your `eslint.config.mjs`: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ // options here }) ``` You might also want to add a script entry to your `package.json`: ```json [package.json] { "scripts": { "lint": "eslint ." } } ``` ### Customizing the Config Note that `createConfigForNuxt()` returns a chainable [`FlatConfigComposer` instance](https://github.com/antfu/eslint-flat-config-utils#composer) from [`eslint-flat-config-utils`](https://github.com/antfu/eslint-flat-config-utils) which allows you to manipulate the ESLint flat config with ease. If you want to combine with other configs, you can use the `.append()` method: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ // options here }) .prepend( // ...Prepend some flat configs in front ) // Override some rules in a specific config, based on their name .override('nuxt/typescript/rules', { rules: { // ...Override rules, for example: '@typescript-eslint/no-explicit-any': 'off' } }) // ...you can chain more operations as needed ``` `FlatConfigComposer` is also a Promise object, that you can use `await` to get the final config object. You can also pass your custom flat configs as extra arguments, they are appended after the Nuxt configs: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt( { // options here }, // ...your custom flat configs ) ``` ### Module Authors This config also provides rules for module/library authors. You can enable them by setting the `features.tooling` option to `true`: ::warning This feature is experimental and may change in the future. :: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ features: { tooling: true } }) ``` This will enable rules with `unicorn`, `regexp` and `jsdoc` plugins, to ensure your module is following best practices. You can also turn them off individually by providing an object with the rule names set to `false`: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ features: { tooling: { regexp: false, } } }) ``` ## ESLint Stylistic Similar to the [ESLint Module](https://nuxt.com/modules/eslint/recipes#eslint-stylistic), you can opt in by setting `stylistic` to `true` in the `features` options. ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ features: { stylistic: true // <--- } }) ``` Learn more about all the available options in the [ESLint Stylistic documentation](https://eslint.style/guide/config-presets#configuration-factory). ## Type Aware Rules TypeScript support is enabled automatically when `typescript` is installed in your project. This config also provides type-aware rules for TypeScript and Vue. You can enable them by setting `features.typescript` to an object with `tsconfigPath` the path to your `tsconfig.json` file. ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ features: { typescript: { tsconfigPath: './tsconfig.json' } } }) ``` The TypeScript rules use the strict preset by default. Set `features.typescript.strict` to `false` to use the recommended preset only, or `features.typescript` to `false` to disable TypeScript support. ## Formatters To also format CSS, HTML, GraphQL and other files with ESLint, set `features.formatters` to `true` or to an object picking the file types. It requires [`eslint-plugin-format`](https://github.com/antfu/eslint-plugin-format) to be installed. See the [Formatters recipe](https://nuxt.com/modules/eslint/recipes#formatters) for the details. ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ features: { formatters: true // <--- } }) ``` ## Import Plugin The import rules use [`eslint-plugin-import-x`](https://github.com/un-ts/eslint-plugin-import-x) by default. You can switch to the lighter [`eslint-plugin-import-lite`](https://github.com/9romise/eslint-plugin-import-lite) with `features.import.package`, or disable the import rules with `features.import: false`: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ features: { import: { package: 'eslint-plugin-import-lite' } } }) ``` ## Nuxt Config Keys Order When `features.stylistic` is enabled, the keys of your `nuxt.config` are sorted in a recommended order by the [`nuxt/nuxt-config-keys-order`](https://nuxt.com/modules/eslint/plugin#nuxtnuxt-config-keys-order) rule. You can turn it on or off independently with `features.nuxt.sortConfigKeys`: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ features: { nuxt: { sortConfigKeys: false } } }) ``` ## Custom Config Presets Like the [ESLint Module](https://nuxt.com/modules/eslint/recipes#custom-config-presets), set `features.standalone` to `false` to only include the Nuxt-specific rules, without the JavaScript, TypeScript, Vue and import setup, when you combine it with another config preset. ## Directories Without the module, the config cannot read your Nuxt project structure. It assumes your source code is in the project root or in `app/`, following both the Nuxt 3 and Nuxt 4 conventions, and derives the directories of pages, layouts, components and others from it. Use `dirs` to match your project when it differs: ```js [eslint.config.mjs] import { createConfigForNuxt } from '@nuxt/eslint-config' export default createConfigForNuxt({ dirs: { src: ['./src'], // Components that are registered with a prefix, so `vue/multi-word-component-names` is not reported componentsPrefixed: ['./src/components/prefixed'] } }) ``` # @nuxt/eslint-plugin > ESLint plugin of additional rules for Nuxt. ESLint plugin of additional rules for Nuxt. ::tip Usually, you don't need to use this package directly. It's already included in the [ESLint Module](https://nuxt.com/modules/eslint) and the [ESLint Config](https://nuxt.com/modules/eslint/config). :: ::note The source code of this package is in [`packages/eslint-plugin`](https://github.com/nuxt/eslint/tree/main/packages/eslint-plugin) on GitHub. :: ## Rules All the rules are enabled by the [ESLint Module](https://nuxt.com/modules/eslint) and the [ESLint Config](https://nuxt.com/modules/eslint/config), except `nuxt/nuxt-config-keys-order` which is enabled with [stylistic rules](https://nuxt.com/modules/eslint/recipes#eslint-stylistic). To use them in your own config, register the plugin under the `nuxt` name: ```js [eslint.config.mjs] import nuxt from '@nuxt/eslint-plugin' export default [ { plugins: { nuxt }, rules: { 'nuxt/prefer-import-meta': 'error' } } ] ``` ### `nuxt/prefer-import-meta` Enforces the use of `import.meta.*` over `process.*` for the Nuxt environment flags (`client`, `browser`, `server`, `nitro`, `dev`, `test` and `prerender`), for example `import.meta.client` instead of `process.client`. Auto-fixable. ```json { "rules": { "nuxt/prefer-import-meta": "error" } } ``` ### `nuxt/no-page-meta-runtime-values` `definePageMeta()` is extracted from the page at build time and runs before the component setup. This rule disallows values that need a runtime context at the top level: composable calls such as `useRoute()`, `ref()` or `inject()`, `this` and `await`. Static values and imported or top-level variables are fine, and composables can still be called inside the `middleware` and `validate` functions. ```ts [pages/index.vue] // ❌ Not available when the page meta is extracted definePageMeta({ name: useRoute().name }) // ✅ Runs at navigation time definePageMeta({ middleware: () => { const route = useRoute() } }) ``` It is enabled for the files in your pages directories. ### `nuxt/no-nuxt-config-test-key` Disallows setting the `test` key to a boolean in your Nuxt config, as the test environment is detected automatically. ```ts [nuxt.config.ts] export default defineNuxtConfig({ test: true // ❌ }) ``` ### `nuxt/nuxt-config-keys-order` Sorts the top-level keys of your Nuxt config, and of its environment overrides such as `$production`, in a recommended order. Auto-fixable. ```json { "rules": { "nuxt/nuxt-config-keys-order": "error" } } ``` # FAQ > Frequently asked questions about ESLint integration in Nuxt This project is composed of multiple packages for different level of ESLint integration. ### What to use? For new projects, we highly recommend using the [ESLint Module](https://nuxt.com/modules/eslint) which provides a project-aware ESLint flat config generation and is more future-proof. If you still use the legacy `.eslintrc` format, `@nuxt/eslint-config` v0.x is the last version that provides a legacy config. Since v1.0, the [ESLint Config](https://nuxt.com/modules/eslint/config) package only supports flat config, so we recommend you to migrate to the flat config format if possible. If you are maintaining your custom ESLint config and want a low-level setup, you can use the [ESLint Plugin](https://nuxt.com/modules/eslint/plugin) package directly to enable some Nuxt-specific rules in your config. ### Package Disambiguation Due to the historical reasons, we have quite a few packages for different ESLint integrations. Here's a table to help you understand the differences: | Package | Tags | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | [`@nuxt/eslint`](https://nuxt.com/modules/eslint) <br> All-in-one ESLint module for Nuxt | :badge[flat-config]{color="primary" variant="subtle"} :badge[recommended]{color="success" variant="subtle"} | | [`@nuxt/eslint-config`](https://nuxt.com/modules/eslint/config) <br> Shared flat config for Nuxt (v0.x also provides a legacy config). <br>Unopinionated but customizable. | ::badge{color="primary" variant="subtle"} flat-config :: | | [`@nuxt/eslint-plugin`](https://nuxt.com/modules/eslint/plugin) <br> Low-level ESLint plugin for Nuxt. | | # Migration Guide > Migrate from @nuxtjs/eslint-module to @nuxt/eslint. ## Migrate from `@nuxtjs/eslint-module` [`@nuxtjs/eslint-module`](https://github.com/nuxt-modules/eslint) is a module for running ESLint checks along with your dev server. Its feature is now part of [`@nuxt/eslint`](https://nuxt.com/modules/eslint), as an opt-in feature under the `checker` option. ```bash npm i -D @nuxt/eslint vite-plugin-eslint2 npm uninstall @nuxtjs/eslint-module ``` ```ts [nuxt.config.ts] export default defineNuxtConfig({ modules: [ // Replace '@nuxtjs/eslint-module' with: '@nuxt/eslint', ], eslint: { checker: { // the options from `@nuxtjs/eslint-module` go here } } }) ``` You can learn more about it on [this page](https://nuxt.com/modules/eslint/recipes#dev-server-checker). `@nuxt/eslint` also generates a project-aware flat config, and creates an `eslint.config.mjs` in your project if you don't have one. If you still use an `.eslintrc` file, we recommend you to [migrate it to flat config](https://eslint.org/docs/latest/use/configure/migration-guide), as ESLint v10 no longer supports it. --- title: "Nuxt API Reference" description: "Explore all Nuxt Internals: Components, Composables, Utils, Commands and more." canonical_url: "https://nuxt.com/docs/3.x/api" --- # Nuxt API Reference > Explore all Nuxt Internals: Components, Composables, Utils, Commands and more. ::card-group :::card{icon="i-lucide-box" title="Components" to="https://nuxt.com/docs/3.x/api/components/client-only"} Explore Nuxt built-in components for pages, layouts, head, and more. ::: :::card{icon="i-lucide-arrow-left-right" title="Composables" to="https://nuxt.com/docs/3.x/api/composables/use-app-config"} Discover Nuxt composable functions for data-fetching, head management and more. ::: :::card{icon="i-lucide-square-function" title="Utils" to="https://nuxt.com/docs/3.x/api/utils/dollarfetch"} Learn about Nuxt utility functions for navigation, error handling and more. ::: :::card{icon="i-lucide-square-terminal" title="Commands" to="https://nuxt.com/docs/3.x/api/commands/add"} List of Nuxt CLI commands to init, analyze, build, and preview your application. ::: :::card{icon="i-lucide-package" title="Nuxt Kit" to="https://nuxt.com/docs/3.x/api/kit/modules"} Understand Nuxt Kit utilities to create modules and control Nuxt. ::: :::card{icon="i-lucide-brain" title="Advanced" to="https://nuxt.com/docs/3.x/api/advanced/hooks"} Go deep in Nuxt internals with Nuxt lifecycle hooks. ::: :::card{icon="i-lucide-cog" title="Nuxt Configuration" to="https://nuxt.com/docs/3.x/api/nuxt-config"} Explore all Nuxt configuration options to customize your application. ::: :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Lifecycle Hooks" description: "Nuxt provides a powerful hooking system to expand almost every aspect using hooks." canonical_url: "https://nuxt.com/docs/3.x/api/advanced/hooks" --- # Lifecycle Hooks > Nuxt provides a powerful hooking system to expand almost every aspect using hooks. ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/hooks"} :: ## App Hooks (runtime) Check the [app source code](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/nuxt.ts#L37) for all available hooks. | Hook | Arguments | Environment | Description | | ---------------------------- | ------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `app:created` | `vueApp` | Server & Client | Called when initial `vueApp` instance is created. | | `app:error` | `err` | Server & Client | Called when a fatal error occurs. | | `app:error:cleared` | `{ redirect? }` | Server & Client | Called when a fatal error occurs. | | `vue:setup` | - | Server & Client | Called when the setup of Nuxt root is initialized. This callback must be synchronous. | | `vue:error` | `err, target, info` | Server & Client | Called when a vue error propagates to the root component. [Learn More](https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured). | | `app:rendered` | `renderContext` | Server | Called when SSR rendering is done. | | `app:redirected` | - | Server | Called before SSR redirection. | | `app:beforeMount` | `vueApp` | Client | Called before mounting the app, called only on client side. | | `app:mounted` | `vueApp` | Client | Called when Vue app is initialized and mounted in browser. | | `app:suspense:resolve` | `appComponent` | Client | On [Suspense](https://vuejs.org/guide/built-ins/suspense#suspense) resolved event. | | `app:manifest:update` | `{ id, timestamp }` | Client | Called when there is a newer version of your app detected. | | `app:data:refresh` | `keys?` | Client | Called when `refreshNuxtData` is called. | | `link:prefetch` | `to` | Client | Called when a `<NuxtLink>` is observed to be prefetched. | | `page:start` | `pageComponent?` | Client | Called on [Suspense](https://vuejs.org/guide/built-ins/suspense#suspense) inside of `NuxtPage` pending event. | | `page:finish` | `pageComponent?` | Client | Called on [Suspense](https://vuejs.org/guide/built-ins/suspense#suspense) inside of `NuxtPage` resolved event. | | `page:loading:start` | - | Client | Called when a route navigation begins (before resolution) or when the page key changes. May fire without the page component's `setup()` re-running if the page is reused (e.g. with a static `key` in `definePageMeta`). | | `page:loading:end` | - | Client | Called after `page:finish` | | `page:transition:finish` | `pageComponent?` | Client | After page transition [onAfterLeave](https://vuejs.org/guide/built-ins/transition#javascript-hooks) event. | | `dev:ssr-logs` | `logs` | Client | Called with an array of server-side logs that have been passed to the client (if `features.devLogs` is enabled). | | `page:view-transition:start` | `transition` | Client | Called after `document.startViewTransition` is called when [experimental viewTransition support is enabled](https://nuxt.com/docs/3.x/getting-started/transitions#view-transitions-api-experimental). | ## Nuxt Hooks (build time) Check the [schema source code](https://github.com/nuxt/nuxt/blob/main/packages/schema/src/types/hooks.ts#L83) for all available hooks. | Hook | Arguments | Description | | --------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `kit:compatibility` | `compatibility, issues` | Allows extending compatibility checks. | | `ready` | `nuxt` | Called after Nuxt initialization, when the Nuxt instance is ready to work. | | `close` | `nuxt` | Called when Nuxt instance is gracefully closing. | | `restart` | `{ hard?: boolean }` | To be called to restart the current Nuxt instance. | | `modules:before` | - | Called during Nuxt initialization, before installing user modules. | | `modules:done` | - | Called during Nuxt initialization, after installing user modules. | | `app:resolve` | `app` | Called after resolving the `app` instance. | | `app:templates` | `app` | Called during `NuxtApp` generation, to allow customizing, modifying or adding new files to the build directory (either virtually or to written to `.nuxt`). | | `app:templatesGenerated` | `app` | Called after templates are compiled into the [virtual file system](https://nuxt.com/docs/3.x/directory-structure/nuxt) (vfs). | | `build:before` | - | Called before Nuxt bundle builder. | | `build:done` | - | Called after Nuxt bundle builder is complete. | | `build:manifest` | `manifest` | Called during the manifest build by Vite and webpack. This allows customizing the manifest that Nitro will use to render `<script>` and `<link>` tags in the final HTML. | | `builder:generateApp` | `options` | Called before generating the app. | | `builder:watch` | `event, path` | Called at build time in development when the watcher spots a change to a file or directory in the project. | | `pages:extend` | `pages` | Called after page routes are scanned from the file system. | | `pages:resolved` | `pages` | Called after page routes have been augmented with scanned metadata. | | `pages:routerOptions` | `{ files: Array<{ path: string, optional?: boolean }> }` | Called when resolving `router.options` files. Later items in the array override earlier ones. | | `server:devHandler` | `handler` | Called when the dev middleware is being registered on the Nitro dev server. | | `imports:sources` | `presets` | Called at setup allowing modules to extend sources. | | `imports:extend` | `imports` | Called at setup allowing modules to extend imports. | | `imports:context` | `context` | Called when the [unimport](https://github.com/unjs/unimport) context is created. | | `imports:dirs` | `dirs` | Allows extending import directories. | | `components:dirs` | `dirs` | Called within `app:resolve` allowing to extend the directories that are scanned for auto-importable components. | | `components:extend` | `components` | Allows extending new components. | | `nitro:config` | `nitroConfig` | Called before initializing Nitro, allowing customization of Nitro's configuration. | | `nitro:init` | `nitro` | Called after Nitro is initialized, which allows registering Nitro hooks and interacting directly with Nitro. | | `nitro:build:before` | `nitro` | Called before building the Nitro instance. | | `nitro:build:public-assets` | `nitro` | Called after copying public assets. Allows modifying public assets before Nitro server is built. | | `prerender:routes` | `ctx` | Allows extending the routes to be pre-rendered. | | `build:error` | `error` | Called when an error occurs at build time. | | `prepare:types` | `options` | Called before `@nuxt/cli` writes TypeScript configuration files (`.nuxt/tsconfig.app.json`, `.nuxt/tsconfig.server.json`, etc.) and `.nuxt/nuxt.d.ts`, allowing addition of custom references and declarations in `nuxt.d.ts`, or directly modifying the options in generated configurations | | `listen` | `listenerServer, listener` | Called when the dev server is loading. | | `schema:extend` | `schemas` | Allows extending default schemas. | | `schema:resolved` | `schema` | Allows extending resolved schema. | | `schema:beforeWrite` | `schema` | Called before writing the given schema. | | `schema:written` | - | Called after the schema is written. | | `vite:extend` | `viteBuildContext` | Allows extending Vite default context. | | `vite:extendConfig` | `viteInlineConfig, env` | Allows extending Vite default config. **Deprecated in Nuxt 5+.** In Nuxt 5, this operates on a shared configuration rather than separate client/server configs. | | `vite:configResolved` | `viteInlineConfig, env` | Allows reading the resolved Vite config. **Deprecated in Nuxt 5+.** In Nuxt 5, this operates on a shared configuration rather than separate client/server configs. | | `vite:serverCreated` | `viteServer, env` | Called when the Vite server is created. | | `vite:compiled` | - | Called after Vite server is compiled. | | `webpack:config` | `webpackConfigs` | Called before configuring the webpack compiler. | | `webpack:configResolved` | `webpackConfigs` | Allows reading the resolved webpack config. | | `webpack:compile` | `options` | Called right before compilation. | | `webpack:compiled` | `options` | Called after resources are loaded. | | `webpack:change` | `shortPath` | Called on `change` on WebpackBar. | | `webpack:error` | - | Called on `done` if has errors on WebpackBar. | | `webpack:done` | - | Called on `allDone` on WebpackBar. | | `webpack:progress` | `statesArray` | Called on `progress` on WebpackBar. | ## Nitro App Hooks (runtime, server-side) See [Nitro](https://nitro.build/guide/plugins#available-hooks) for all available hooks. | Hook | Arguments | Description | Types | | ----------------- | ------------------------------------------ | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `dev:ssr-logs` | `{ path, logs }` | Server | Called at the end of a request cycle with an array of server-side logs. | | `render:response` | `response, { event }` | Called before sending the response. | [response](https://github.com/nuxt/nuxt/blob/71ef8bd3ff207fd51c2ca18d5a8c7140476780c7/packages/nuxt/src/core/runtime/nitro/renderer.ts#L24), [event](https://github.com/h3js/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38) | | `render:html` | `html, { event }` | Called before constructing the HTML. | [html](https://github.com/nuxt/nuxt/blob/71ef8bd3ff207fd51c2ca18d5a8c7140476780c7/packages/nuxt/src/core/runtime/nitro/renderer.ts#L15), [event](https://github.com/h3js/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38) | | `render:island` | `islandResponse, { event, islandContext }` | Called before constructing the island HTML. | [islandResponse](https://github.com/nuxt/nuxt/blob/e50cabfed1984c341af0d0c056a325a8aec26980/packages/nuxt/src/core/runtime/nitro/renderer.ts#L28), [event](https://github.com/h3js/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38), [islandContext](https://github.com/nuxt/nuxt/blob/e50cabfed1984c341af0d0c056a325a8aec26980/packages/nuxt/src/core/runtime/nitro/renderer.ts#L38) | | `close` | - | Called when Nitro is closed. | - | | `error` | `error, { event? }` | Called when an error occurs. | [error](https://github.com/nitrojs/nitro/blob/d20ffcbd16fc4003b774445e1a01e698c2bb078a/src/types/runtime/nitro.ts#L48), [event](https://github.com/h3js/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38) | | `request` | `event` | Called when a request is received. | [event](https://github.com/h3js/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38) | | `beforeResponse` | `event, { body }` | Called before sending the response. | [event](https://github.com/h3js/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38), unknown | | `afterResponse` | `event, { body }` | Called after sending the response. | [event](https://github.com/h3js/h3/blob/f6ceb5581043dc4d8b6eab91e9be4531e0c30f8e/src/types.ts#L38), unknown | ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Import meta" description: "Understand where your code is running using `import.meta`." canonical_url: "https://nuxt.com/docs/3.x/api/advanced/import-meta" --- # Import meta > Understand where your code is running using \`import.meta\`. ## The `import.meta` object With ES modules you can obtain some metadata from the code that imports or compiles your ES-module. This is done through `import.meta`, which is an object that provides your code with this information. Throughout the Nuxt documentation you may see snippets that use this already to figure out whether the code is currently running on the client or server side. ::read-more{to="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/import.meta"} Read more about `import.meta`. :: ## Runtime (App) Properties These values are statically injected and can be used for tree-shaking your runtime code. | Property | Type | Description | | ----------------------- | ------- | ---------------------------------------------------------------------------- | | `import.meta.client` | boolean | True when evaluated on the client side. | | `import.meta.browser` | boolean | True when evaluated on the client side. | | `import.meta.server` | boolean | True when evaluated on the server side. | | `import.meta.nitro` | boolean | True when evaluated on the server side. | | `import.meta.dev` | boolean | True when running the Nuxt dev server. | | `import.meta.test` | boolean | True when running in a test context. | | `import.meta.prerender` | boolean | True when rendering HTML on the server in the prerender stage of your build. | ## Builder Properties These values are available both in modules and in your `nuxt.config`. | Property | Type | Description | | ----------------- | ------ | ------------------------------------- | | `import.meta.env` | object | Equals `process.env` | | `import.meta.url` | string | Resolvable path for the current file. | ## Examples ### Using `import.meta.url` to resolve files within modules ```ts [modules/my-module/index.ts] import { createResolver } from 'nuxt/kit' // Resolve relative from the current file const resolver = createResolver(import.meta.url) export default defineNuxtModule({ meta: { name: 'myModule' }, setup () { addComponent({ name: 'MyModuleComponent', // Resolves to '/modules/my-module/components/MyModuleComponent.vue' filePath: resolver.resolve('./components/MyModuleComponent.vue'), }) }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<ClientOnly>" description: "Render components only in client-side with the <ClientOnly> component." canonical_url: "https://nuxt.com/docs/3.x/api/components/client-only" --- # \<ClientOnly> > Render components only in client-side with the \<ClientOnly> component. The `<ClientOnly>` component is used for purposely rendering a component only on client side. ::note The content of the default slot will be tree-shaken out of the server build. (This does mean that any CSS used by components within it may not be inlined when rendering the initial HTML.) :: ## Props - `placeholderTag` | `fallbackTag`: specify a tag to be rendered server-side. - `placeholder` | `fallback`: specify a content to be rendered server-side. ```vue <template> <div> <Sidebar /> <!-- The <Comment> component will only be rendered on client-side --> <ClientOnly fallback-tag="span" fallback="Loading comments..." > <Comment /> </ClientOnly> </div> </template> ``` ## Slots - `#fallback`: specify a content to be rendered on the server and displayed until `<ClientOnly>` is mounted in the browser. ```vue [pages/example.vue] <template> <div> <Sidebar /> <!-- This renders the "span" element on the server side --> <ClientOnly fallback-tag="span"> <!-- this component will only be rendered on client side --> <Comments /> <template #fallback> <!-- this will be rendered on server side --> <p>Loading comments...</p> </template> </ClientOnly> </div> </template> ``` ## Examples ### Accessing HTML Elements Components inside `<ClientOnly>` are rendered only after being mounted. To access the rendered elements in the DOM, you can watch a template ref: ```vue [pages/example.vue] <script setup lang="ts"> const nuxtWelcomeRef = useTemplateRef('nuxtWelcomeRef') // The watch will be triggered when the component is available watch(nuxtWelcomeRef, () => { console.log('<NuxtWelcome /> mounted') }, { once: true }) </script> <template> <ClientOnly> <NuxtWelcome ref="nuxtWelcomeRef" /> </ClientOnly> </template> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/client-only.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<DevOnly>" description: "Render components only during development with the <DevOnly> component." canonical_url: "https://nuxt.com/docs/3.x/api/components/dev-only" --- # \<DevOnly> > Render components only during development with the \<DevOnly> component. Nuxt provides the `<DevOnly>` component to render a component only during development. The content will not be included in production builds. ```vue [pages/example.vue] <template> <div> <Sidebar /> <DevOnly> <!-- this component will only be rendered during development --> <LazyDebugBar /> <!-- if you ever require to have a replacement during production --> <!-- be sure to test these using `nuxt preview` --> <template #fallback> <div><!-- empty div for flex.justify-between --></div> </template> </DevOnly> </div> </template> ``` ## Slots - `#fallback`: if you ever require to have a replacement during production. ```vue <template> <div> <Sidebar /> <DevOnly> <!-- this component will only be rendered during development --> <LazyDebugBar /> <!-- be sure to test these using `nuxt preview` --> <template #fallback> <div><!-- empty div for flex.justify-between --></div> </template> </DevOnly> </div> </template> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/dev-only.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtClientFallback>" description: "Nuxt provides the <NuxtClientFallback> component to render its content on the client if any of its children trigger an error in SSR" canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-client-fallback" --- # \<NuxtClientFallback> > Nuxt provides the \<NuxtClientFallback> component to render its content on the client if any of its children trigger an error in SSR Nuxt provides the `<NuxtClientFallback>` component to render its content on the client if any of its children trigger an error in SSR. ::note{to="https://nuxt.com/docs/3.x/guide/going-further/experimental-features#clientfallback"} This component is experimental and in order to use it you must enable the `experimental.clientFallback` option in your `nuxt.config`. :: ```vue [pages/example.vue] <template> <div> <Sidebar /> <!-- this component will be rendered on client-side --> <NuxtClientFallback fallback-tag="span"> <Comments /> <BrokeInSSR /> </NuxtClientFallback> </div> </template> ``` ## Events - `@ssr-error`: Event emitted when a child triggers an error in SSR. Note that this will only be triggered on the server. ```vue <template> <NuxtClientFallback @ssr-error="logSomeError"> <!-- ... --> </NuxtClientFallback> </template> ``` ## Props - `placeholderTag` | `fallbackTag`: Specify a fallback tag to be rendered if the slot fails to render on the server. - **type**: `string` - **default**: `div` - `placeholder` | `fallback`: Specify fallback content to be rendered if the slot fails to render. - **type**: `string` - `keepFallback`: Keep the fallback content if it failed to render server-side. - **type**: `boolean` - **default**: `false` ::warning{icon="i-ph-warning-duotone"} The `placeholder` and `fallback` props render content as raw HTML. Do not pass untrusted user input to these props as it may lead to XSS vulnerabilities. Use the `#fallback` or `#placeholder` slots instead for dynamic content that needs proper escaping. :: ```vue <template> <!-- render <span>Hello world</span> server-side if the default slot fails to render --> <NuxtClientFallback fallback-tag="span" fallback="Hello world" > <BrokeInSSR /> </NuxtClientFallback> </template> ``` ## Slots - `#fallback`: specify content to be displayed server-side if the slot fails to render. ```vue <template> <NuxtClientFallback> <!-- ... --> <template #fallback> <!-- this will be rendered on server side if the default slot fails to render in ssr --> <p>Hello world</p> </template> </NuxtClientFallback> </template> ``` --- - [Source (client)](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/client-fallback.client.ts) - [Source (server)](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/client-fallback.server.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtErrorBoundary>" description: "The <NuxtErrorBoundary> component handles client-side errors happening in its default slot." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-error-boundary" --- # \<NuxtErrorBoundary> > The \<NuxtErrorBoundary> component handles client-side errors happening in its default slot. ::tip The `<NuxtErrorBoundary>` uses Vue's [`onErrorCaptured`](https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured) hook under the hood. :: ## Events - `@error`: Event emitted when the default slot of the component throws an error. ```vue <template> <NuxtErrorBoundary @error="logSomeError"> <!-- ... --> </NuxtErrorBoundary> </template> ``` ## Slots - `#error`: Specify a fallback content to display in case of error. ```vue <template> <NuxtErrorBoundary> <!-- ... --> <template #error="{ error, clearError }"> <p>An error occurred: {{ error }}</p> <button @click="clearError"> Clear error </button> </template> </NuxtErrorBoundary> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/error-handling"} :: ## Examples ### Accessing `error` and `clearError` in script You can access `error` and `clearError` properties within the component's script as below: ```vue <template> <NuxtErrorBoundary ref="errorBoundary"> <!-- ... --> </NuxtErrorBoundary> </template> <script setup lang="ts"> const errorBoundary = useTemplateRef('errorBoundary') // errorBoundary.value?.error // errorBoundary.value?.clearError() </script> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-error-boundary.vue) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtImg>" description: "Nuxt provides a <NuxtImg> component to handle automatic image optimization." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-img" --- # \<NuxtImg> > Nuxt provides a \<NuxtImg> component to handle automatic image optimization. `<NuxtImg>` is a drop-in replacement for the native `<img>` tag. - Uses built-in provider to optimize local and remote images - Converts `src` to provider-optimized URLs - Automatically resizes images based on `width` and `height` - Generates responsive sizes when providing `sizes` option - Supports native lazy loading as well as other `<img>` attributes ## Setup In order to use `<NuxtImg>` you should install and enable the Nuxt Image module: ```bash [Terminal] npx nuxt module add image ``` ## Usage `<NuxtImg>` outputs a native `img` tag directly (without any wrapper around it). Use it like you would use the `<img>` tag: ```html <NuxtImg src="/nuxt-icon.png" /> ``` Will result in: ```html <img src="/nuxt-icon.png" /> ``` ::read-more{to="https://image.nuxt.com/usage/nuxt-img" target="_blank"} Read more about the `<NuxtImg>` component. :: --- - [Source](https://github.com/nuxt/image/blob/main/src/runtime/components/NuxtImg.vue) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtIsland>" description: "Nuxt provides the <NuxtIsland> component to render a non-interactive component without any client JS." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-island" --- # \<NuxtIsland> > Nuxt provides the \<NuxtIsland> component to render a non-interactive component without any client JS. When rendering an island component, the content of the island component is static, thus no JS is downloaded client-side. Changing the island component props triggers a refetch of the island component to re-render it again. ::note Global styles of your application are sent with the response. :: ::tip Server only components use `<NuxtIsland>` under the hood :: ## Props - `name` : Name of the component to render. - **type**: `string` - **required** - `lazy`: Make the component non-blocking. - **type**: `boolean` - **default**: `false` - `props`: Props to send to the component to render. - **type**: `Record<string, any>` - `source`: Remote source to call the island to render. - **type**: `string` - **dangerouslyLoadClientComponents**: Required to load client components from a remote source. - **type**: `boolean` - **default**: `false` ::note Remote islands need `experimental.componentIslands` to be `'local+remote'` in your `nuxt.config`. :: ::warning{icon="i-ph-warning-duotone"} Using the `source` prop to render content from a remote server is inherently dangerous. When you specify a remote `source`, you are fully trusting that server to provide safe HTML content that will be rendered directly in your application. This is similar to using `v-html` with external content - the remote server can inject any HTML, including potentially malicious content. **Only use `source` with servers you fully trust and control.** The `dangerouslyLoadClientComponents` prop controls an additional layer of risk: whether to also download and execute client components from the remote source. Even with `dangerouslyLoadClientComponents` disabled (the default), you are still trusting the remote server's HTML output. :: ::note Component props and context are sent as GET query parameters to enable caching. Query parameters may be visible in server access logs, CDN caches, and HTTP `Referer` headers. :: ::note By default, component islands are scanned from the `~/components/islands/` directory. So the `~/components/islands/MyIsland.vue` component could be rendered with `<NuxtIsland name="MyIsland" />`. :: ## Slots Slots can be passed to an island component if declared. Every slot is interactive since the parent component is the one providing it. Some slots are reserved to `NuxtIsland` for special cases. - `#fallback`: Specify the content to be rendered before the island loads (if the component is lazy) or if `NuxtIsland` fails to fetch the component. ## Ref - `refresh()` - **type**: `() => Promise<void>` - **description**: force refetch the server component by refetching it. ## Events - `error` - **parameters**: - **error**: - **type**: `unknown` - **description**: emitted when `NuxtIsland` fails to fetch the new island. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-island.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtLayout>" description: "Nuxt provides the <NuxtLayout> component to show layouts on pages and error pages." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-layout" --- # \<NuxtLayout> > Nuxt provides the \<NuxtLayout> component to show layouts on pages and error pages. You can use `<NuxtLayout />` component to activate the `default` layout on `app.vue` or `error.vue`. ```vue [app.vue] <template> <NuxtLayout> some page content </NuxtLayout> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/layouts"} :: ## Props - `name`: Specify a layout name to be rendered, can be a string, reactive reference or a computed property. It **must** match the name of the corresponding layout file in the [`layouts/`](https://nuxt.com/docs/3.x/directory-structure/layouts) directory, or `false` to disable the layout. - **type**: `string | false` - **default**: `default` ```vue [pages/index.vue] <script setup lang="ts"> // layouts/custom.vue const layout = 'custom' </script> <template> <NuxtLayout :name="layout"> <NuxtPage /> </NuxtLayout> </template> ``` ::note Please note the layout name is normalized to kebab-case, so if your layout file is named `errorLayout.vue`, it will become `error-layout` when passed as a `name` property to `<NuxtLayout />`. :: ```vue [error.vue] <template> <NuxtLayout name="error-layout"> <NuxtPage /> </NuxtLayout> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/layouts"} Read more about dynamic layouts. :: - `fallback`: If an invalid layout is passed to the `name` prop, no layout will be rendered. Specify a `fallback` layout to be rendered in this scenario. It **must** match the name of the corresponding layout file in the [`layouts/`](https://nuxt.com/docs/3.x/directory-structure/layouts) directory. - **type**: `string` - **default**: `null` ## Additional Props `NuxtLayout` also accepts any additional props that you may need to pass to the layout. These custom props are then made accessible as attributes. ```vue [pages/some-page.vue] <template> <div> <NuxtLayout name="custom" title="I am a custom layout" > <!-- ... --> </NuxtLayout> </div> </template> ``` In the above example, the value of `title` will be available using `$attrs.title` in the template or `useAttrs().title` in `<script setup>` at custom.vue. ```vue [layouts/custom.vue] <script setup lang="ts"> const layoutCustomProps = useAttrs() console.log(layoutCustomProps.title) // I am a custom layout </script> ``` ## Transitions `<NuxtLayout />` renders incoming content via `<slot />`, which is then wrapped around Vue’s `<Transition />` component to activate layout transition. For this to work as expected, it is recommended that `<NuxtLayout />` is **not** the root element of the page component. ::code-group ```vue [pages/index.vue] <template> <div> <NuxtLayout name="custom"> <template #header> Some header template content. </template> </NuxtLayout> </div> </template> ``` ```vue [layouts/custom.vue] <template> <div> <!-- named slot --> <slot name="header" /> <slot /> </div> </template> ``` :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/transitions"} :: ## Layout's Ref To get the ref of a layout component, access it through `ref.value.layoutRef`. ::code-group ```vue [app.vue] <script setup lang="ts"> const layout = ref() function logFoo () { layout.value.layoutRef.foo() } </script> <template> <NuxtLayout ref="layout"> default layout </NuxtLayout> </template> ``` ```vue [layouts/default.vue] <script setup lang="ts"> const foo = () => console.log('foo') defineExpose({ foo, }) </script> <template> <div> default layout <slot /> </div> </template> ``` :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/layouts"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-layout.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtLink>" description: "Nuxt provides <NuxtLink> component to handle any kind of links within your application." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-link" --- # \<NuxtLink> > Nuxt provides \<NuxtLink> component to handle any kind of links within your application. ::note `<NuxtLink>` is a drop-in replacement for both Vue Router's `<RouterLink>` component and HTML's `<a>` tag. It intelligently determines whether the link is *internal* or *external* and renders it accordingly with available optimizations (prefetching, default attributes, etc.) :: ## Internal Routing In this example, we use `<NuxtLink>` component to link to another page of the application. ::code-group ```vue [pages/index.vue] <template> <NuxtLink to="/about">About page</NuxtLink> </template> ``` ```html [(Renders as) index.html] <!-- (Vue Router & Smart Prefetching) --> <a href="/about">About page</a> ``` :: ### Passing Params to Dynamic Routes In this example, we pass the `id` param to link to the route `~/pages/posts/[id].vue`. ::code-group ```vue [pages/index.vue] <template> <NuxtLink :to="{ name: 'posts-id', params: { id: 123 } }"> Post 123 </NuxtLink> </template> ``` ```html [(Renders as) index.html] <a href="/posts/123">Post 123</a> ``` :: ::tip Check out the Pages panel in Nuxt DevTools to see the route name and the params it might take. :: ::tip When you pass an object into the `to` prop, `<NuxtLink>` will inherit Vue Router’s handling of query parameters. Keys and values will be automatically encoded, so you don’t need to call `encodeURI` or `encodeURIComponent` manually. :: ### Handling Static File and Cross-App Links By default, `<NuxtLink>` uses Vue Router's client side navigation for relative route. When linking to static files in the `/public` directory or to another application hosted on the same domain, it might result in unexpected 404 errors because they are not part of the client routes. In such cases, you can use the `external` prop with `<NuxtLink>` to bypass Vue Router's internal routing mechanism. The `external` prop explicitly indicates that the link is external. `<NuxtLink>` will render the link as a standard HTML `<a>` tag. This ensures the link behaves correctly, bypassing Vue Router’s logic and directly pointing to the resource. #### Linking to Static Files For static files in the `/public` directory, such as PDFs or images, use the `external` prop to ensure the link resolves correctly. ```vue [pages/index.vue] <template> <NuxtLink to="/example-report.pdf" external > Download Report </NuxtLink> </template> ``` #### Linking to a Cross-App URL When pointing to a different application on the same domain, using the `external` prop ensures the correct behavior. ```vue [pages/index.vue] <template> <NuxtLink to="/another-app" external > Go to Another App </NuxtLink> </template> ``` Using the `external` prop or relying on automatic handling ensures proper navigation, avoids unexpected routing issues, and improves compatibility with static resources or cross-application scenarios. ## External Routing In this example, we use `<NuxtLink>` component to link to a website. ```vue [app.vue] <template> <NuxtLink to="https://nuxtjs.org"> Nuxt website </NuxtLink> <!-- <a href="https://nuxtjs.org" rel="noopener noreferrer">...</a> --> </template> ``` ## `rel` and `noRel` Attributes A `rel` attribute of `noopener noreferrer` is applied by default to links with a `target` attribute or to absolute links (e.g., links starting with `http://`, `https://`, or `//`). - `noopener` solves a [security bug](https://mathiasbynens.github.io/rel-noopener/) in older browsers. - `noreferrer` improves privacy for your users by not sending the `Referer` header to the linked site. These defaults have no negative impact on SEO and are considered [best practice](https://developer.chrome.com/docs/lighthouse/best-practices/external-anchors-use-rel-noopener). When you need to overwrite this behavior you can use the `rel` or `noRel` props. ```vue [app.vue] <template> <NuxtLink to="https://twitter.com/nuxt_js"> Nuxt Twitter </NuxtLink> <!-- <a href="https://twitter.com/nuxt_js" rel="noopener noreferrer">...</a> --> <NuxtLink to="https://discord.nuxtjs.org" rel="noopener" > Nuxt Discord </NuxtLink> <!-- <a href="https://discord.nuxtjs.org" rel="noopener">...</a> --> <NuxtLink to="/about" target="_blank" >About page</NuxtLink> <!-- <a href="/about" target="_blank" rel="noopener noreferrer">...</a> --> </template> ``` A `noRel` prop can be used to prevent the default `rel` attribute from being added to the absolute links. ```vue [app.vue] <template> <NuxtLink to="https://github.com/nuxt" no-rel > Nuxt GitHub </NuxtLink> <!-- <a href="https://github.com/nuxt">...</a> --> </template> ``` ::note `noRel` and `rel` cannot be used together. `rel` will be ignored. :: ## Prefetch Links Nuxt automatically includes smart prefetching. That means it detects when a link is visible (by default), either in the viewport or when scrolling and prefetches the JavaScript for those pages so that they are ready when the user clicks the link. Nuxt only loads the resources when the browser isn't busy and skips prefetching if your connection is offline or if you only have 2g connection. ```vue [pages/index.vue] <NuxtLink to="/about" no-prefetch> About page not pre-fetched </NuxtLink> <NuxtLink to="/about" :prefetch="false"> About page not pre-fetched </NuxtLink> ``` ### Custom Prefetch Triggers We now support custom prefetch triggers for `<NuxtLink>` after `v3.13.0`. You can use the `prefetchOn` prop to control when to prefetch links. ```vue <template> <NuxtLink prefetch-on="visibility"> This will prefetch when it becomes visible (default) </NuxtLink> <NuxtLink prefetch-on="interaction"> This will prefetch when hovered or when it gains focus </NuxtLink> </template> ``` - `visibility`: Prefetches when the link becomes visible in the viewport. Monitors the element's intersection with the viewport using the [Intersection Observer API](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API). Prefetching is triggered when the element is scrolled into view. - `interaction`: Prefetches when the link is hovered or focused. This approach listens for `pointerenter` and `focus` events, proactively prefetching resources when the user indicates intent to interact. You can also use an object to configure `prefetchOn`: ```vue <template> <NuxtLink :prefetch-on="{ interaction: true }"> This will prefetch when hovered or when it gains focus </NuxtLink> </template> ``` That you probably don't want both enabled! ```vue <template> <NuxtLink :prefetch-on="{ visibility: true, interaction: true }"> This will prefetch when hovered/focus - or when it becomes visible </NuxtLink> </template> ``` This configuration will observe when the element enters the viewport and also listen for `pointerenter` and `focus` events. This may result in unnecessary resource usage or redundant prefetching, as both triggers can prefetch the same resource under different conditions. ### Enable Cross-origin Prefetch To enable cross-origin prefetching, you can set the `crossOriginPrefetch` option in your `nuxt.config`. This will enable cross-origin prefetching using the [Speculation Rules API](https://developer.mozilla.org/en-US/docs/Web/API/Speculation_Rules_API). ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { crossOriginPrefetch: true, }, }) ``` ### Disable prefetch globally It's also possible to enable/disable prefetching all links globally for your app. ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { prefetch: false, }, }, }, }) ``` ## Props ### RouterLink When not using `external`, `<NuxtLink>` supports all Vue Router's [`RouterLink` props](https://router.vuejs.org/api/interfaces/routerlinkprops) - `to`: Any URL or a [route location object](https://router.vuejs.org/api/type-aliases/routelocation) from Vue Router - `custom`: Whether `<NuxtLink>` should wrap its content in an `<a>` element. It allows taking full control of how a link is rendered and how navigation works when it is clicked. Works the same as [Vue Router's `custom` prop](https://router.vuejs.org/api/interfaces/routerlinkprops#custom-) - `exactActiveClass`: A class to apply on exact active links. Works the same as [Vue Router's `exactActiveClass` prop](https://router.vuejs.org/api/interfaces/routerlinkprops#exactActiveClass-) on internal links. Defaults to Vue Router's default (`"router-link-exact-active"`) - `activeClass`: A class to apply on active links. Works the same as [Vue Router's `activeClass` prop](https://router.vuejs.org/api/interfaces/routerlinkprops#activeClass-) on internal links. Defaults to Vue Router's default (`"router-link-active"`) - `replace`: Works the same as [Vue Router's `replace` prop](https://router.vuejs.org/api/interfaces/routelocationoptions#replace-) on internal links - `ariaCurrentValue`: An `aria-current` attribute value to apply on exact active links. Works the same as [Vue Router's `ariaCurrentValue` prop](https://router.vuejs.org/api/interfaces/routerlinkprops#ariaCurrentValue-) on internal links ### NuxtLink {#nuxtlink} - `href`: An alias for `to`. If used with `to`, `href` will be ignored - `noRel`: If set to `true`, no `rel` attribute will be added to the external link - `external`: Forces the link to be rendered as an `<a>` tag instead of a Vue Router `RouterLink`. - `prefetch`: When enabled will prefetch middleware, layouts and payloads (when using [payloadExtraction](https://nuxt.com/docs/3.x/guide/going-further/experimental-features#payloadextraction)) of links in the viewport. Used by the experimental [crossOriginPrefetch](https://nuxt.com/docs/3.x/guide/going-further/experimental-features#crossoriginprefetch) config. - `prefetchOn`: Allows custom control of when to prefetch links. Possible options are `interaction` and `visibility` (default). You can also pass an object for full control, for example: `{ interaction: true, visibility: true }`. This prop is only used when `prefetch` is enabled (default) and `noPrefetch` is not set. - `noPrefetch`: Disables prefetching. - `prefetchedClass`: A class to apply to links that have been prefetched. ### Anchor - `target`: A `target` attribute value to apply on the link - `rel`: A `rel` attribute value to apply on the link. Defaults to `"noopener noreferrer"` for external links. ::tip Defaults can be overwritten, see [overwriting defaults](https://nuxt.com/docs/3.x/api/components/nuxt-link#overwriting-defaults) if you want to change them. :: ## Overwriting Defaults ### In Nuxt Config You can overwrite some `<NuxtLink>` defaults in your [`nuxt.config`](https://nuxt.com/docs/3.x/guide/going-further/experimental-features#defaults) ::important These options will likely be moved elsewhere in the future, such as into `app.config` or into the `app/` directory. :: ```ts [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { // default values componentName: 'NuxtLink', externalRelAttribute: 'noopener noreferrer', activeClass: 'router-link-active', exactActiveClass: 'router-link-exact-active', prefetchedClass: undefined, // can be any valid string class name trailingSlash: undefined, // can be 'append' or 'remove' prefetch: true, prefetchOn: { visibility: true }, }, }, }, }) ``` ### Custom Link Component You can overwrite `<NuxtLink>` defaults by creating your own link component using `defineNuxtLink`. ```ts [components/MyNuxtLink.ts] export default defineNuxtLink({ componentName: 'MyNuxtLink', /* see signature below for more */ }) ``` You can then use `<MyNuxtLink />` component as usual with your new defaults. ### `defineNuxtLink` Signature ```ts interface NuxtLinkOptions { componentName?: string externalRelAttribute?: string activeClass?: string exactActiveClass?: string trailingSlash?: 'append' | 'remove' prefetch?: boolean prefetchedClass?: string prefetchOn?: Partial<{ visibility: boolean interaction: boolean }> } function defineNuxtLink (options: NuxtLinkOptions): Component {} ``` - `componentName`: A name for the component. Default is `NuxtLink`. - `externalRelAttribute`: A default `rel` attribute value applied on external links. Defaults to `"noopener noreferrer"`. Set it to `""` to disable - `activeClass`: A default class to apply on active links. Works the same as [Vue Router's `linkActiveClass` option](https://router.vuejs.org/api/interfaces/routeroptions#linkActiveClass-). Defaults to Vue Router's default (`"router-link-active"`) - `exactActiveClass`: A default class to apply on exact active links. Works the same as [Vue Router's `linkExactActiveClass` option](https://router.vuejs.org/api/interfaces/routeroptions#linkExactActiveClass-). Defaults to Vue Router's default (`"router-link-exact-active"`) - `trailingSlash`: An option to either add or remove trailing slashes in the `href`. If unset or not matching the valid values `append` or `remove`, it will be ignored. - `prefetch`: Whether or not to prefetch links by default. - `prefetchOn`: Granular control of which prefetch strategies to apply by default. - `prefetchedClass`: A default class to apply to links that have been prefetched. ::link-example{to="https://nuxt.com/docs/examples/routing/pages"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-link.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtLoadingIndicator>" description: "Display a progress bar between page navigations." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-loading-indicator" --- # \<NuxtLoadingIndicator> > Display a progress bar between page navigations. ## Usage Add `<NuxtLoadingIndicator/>` in your [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app) or [`layouts/`](https://nuxt.com/docs/3.x/directory-structure/layouts). ```vue [app.vue] <template> <NuxtLoadingIndicator /> <NuxtLayout> <NuxtPage /> </NuxtLayout> </template> ``` ::link-example{to="https://nuxt.com/docs/examples/routing/pages"} :: ## Slots You can pass custom HTML or components through the loading indicator's default slot. ## Props - `color`: The color of the loading bar. It can be set to `false` to turn off explicit color styling. - `errorColor`: The color of the loading bar when `error` is set to `true`. - `height`: Height of the loading bar, in pixels (default `3`). - `duration`: Duration of the loading bar, in milliseconds (default `2000`). - `throttle`: Throttle the appearing and hiding, in milliseconds (default `200`). - `estimatedProgress`: By default Nuxt will back off as it approaches 100%. You can provide a custom function to customize the progress estimation, which is a function that receives the duration of the loading bar (above) and the elapsed time. It should return a value between 0 and 100. ::note This component is optional. To achieve full customization, you can implement your own one based on [its source code](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-loading-indicator.ts). :: ::note You can hook into the underlying indicator instance using [the `useLoadingIndicator` composable](https://nuxt.com/docs/3.x/api/composables/use-loading-indicator), which will allow you to trigger start/finish events yourself. :: ::tip The loading indicator's speed gradually decreases after reaching a specific point controlled by `estimatedProgress`. This adjustment provides a more accurate reflection of longer page loading times and prevents the indicator from prematurely showing 100% completion. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-loading-indicator.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtPage>" description: "The <NuxtPage> component is required to display pages located in the pages/ directory." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-page" --- # \<NuxtPage> > The \<NuxtPage> component is required to display pages located in the pages/ directory. `<NuxtPage>` is a built-in component that comes with Nuxt. It lets you display top-level or nested pages located in the [`pages/`](https://nuxt.com/docs/3.x/directory-structure/pages) directory. ::note `<NuxtPage>` is a wrapper around [`<RouterView>`](https://router.vuejs.org/api/interfaces/routerviewprops) from Vue Router. It should be used instead of `<RouterView>` because the former takes additional care of internal states. Otherwise, `useRoute()` may return incorrect paths. :: `<NuxtPage>` includes the following components: ```vue <template> <RouterView v-slot="{ Component }"> <!-- Optional, when using transitions --> <Transition> <!-- Optional, when using keep-alive --> <KeepAlive> <Suspense> <component :is="Component" /> </Suspense> </KeepAlive> </Transition> </RouterView> </template> ``` By default, Nuxt does not enable `<Transition>` and `<KeepAlive>`. You can enable them in the nuxt.config file or by setting the `transition` and `keepalive` properties on `<NuxtPage>`. If you want to define a specific page, you can set it in `definePageMeta` in the page component. ::warning If you enable `<Transition>` in your page component, ensure that the page has a single root element. :: Since `<NuxtPage>` uses `<Suspense>` under the hood, the component lifecycle behavior during page changes differs from that of a typical Vue application. In a typical Vue application, a new page component is mounted **only after** the previous one has been fully unmounted. However, in Nuxt, due to how Vue `<Suspense>` is implemented, the new page component is mounted **before** the previous one is unmounted. ## Props - `name`: tells `<RouterView>` to render the component with the corresponding name in the matched route record's components option. - type: `string` - `route`: route location that has all of its components resolved. - type: `RouteLocationNormalized` - `pageKey`: control when the `NuxtPage` component is re-rendered. - type: `string` or `function` - `transition`: define global transitions for all pages rendered with the `NuxtPage` component. - type: `boolean` or [`TransitionProps`](https://vuejs.org/api/built-in-components#transition) - `keepalive`: control state preservation of pages rendered with the `NuxtPage` component. - type: `boolean` or [`KeepAliveProps`](https://vuejs.org/api/built-in-components#keepalive) ::tip Nuxt automatically resolves the `name` and `route` by scanning and rendering all Vue component files found in the `/pages` directory. :: ## Example For example, if you pass a key that never changes, the `<NuxtPage>` component will be rendered only once - when it is first mounted. ```vue [app.vue] <template> <NuxtPage page-key="static" /> </template> ``` You can also use a dynamic key based on the current route: ```html <NuxtPage :page-key="route => route.fullPath" /> ``` ::warning Don't use `$route` object here as it can cause problems with how `<NuxtPage>` renders pages with `<Suspense>`. :: Alternatively, `pageKey` can be passed as a `key` value via [`definePageMeta`](https://nuxt.com/docs/3.x/api/utils/define-page-meta) from the `<script>` section of your Vue component in the `/pages` directory. ```vue [pages/my-page.vue] <script setup lang="ts"> definePageMeta({ key: route => route.fullPath, }) </script> ``` ::link-example{to="https://nuxt.com/docs/examples/routing/pages"} :: ## Page's Ref To get the `ref` of a page component, access it through `ref.value.pageRef` ```vue [app.vue] <script setup lang="ts"> const page = ref() function logFoo () { page.value.pageRef.foo() } </script> <template> <NuxtPage ref="page" /> </template> ``` ```vue [my-page.vue] <script setup lang="ts"> const foo = () => { console.log('foo method called') } defineExpose({ foo, }) </script> ``` ## Custom Props `<NuxtPage>` also accepts custom props that you may need to pass further down the hierarchy. For example, in the below example, the value of `foobar` will be passed to the `NuxtPage` component and then to the page components. ```vue [app.vue] <template> <NuxtPage :foobar="123" /> </template> ``` We can access the `foobar` prop in the page component: ```vue [pages/page.vue] <script setup lang="ts"> const props = defineProps<{ foobar: number }>() console.log(props.foobar) // Outputs: 123 ``` If you have not defined the prop with `defineProps`, any props passed down to `NuxtPage` can still be accessed directly from the page `attrs`: ```vue [pages/page.vue] <script setup lang="ts"> const attrs = useAttrs() console.log(attrs.foobar) // Outputs: 123 </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/pages"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/pages/runtime/page.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtPicture>" description: "Nuxt provides a <NuxtPicture> component to handle automatic image optimization." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-picture" --- # \<NuxtPicture> > Nuxt provides a \<NuxtPicture> component to handle automatic image optimization. `<NuxtPicture>` is a drop-in replacement for the native `<picture>` tag. Usage of `<NuxtPicture>` is almost identical to [`<NuxtImg>`](https://nuxt.com/docs/3.x/api/components/nuxt-img) but it also allows serving modern formats like `webp` when possible. Learn more about the [`<picture>` tag on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/picture). ## Setup In order to use `<NuxtPicture>` you should install and enable the Nuxt Image module: ```bash [Terminal] npx nuxt module add image ``` ::read-more{to="https://image.nuxt.com/usage/nuxt-picture" target="_blank"} Read more about the `<NuxtPicture>` component. :: --- - [Source](https://github.com/nuxt/image/blob/main/src/runtime/components/NuxtPicture.vue) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtRouteAnnouncer>" description: "The <NuxtRouteAnnouncer> component adds a hidden element with the page title to announce route changes to assistive technologies." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-route-announcer" --- # \<NuxtRouteAnnouncer> > The \<NuxtRouteAnnouncer> component adds a hidden element with the page title to announce route changes to assistive technologies. ::important This component is available in Nuxt v3.12+. :: ## Usage Add `<NuxtRouteAnnouncer/>` in your [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app) or [`layouts/`](https://nuxt.com/docs/3.x/directory-structure/layouts) to enhance accessibility by informing assistive technologies about page title changes. This ensures that navigational changes are announced to users relying on screen readers. ```vue [app.vue] <template> <NuxtRouteAnnouncer /> <NuxtLayout> <NuxtPage /> </NuxtLayout> </template> ``` ## Slots You can pass custom HTML or components through the route announcer's default slot. ```vue <template> <NuxtRouteAnnouncer> <template #default="{ message }"> <p>{{ message }} was loaded.</p> </template> </NuxtRouteAnnouncer> </template> ``` ## Props - `atomic`: Controls if screen readers only announce changes or the entire content. Set to true for full content readouts on updates, false for changes only. (default `false`) - `politeness`: Sets the urgency for screen reader announcements: `off` (disable the announcement), `polite` (waits for silence), or `assertive` (interrupts immediately). (default `polite`) ::callout This component is optional. To achieve full customization, you can implement your own one based on [its source code](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-route-announcer.ts). :: ::callout You can hook into the underlying announcer instance using [the `useRouteAnnouncer` composable](https://nuxt.com/docs/3.x/api/composables/use-route-announcer), which allows you to set a custom announcement message. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-route-announcer.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtTime>" description: "The <NuxtTime> component displays time in a locale-friendly format with server-client consistency." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-time" --- # \<NuxtTime> > The \<NuxtTime> component displays time in a locale-friendly format with server-client consistency. ::important This component is available in Nuxt v3.17+. :: The `<NuxtTime>` component lets you display dates and times in a locale-friendly format with proper `<time>` HTML semantics. It ensures consistent rendering between server and client without hydration mismatches. ## Usage You can use the `<NuxtTime>` component anywhere in your app: ```vue <template> <NuxtTime :datetime="Date.now()" /> </template> ``` ## Props ### `datetime` - Type: `Date | number | string` - Required: `true` The date and time value to display. You can provide: - A `Date` object - A timestamp (number) - An ISO-formatted date string ```vue <template> <NuxtTime :datetime="Date.now()" /> <NuxtTime :datetime="new Date()" /> <NuxtTime datetime="2023-06-15T09:30:00.000Z" /> </template> ``` ### `locale` - Type: `string` - Required: `false` - Default: Uses the browser or server's default locale The [BCP 47 language tag](https://datatracker.ietf.org/doc/html/rfc5646) for formatting (e.g., 'en-US', 'fr-FR', 'ja-JP'): ```vue <template> <NuxtTime :datetime="Date.now()" locale="fr-FR" /> </template> ``` ### Formatting Props The component accepts any property from the [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat) options: ```vue <template> <NuxtTime :datetime="Date.now()" year="numeric" month="long" day="numeric" hour="2-digit" minute="2-digit" /> </template> ``` This would output something like: "April 22, 2025, 08:30 AM" ### `relative` - Type: `boolean` - Required: `false` - Default: `false` Enables relative time formatting using the Intl.RelativeTimeFormat API: ```vue <template> <!-- Shows something like "5 minutes ago" --> <NuxtTime :datetime="Date.now() - 5 * 60 * 1000" relative /> </template> ``` ### Relative Time Formatting Props When `relative` is set to `true`, the component also accepts properties from [Intl.RelativeTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/RelativeTimeFormat/RelativeTimeFormat): ::warning Due to `style` being a reserved prop, `relativeStyle` prop is used instead. :: ```vue <template> <NuxtTime :datetime="Date.now() - 3 * 24 * 60 * 60 * 1000" relative numeric="auto" relative-style="long" /> </template> ``` This would output something like: "3 days ago" or "last Friday" depending on the `numeric` setting. ## Examples ### Basic Usage ```vue <template> <NuxtTime :datetime="Date.now()" /> </template> ``` ### Custom Formatting ```vue <template> <NuxtTime :datetime="Date.now()" weekday="long" year="numeric" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" time-zone-name="short" /> </template> ``` ### Relative Time ```vue <template> <div> <p> <NuxtTime :datetime="Date.now() - 30 * 1000" relative /> <!-- 30 seconds ago --> </p> <p> <NuxtTime :datetime="Date.now() - 45 * 60 * 1000" relative /> <!-- 45 minutes ago --> </p> <p> <NuxtTime :datetime="Date.now() + 2 * 24 * 60 * 60 * 1000" relative /> <!-- in 2 days --> </p> </div> </template> ``` ### With Custom Locale ```vue <template> <div> <NuxtTime :datetime="Date.now()" locale="en-US" weekday="long" /> <NuxtTime :datetime="Date.now()" locale="fr-FR" weekday="long" /> <NuxtTime :datetime="Date.now()" locale="ja-JP" weekday="long" /> </div> </template> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-time.vue) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<NuxtWelcome>" description: "The <NuxtWelcome> component greets users in new projects made from the starter template." canonical_url: "https://nuxt.com/docs/3.x/api/components/nuxt-welcome" --- # \<NuxtWelcome> > The \<NuxtWelcome> component greets users in new projects made from the starter template. It includes links to the Nuxt documentation, source code, and social media accounts. ```vue [app.vue] <template> <NuxtWelcome /> </template> ``` ::read-more{to="https://templates.ui.nuxtjs.org/templates/welcome" target="_blank"} Preview the `<NuxtWelcome />` component. :: ::tip This component is part of [`@nuxt/ui-templates`](https://github.com/nuxt/nuxt/tree/main/packages/ui-templates) package. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/ui-templates/templates/welcome/index.html) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "<Teleport>" description: "The <Teleport> component teleports a component to a different location in the DOM." canonical_url: "https://nuxt.com/docs/3.x/api/components/teleports" --- # \<Teleport> > The \<Teleport> component teleports a component to a different location in the DOM. ::warning The `to` target of [`<Teleport>`](https://vuejs.org/guide/built-ins/teleport) expects a CSS selector string or an actual DOM node. Nuxt currently has SSR support for teleports to `#teleports` only, with client-side support for other targets using a `<ClientOnly>` wrapper. :: ## Body Teleport ```vue <template> <button @click="open = true"> Open Modal </button> <Teleport to="#teleports"> <div v-if="open" class="modal" > <p>Hello from the modal!</p> <button @click="open = false"> Close </button> </div> </Teleport> </template> ``` ## Client-side Teleport ```vue <template> <ClientOnly> <Teleport to="#some-selector"> <!-- content --> </Teleport> </ClientOnly> </template> ``` ::link-example{to="https://nuxt.com/docs/examples/advanced/teleport"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "onPrehydrate" description: "Use onPrehydrate to run a callback on the client immediately before Nuxt hydrates the page." canonical_url: "https://nuxt.com/docs/3.x/api/composables/on-prehydrate" --- # onPrehydrate > Use onPrehydrate to run a callback on the client immediately before Nuxt hydrates the page. ::important This composable is available in Nuxt v3.12+. :: `onPrehydrate` is a composable lifecycle hook that allows you to run a callback on the client immediately before Nuxt hydrates the page. ::note This is an advanced utility and should be used with care. For example, [`nuxt-time`](https://github.com/danielroe/nuxt-time/pull/251) and [`@nuxtjs/color-mode`](https://github.com/nuxt-modules/color-mode/blob/main/src/script.js) manipulate the DOM to avoid hydration mismatches. :: ## Usage Call `onPrehydrate` in the setup function of a Vue component (e.g., in `<script setup>`) or in a plugin. It only has an effect when called on the server and will not be included in your client build. ## Type ```ts [Signature] export function onPrehydrate (callback: (el: HTMLElement) => void): void export function onPrehydrate (callback: string | ((el: HTMLElement) => void), key?: string): undefined | string ``` ## Parameters | Parameter | Type | Required | Description | | ---------- | --------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `callback` | `((el: HTMLElement) => void) \| string` | Yes | A function (or stringified function) to run before Nuxt hydrates. It will be stringified and inlined in the HTML. Should not have external dependencies or reference variables outside the callback. Runs before Nuxt runtime initializes, so it should not rely on Nuxt or Vue context. | | `key` | `string` | No | (Advanced) A unique key to identify the prehydrate script, useful for advanced scenarios like multiple root nodes. | ## Return Values - Returns `undefined` when called with only a callback function. - Returns a string (the prehydrate id) when called with a callback and a key, which can be used to set or access the `data-prehydrate-id` attribute for advanced use cases. ## Example ```vue twoslash [app.vue] <script setup lang="ts"> declare const window: Window // ---cut--- // Run code before Nuxt hydrates onPrehydrate(() => { console.log(window) }) // Access the root element onPrehydrate((el) => { console.log(el.outerHTML) // <div data-v-inspector="app.vue:15:3" data-prehydrate-id=":b3qlvSiBeH:"> Hi there </div> }) // Advanced: access/set `data-prehydrate-id` yourself const prehydrateId = onPrehydrate((el) => {}) </script> <template> <div> Hi there </div> </template> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useAppConfig" description: "Access the reactive app config defined in the project." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-app-config" --- # useAppConfig > Access the reactive app config defined in the project. ## Usage ```ts const appConfig = useAppConfig() console.log(appConfig) ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/app-config"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/config.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useAsyncData" description: "useAsyncData provides access to data that resolves asynchronously in an SSR-friendly composable." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-async-data" --- # useAsyncData > useAsyncData provides access to data that resolves asynchronously in an SSR-friendly composable. Within your pages, components, and plugins you can use useAsyncData to get access to data that resolves asynchronously. ::note [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) is a composable meant to be called directly in the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context). It returns reactive composables and handles adding responses to the Nuxt payload so they can be passed from server to client **without re-fetching the data on client side** when the page hydrates. :: ## Usage ```vue [pages/index.vue] <script setup lang="ts"> const { data, status, pending, error, refresh, clear } = await useAsyncData( 'mountains', (_nuxtApp, { signal }) => $fetch('https://api.nuxtjs.dev/mountains', { signal }), ) </script> ``` ::warning{to="https://nuxt.com/docs/3.x/guide/recipes/custom-usefetch#custom-usefetchuseasyncdata"} If you're using a custom `useAsyncData` wrapper, do not await it in the composable as that can cause unexpected behavior. See recipe for custom async data fetcher. :: ::note `data`, `status`, `pending` and `error` are Vue refs and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions. :: ### Watch Params The built-in `watch` option allows automatically rerunning the fetcher function when any changes are detected. ```vue [pages/index.vue] <script setup lang="ts"> const page = ref(1) const { data: posts } = await useAsyncData( 'posts', (_nuxtApp, { signal }) => $fetch('https://fakeApi.com/posts', { params: { page: page.value, }, signal, }), { watch: [page], }, ) </script> ``` ### Reactive Keys You can use a computed ref, plain ref or a getter function as the key, allowing for dynamic data fetching that automatically updates when the key changes: ```vue [pages/[id\\].vue] <script setup lang="ts"> const route = useRoute() const userId = computed(() => `user-${route.params.id}`) // When the route changes and userId updates, the data will be automatically refetched const { data: user } = useAsyncData( userId, () => fetchUserById(route.params.id), ) </script> ``` ### Make your `handler` abortable You can make your `handler` function abortable by using the `signal` provided in the second argument. This is useful for cancelling requests when they are no longer needed, such as when a user navigates away from a page. `$fetch` natively supports abort signals. ```ts const { data, error } = await useAsyncData( 'users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), ) refresh() // will actually cancel the $fetch request (if dedupe: cancel) refresh() // will actually cancel the $fetch request (if dedupe: cancel) refresh() clear() // will cancel the latest pending handler ``` You can also pass an `AbortSignal` to the `refresh`/`execute` function to cancel individual requests manually. ```ts const { refresh } = await useAsyncData( 'users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), ) let abortController: AbortController | undefined function handleUserAction () { abortController = new AbortController() refresh({ signal: abortController.signal }) } function handleCancel () { abortController?.abort() // aborts the ongoing refresh request } ``` If your `handler` function does not support abort signals, you can implement your own abort logic using the `signal` provided. ```ts const { data, error } = await useAsyncData( 'users', (_nuxtApp, { signal }) => { return new Promise((resolve, reject) => { signal?.addEventListener('abort', () => { reject(new Error('Request aborted')) }) return Promise.resolve(callback.call(this, yourHandler)).then(resolve, reject) }) }, ) ``` The handler signal will be aborted when: - A new request is made with `dedupe: 'cancel'` - The `clear` function is called - The `options.timeout` duration is exceeded ::warning [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) is a reserved function name transformed by the compiler, so you should not name your own function [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data). :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching#useasyncdata"} :: ## Params - `key`: a unique key to ensure that data fetching can be properly de-duplicated across requests. If you do not provide a key, then a key that is unique to the file name and line number of the instance of `useAsyncData` will be generated for you. - `handler`: an asynchronous function that must return a truthy value (for example, it should not be `undefined` or `null`) or the request may be duplicated on the client side. ::warning The `handler` function should be **side-effect free** to ensure predictable behavior during SSR and CSR hydration. If you need to trigger side effects, use the [`callOnce`](https://nuxt.com/docs/3.x/api/utils/call-once) utility to do so. :: - `options`: - `server`: whether to fetch the data on the server (defaults to `true`) - `lazy`: whether to resolve the async function after loading the route, instead of blocking client-side navigation (defaults to `false`) - `immediate`: when set to `false`, will prevent the request from firing immediately. (defaults to `true`) - `default`: a factory function to set the default value of the `data`, before the async function resolves - useful with the `lazy: true` or `immediate: false` option - `transform`: a function that can be used to alter `handler` function result after resolving - `getCachedData`: Provide a function which returns cached data. An `undefined` return value will trigger a fetch. By default, this is: ```ts const getDefaultCachedData = (key, nuxtApp, ctx) => nuxtApp.isHydrating ? nuxtApp.payload.data[key] : nuxtApp.static.data[key] ``` Which only caches data when `experimental.payloadExtraction` of `nuxt.config` is enabled. - `pick`: only pick specified keys in this array from the `handler` function result - `watch`: watch reactive sources to auto-refresh - `deep`: return data in a deep ref object (it is `true` by default). It can be set to `false` to return data in a shallow ref object, which can improve performance if your data does not need to be deeply reactive. - `dedupe`: avoid fetching same key more than once at a time (defaults to `cancel`). Possible options: - `cancel` - cancels existing requests when a new one is made - `defer` - does not make new requests at all if there is a pending request - `timeout` - a number in milliseconds to wait before timing out the request (defaults to `undefined`, which means no timeout) ::note Under the hood, `lazy: false` uses `<Suspense>` to block the loading of the route before the data has been fetched. Consider using `lazy: true` and implementing a loading state instead for a snappier user experience. :: ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-lazy-async-data"} You can use `useLazyAsyncData` to have the same behavior as `lazy: true` with `useAsyncData`. :: ::video-accordion{title="Watch a video from Alexander Lichter about client-side caching with getCachedData" videoId="aQPR0xn-MMk"} :: ### Shared State and Option Consistency When using the same key for multiple `useAsyncData` calls, they will share the same `data`, `error`, `status` and `pending` refs. This ensures consistency across components but requires option consistency. The following options **must be consistent** across all calls with the same key: - `handler` function - `deep` option - `transform` function - `pick` array - `getCachedData` function - `default` value The following options **can differ** without triggering warnings: - `server` - `lazy` - `immediate` - `dedupe` - `watch` ```ts // ❌ This will trigger a development warning const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: false }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: true }) // ✅ This is allowed const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: true }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: false }) ``` ::tip Keyed state created using `useAsyncData` can be retrieved across your Nuxt application using [`useNuxtData`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-data). :: ## Return Values - `data`: the result of the asynchronous function that is passed in. - `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function. - `error`: an error object if the data fetching failed. - `status`: a string indicating the status of the data request: - `idle`: when the request has not started, such as: - when `execute` has not yet been called and `{ immediate: false }` is set - when rendering HTML on the server and `{ server: false }` is set - `pending`: the request is in progress - `success`: the request has completed successfully - `error`: the request has failed - `pending`: a `Ref<boolean>` that is `true` while the request is in progress. - `clear`: a function that can be used to set `data` to `undefined` (or the value of `options.default()` if provided), set `error` to `null`, set `status` to `idle`, and mark any currently pending requests as cancelled. By default, Nuxt waits until a `refresh` is finished before it can be executed again. ::note If you have not fetched data on the server (for example, with `server: false`), then the data *will not* be fetched until hydration completes. This means even if you await [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) on the client side, `data` will remain `undefined` within `<script setup>`. :: ## Type ```ts [Signature] export type AsyncDataHandler<ResT> = (nuxtApp: NuxtApp, options: { signal: AbortSignal }) => Promise<ResT> export function useAsyncData<DataT, DataE> ( handler: AsyncDataHandler<DataT>, options?: AsyncDataOptions<DataT>, ): AsyncData<DataT, DataE> export function useAsyncData<DataT, DataE> ( key: MaybeRefOrGetter<string>, handler: AsyncDataHandler<DataT>, options?: AsyncDataOptions<DataT>, ): Promise<AsyncData<DataT, DataE>> type AsyncDataOptions<DataT> = { server?: boolean lazy?: boolean immediate?: boolean deep?: boolean dedupe?: 'cancel' | 'defer' default?: () => DataT | Ref<DataT> | null transform?: (input: DataT) => DataT | Promise<DataT> pick?: string[] watch?: MultiWatchSources | false getCachedData?: (key: string, nuxtApp: NuxtApp, ctx: AsyncDataRequestContext) => DataT | undefined timeout?: number } type AsyncDataRequestContext = { /** The reason for this data request */ cause: 'initial' | 'refresh:manual' | 'refresh:hook' | 'watch' } type AsyncData<DataT, ErrorT> = { data: Ref<DataT | null> refresh: (opts?: AsyncDataExecuteOptions) => Promise<void> execute: (opts?: AsyncDataExecuteOptions) => Promise<void> clear: () => void error: Ref<ErrorT | null> status: Ref<AsyncDataRequestStatus> pending: Ref<boolean> } interface AsyncDataExecuteOptions { dedupe?: 'cancel' | 'defer' timeout?: number signal?: AbortSignal } type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error' ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useCookie" description: "useCookie is an SSR-friendly composable to read and write cookies." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-cookie" --- # useCookie > useCookie is an SSR-friendly composable to read and write cookies. ## Usage Within your pages, components, and plugins, you can use `useCookie` to read and write cookies in an SSR-friendly way. ```ts const cookie = useCookie(name, options) ``` ::note `useCookie` only works in the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context). :: ::tip The returned ref will automatically serialize and deserialize cookie values to JSON. :: ## Type ```ts [Signature] import type { Ref } from 'vue' import type { CookieParseOptions, CookieSerializeOptions } from 'cookie-es' export interface CookieOptions<T = any> extends Omit<CookieSerializeOptions & CookieParseOptions, 'decode' | 'encode'> { decode?(value: string): T encode?(value: T): string default?: () => T | Ref<T> watch?: boolean | 'shallow' readonly?: boolean } export interface CookieRef<T> extends Ref<T> {} export function useCookie<T = string | null | undefined> ( name: string, options?: CookieOptions<T>, ): CookieRef<T> ``` ## Parameters `name`: The name of the cookie. `options`: Options to control cookie behavior. The object can have the following properties: Most of the options will be directly passed to the [cookie](https://github.com/jshttp/cookie) package. | Property | Type | Default | Description | | ------------- | ---------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `decode` | `(value: string) => T` | `decodeURIComponent` + [destr](https://github.com/unjs/destr). | Custom function to decode the cookie value. Since the value of a cookie has a limited character set (and must be a simple string), this function can be used to decode a previously encoded cookie value into a JavaScript string or other object. <br> **Note:** If an error is thrown from this function, the original, non-decoded cookie value will be returned as the cookie's value. | | `encode` | `(value: T) => string` | `JSON.stringify` + `encodeURIComponent` | Custom function to encode the cookie value. Since the value of a cookie has a limited character set (and must be a simple string), this function can be used to encode a value into a string suited for a cookie's value. | | `default` | `() => T \| Ref<T>` | `undefined` | Function returning the default value if the cookie does not exist. The function can also return a `Ref`. | | `watch` | `boolean \| 'shallow'` | `true` | Whether to watch for changes and update the cookie. `true` for deep watch, `'shallow'` for shallow watch, i.e. data changes for only top level properties, `false` to disable. <br> **Note:** Refresh `useCookie` values manually when a cookie has changed with [`refreshCookie`](https://nuxt.com/docs/3.x/api/utils/refresh-cookie). | | `readonly` | `boolean` | `false` | If `true`, disables writing to the cookie. | | `maxAge` | `number` | `undefined` | Max age in seconds for the cookie, i.e. the value for the [`Max-Age` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.2). The given number will be converted to an integer by rounding down. By default, no maximum age is set. | | `expires` | `Date` | `undefined` | Expiration date for the cookie. By default, no expiration is set. Most clients will consider this a "non-persistent cookie" and will delete it on a condition like exiting a web browser application. <br> **Note:** The [cookie storage model specification](https://datatracker.ietf.org/doc/html/rfc6265#section-5.3) states that if both `expires` and `maxAge` is set, then `maxAge` takes precedence, but not all clients may obey this, so if both are set, they should point to the same date and time! <br>If neither of `expires` and `maxAge` is set, the cookie will be session-only and removed when the user closes their browser. | | `httpOnly` | `boolean` | `false` | Sets the HttpOnly attribute. <br> **Note:** Be careful when setting this to `true`, as compliant clients will not allow client-side JavaScript to see the cookie in `document.cookie`. | | `secure` | `boolean` | `false` | Sets the [`Secure` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.5). <br>**Note:** Be careful when setting this to `true`, as compliant clients will not send the cookie back to the server in the future if the browser does not have an HTTPS connection. This can lead to hydration errors. | | `partitioned` | `boolean` | `false` | Sets the [`Partitioned` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/draft-cutler-httpbis-partitioned-cookies#section-2.1). <br>**Note:** This is an attribute that has not yet been fully standardized, and may change in the future. <br>This also means many clients may ignore this attribute until they understand it.<br>More information can be found in the [proposal](https://github.com/privacycg/CHIPS). | | `domain` | `string` | `undefined` | Sets the [`Domain` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.3). By default, no domain is set, and most clients will consider applying the cookie only to the current domain. | | `path` | `string` | `'/'` | Sets the [`Path` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/rfc6265#section-5.2.4). By default, the path is considered the ["default path"](https://datatracker.ietf.org/doc/html/rfc6265#section-5.1.4). | | `sameSite` | `boolean \| string` | `undefined` | Sets the [`SameSite` `Set-Cookie` attribute](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7). <br>- `true` will set the `SameSite` attribute to `Strict` for strict same-site enforcement.<br>- `false` will not set the `SameSite` attribute.<br>- `'lax'` will set the `SameSite` attribute to `Lax` for lax same-site enforcement.<br>- `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.<br>- `'strict'` will set the `SameSite` attribute to `Strict` for strict same-site enforcement. | ## Return Values Returns a Vue `Ref<T>` representing the cookie value. Updating the ref will update the cookie (unless `readonly` is set). The ref is SSR-friendly and will work on both client and server. ## Examples ### Basic Usage The example below creates a cookie called `counter`. If the cookie doesn't exist, it is initially set to a random value. Whenever we update the `counter` variable, the cookie will be updated accordingly. ```vue [app.vue] <script setup lang="ts"> const counter = useCookie('counter') counter.value ||= Math.round(Math.random() * 1000) </script> <template> <div> <h1>Counter: {{ counter || '-' }}</h1> <button @click="counter = null"> reset </button> <button @click="counter--"> - </button> <button @click="counter++"> + </button> </div> </template> ``` ### Readonly Cookies ```vue <script setup lang="ts"> const user = useCookie( 'userInfo', { default: () => ({ score: -1 }), watch: false, }, ) if (user.value) { // the actual `userInfo` cookie will not be updated user.value.score++ } </script> <template> <div>User score: {{ user?.score }}</div> </template> ``` ### Writable Cookies ```vue <script setup lang="ts"> const list = useCookie( 'list', { default: () => [], watch: 'shallow', }, ) function add () { list.value?.push(Math.round(Math.random() * 1000)) // list cookie won't be updated with this change } function save () { // the actual `list` cookie will be updated list.value &&= [...list.value] } </script> <template> <div> <h1>List</h1> <pre>{{ list }}</pre> <button @click="add"> Add </button> <button @click="save"> Save </button> </div> </template> ``` ### Cookies in API Routes You can use `getCookie` and `setCookie` from [`h3`](https://github.com/h3js/h3) package to set cookies in server API routes. ```ts [server/api/counter.ts] export default defineEventHandler((event) => { // Read counter cookie let counter = getCookie(event, 'counter') || 0 // Increase counter cookie by 1 setCookie(event, 'counter', ++counter) // Send JSON response return { counter } }) ``` ::link-example{to="https://nuxt.com/docs/examples/advanced/use-cookie"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/cookie.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useError" description: "useError composable returns the global Nuxt error that is being handled." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-error" --- # useError > useError composable returns the global Nuxt error that is being handled. ## Usage The `useError` composable returns the global Nuxt error that is being handled and is available on both client and server. It provides a reactive, SSR-friendly error state across your app. ```ts const error = useError() ``` You can use this composable in your components, pages, or plugins to access or react to the current Nuxt error. ## Type ```ts interface NuxtError<DataT = unknown> { status: number statusText: string message: string data?: DataT error?: true } export const useError: () => Ref<NuxtError | undefined> ``` ## Parameters This composable does not take any parameters. ## Return Values Returns a `Ref` containing the current Nuxt error (or `undefined` if there is no error). The error object is reactive and will update automatically when the error state changes. ## Example ```vue <script setup lang="ts"> const error = useError() if (error.value) { console.error('Nuxt error:', error.value) } </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/error-handling"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/error.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useFetch" description: "Fetch data from an API endpoint with an SSR-friendly composable." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-fetch" --- # useFetch > Fetch data from an API endpoint with an SSR-friendly composable. This composable provides a convenient wrapper around [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) and [`$fetch`](https://nuxt.com/docs/3.x/api/utils/dollarfetch). It automatically generates a key based on URL and fetch options, provides type hints for request url based on server routes, and infers API response type. ::note `useFetch` is a composable meant to be called directly in a setup function, plugin, or route middleware. It returns reactive composables and handles adding responses to the Nuxt payload so they can be passed from server to client without re-fetching the data on client side when the page hydrates. :: ## Usage ```vue [pages/modules.vue] <script setup lang="ts"> const { data, status, error, refresh, clear } = await useFetch('/api/modules', { pick: ['title'], }) </script> ``` ::warning{to="https://nuxt.com/docs/3.x/guide/recipes/custom-usefetch#custom-usefetchuseasyncdata"} If you're using a custom `useFetch` wrapper, do not await it in the composable as that can cause unexpected behavior. See recipe for custom async data fetcher. :: ::note `data`, `status`, and `error` are Vue refs, and they should be accessed with `.value` when used within the `<script setup>`, while `refresh`/`execute` and `clear` are plain functions. :: Using the `query` option, you can add search parameters to your query. This option is extended from [unjs/ofetch](https://github.com/unjs/ofetch) and is using [unjs/ufo](https://github.com/unjs/ufo) to create the URL. Objects are automatically stringified. ```ts const param1 = ref('value1') const { data, status, error, refresh } = await useFetch('/api/modules', { query: { param1, param2: 'value2' }, }) ``` The above example results in `https://api.nuxt.com/modules?param1=value1¶m2=value2`. You can also use [interceptors](https://github.com/unjs/ofetch#%EF%B8%8F-interceptors): ```ts const { data, status, error, refresh, clear } = await useFetch('/api/auth/login', { onRequest ({ request, options }) { // Set the request headers // note that this relies on ofetch >= 1.4.0 - you may need to refresh your lockfile options.headers.set('Authorization', '...') }, onRequestError ({ request, options, error }) { // Handle the request errors }, onResponse ({ request, response, options }) { // Process the response data localStorage.setItem('token', response._data.token) }, onResponseError ({ request, response, options }) { // Handle the response errors }, }) ``` ### Reactive Keys and Shared State You can use a computed ref or a plain ref as the URL, allowing for dynamic data fetching that automatically updates when the URL changes: ```vue [pages/[id\\].vue] <script setup lang="ts"> const route = useRoute() const id = computed(() => route.params.id) // When the route changes and id updates, the data will be automatically refetched const { data: post } = await useFetch(() => `/api/posts/${id.value}`) </script> ``` When using `useFetch` with the same URL and options in multiple components, they will share the same `data`, `error` and `status` refs. This ensures consistency across components. ::tip Keyed state created using `useFetch` can be retrieved across your Nuxt application using [`useNuxtData`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-data). :: ::warning `useFetch` is a reserved function name transformed by the compiler, so you should not name your own function `useFetch`. :: ::warning If you encounter the `data` variable destructured from a `useFetch` returns a string and not a JSON parsed object then make sure your component doesn't include an import statement like `import { useFetch } from '@vueuse/core`. :: ::video-accordion{title="Watch the video from Alexander Lichter to avoid using useFetch the wrong way" videoId="njsGVmcWviY"} :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: ### Reactive Fetch Options Fetch options can be provided as reactive, supporting `computed`, `ref` and [computed getters](https://vuejs.org/guide/essentials/computed). When a reactive fetch option is updated it will trigger a refetch using the updated resolved reactive value. ```ts const searchQuery = ref('initial') const { data } = await useFetch('/api/search', { query: { q: searchQuery }, }) // triggers a refetch: /api/search?q=new%20search searchQuery.value = 'new search' ``` If needed, you can opt out of this behavior using `watch: false`: ```ts const searchQuery = ref('initial') const { data } = await useFetch('/api/search', { query: { q: searchQuery }, watch: false, }) // does not trigger a refetch searchQuery.value = 'new search' ``` ## Type ```ts [Signature] export function useFetch<DataT, ErrorT> ( url: string | Request | Ref<string | Request> | (() => string | Request), options?: UseFetchOptions<DataT>, ): Promise<AsyncData<DataT, ErrorT>> type UseFetchOptions<DataT> = { key?: MaybeRefOrGetter<string> method?: MaybeRefOrGetter<string> query?: MaybeRefOrGetter<SearchParams> params?: MaybeRefOrGetter<SearchParams> body?: MaybeRefOrGetter<RequestInit['body'] | Record<string, any>> headers?: MaybeRefOrGetter<Record<string, string> | [key: string, value: string][] | Headers> baseURL?: MaybeRefOrGetter<string> cache?: false | 'default' | 'force-cache' | 'no-cache' | 'no-store' | 'only-if-cached' | 'reload' server?: boolean lazy?: boolean immediate?: boolean getCachedData?: (key: string, nuxtApp: NuxtApp, ctx: AsyncDataRequestContext) => DataT | undefined deep?: boolean dedupe?: 'cancel' | 'defer' timeout?: number default?: () => DataT transform?: (input: DataT) => DataT | Promise<DataT> pick?: string[] $fetch?: typeof globalThis.$fetch watch?: MultiWatchSources | false timeout?: MaybeRefOrGetter<number> } type AsyncDataRequestContext = { /** The reason for this data request */ cause: 'initial' | 'refresh:manual' | 'refresh:hook' | 'watch' } type AsyncData<DataT, ErrorT> = { data: Ref<DataT | null> pending: Ref<boolean> refresh: (opts?: AsyncDataExecuteOptions) => Promise<void> execute: (opts?: AsyncDataExecuteOptions) => Promise<void> clear: () => void error: Ref<ErrorT | null> status: Ref<AsyncDataRequestStatus> } interface AsyncDataExecuteOptions { dedupe?: 'cancel' | 'defer' timeout?: number signal?: AbortSignal } type AsyncDataRequestStatus = 'idle' | 'pending' | 'success' | 'error' ``` ## Parameters - `URL` (`string | Request | Ref<string | Request> | () => string | Request`): The URL or request to fetch. Can be a string, a Request object, a Vue ref, or a function returning a string/Request. Supports reactivity for dynamic endpoints. - `options` (object): Configuration for the fetch request. Extends [unjs/ofetch](https://github.com/unjs/ofetch) options and [`AsyncDataOptions`](https://nuxt.com/docs/3.x/api/composables/use-async-data#params). All options can be a static value, a `ref`, or a computed value. | Option | Type | Default | Description | | --------------- | ----------------------------------------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------- | | `key` | `MaybeRefOrGetter<string>` | auto-gen | Unique key for de-duplication. If not provided, generated from URL and options. | | `method` | `MaybeRefOrGetter<string>` | `'GET'` | HTTP request method. | | `query` | `MaybeRefOrGetter<SearchParams>` | - | Query/search params to append to the URL. Alias: `params`. | | `params` | `MaybeRefOrGetter<SearchParams>` | - | Alias for `query`. | | `body` | `MaybeRefOrGetter<RequestInit['body'] \| Record<string, any>>` | - | Request body. Objects are automatically stringified. | | `headers` | `MaybeRefOrGetter<Record<string, string> \| [key, value][] \| Headers>` | - | Request headers. | | `baseURL` | `MaybeRefOrGetter<string>` | - | Base URL for the request. | | `cache` | `false \| string` | - | Cache control. Boolean disables cache, or use Fetch API values: `default`, `no-store`, etc. | | `server` | `boolean` | `true` | Whether to fetch on the server. | | `lazy` | `boolean` | `false` | If true, resolves after route loads (does not block navigation). | | `immediate` | `boolean` | `true` | If false, prevents request from firing immediately. | | `default` | `() => DataT` | - | Factory for default value of `data` before async resolves. | | `timeout` | `number` | - | A number in milliseconds to wait before timing out the request (defaults to `undefined`, which means no timeout) | | `transform` | `(input: DataT) => DataT \| Promise<DataT>` | - | Function to transform the result after resolving. | | `getCachedData` | `(key, nuxtApp, ctx) => DataT \| undefined` | - | Function to return cached data. See below for default. | | `pick` | `string[]` | - | Only pick specified keys from the result. | | `watch` | `MultiWatchSources \| false` | - | Array of reactive sources to watch and auto-refresh. `false` disables watching. | | `deep` | `boolean` | `false` | Return data in a deep ref object. | | `dedupe` | `'cancel' \| 'defer'` | `'cancel'` | Avoid fetching same key more than once at a time. | | `$fetch` | `typeof globalThis.$fetch` | - | Custom $fetch implementation. See [Custom useFetch in Nuxt](https://nuxt.com/docs/3.x/guide/recipes/custom-usefetch) | ::note All fetch options can be given a `computed` or `ref` value. These will be watched and new requests made automatically with any new values if they are updated. :: **getCachedData default:** ```ts const getDefaultCachedData = (key, nuxtApp, ctx) => nuxtApp.isHydrating ? nuxtApp.payload.data[key] : nuxtApp.static.data[key] ``` This only caches data when `experimental.payloadExtraction` in `nuxt.config` is enabled. ## Return Values | Name | Type | Description | | --------- | --------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `data` | `Ref<DataT \| undefined>` | The result of the asynchronous fetch. | | `refresh` | `(opts?: AsyncDataExecuteOptions) => Promise<void>` | Function to manually refresh the data. By default, Nuxt waits until a `refresh` is finished before it can be executed again. | | `execute` | `(opts?: AsyncDataExecuteOptions) => Promise<void>` | Alias for `refresh`. | | `error` | `Ref<ErrorT \| undefined>` | Error object if the data fetching failed. | | `status` | `Ref<'idle' \| 'pending' \| 'success' \| 'error'>` | Status of the data request. See below for possible values. | | `pending` | `Ref<boolean>` | Boolean flag indicating whether the current request is in progress. | | `clear` | `() => void` | Resets `data` to `undefined` (or the value of `options.default()` if provided), `error` to `undefined`, set `status` to `idle`, and cancels any pending requests. | ### Status values - `idle`: Request has not started (e.g. `{ immediate: false }` or `{ server: false }` on server render) - `pending`: Request is in progress - `success`: Request completed successfully - `error`: Request failed ::note If you have not fetched data on the server (for example, with `server: false`), then the data *will not* be fetched until hydration completes. This means even if you await `useFetch` on client-side, `data` will remain undefined within `<script setup>`. :: ### Examples ::link-example{to="https://nuxt.com/docs/examples/advanced/use-custom-fetch-composable"} :: ::link-example{to="https://nuxt.com/docs/examples/features/data-fetching"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/fetch.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useHead" description: "useHead customizes the head properties of individual pages of your Nuxt app." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-head" --- # useHead > useHead customizes the head properties of individual pages of your Nuxt app. ## Usage The `useHead` composable allows you to manage your head tags in a programmatic and reactive way, powered by [Unhead](https://unhead.unjs.io). It lets you customize the meta tags, links, scripts, and other elements in the `<head>` section of your HTML document. ```vue [app.vue] <script setup lang="ts"> useHead({ title: 'My App', meta: [ { name: 'description', content: 'My amazing site.' }, ], bodyAttrs: { class: 'test', }, script: [{ innerHTML: 'console.log(\'Hello world\')' }], }) </script> ``` ::warning If the data comes from a user or other untrusted source, we recommend you check out [`useHeadSafe`](https://nuxt.com/docs/3.x/api/composables/use-head-safe). :: ::note The properties of `useHead` can be dynamic, accepting `ref`, `computed` and `reactive` properties. The `meta` parameter can also accept a function returning an object to make the entire object reactive. :: ## Type ```ts [Signature] export function useHead (meta: MaybeComputedRef<MetaObject>): ActiveHeadEntry<UseHeadInput> interface MetaObject { title?: string titleTemplate?: string | ((title?: string) => string) base?: Base link?: Link[] meta?: Meta[] style?: Style[] script?: Script[] noscript?: Noscript[] htmlAttrs?: HtmlAttributes bodyAttrs?: BodyAttributes } interface ActiveHeadEntry<Input> { /** * Updates the entry with new input. * * Will first clear any side effects for previous input. */ patch: (input: Input) => void /** * Dispose the entry, removing it from the active head. * * Will queue side effects for removal. */ dispose: () => void } ``` See [@unhead/schema](https://github.com/unjs/unhead/blob/main/packages/vue/src/types/schema.ts) for more detailed types. ## Parameters `meta`: An object accepting head metadata properties to customize the page's `<head>` section. All properties support reactive values (`ref`, `computed`, `reactive`) or can be a function returning the metadata object. | Property | Type | Description | | --------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | `title` | `string` | Sets the page title. | | `titleTemplate` | `string \| ((title?: string) => string)` | Configures a dynamic template to customize the page title. Can be a string with `%s` placeholder or a function. | | `base` | `Base` | Sets the `<base>` tag for the document. | | `link` | `Link[]` | Array of link objects. Each element is mapped to a `<link>` tag, where object properties correspond to HTML attributes. | | `meta` | `Meta[]` | Array of meta objects. Each element is mapped to a `<meta>` tag, where object properties correspond to HTML attributes. | | `style` | `Style[]` | Array of style objects. Each element is mapped to a `<style>` tag, where object properties correspond to HTML attributes. | | `script` | `Script[]` | Array of script objects. Each element is mapped to a `<script>` tag, where object properties correspond to HTML attributes. | | `noscript` | `Noscript[]` | Array of noscript objects. Each element is mapped to a `<noscript>` tag, where object properties correspond to HTML attributes. | | `htmlAttrs` | `HtmlAttributes` | Sets attributes of the `<html>` tag. Each object property is mapped to the corresponding attribute. | | `bodyAttrs` | `BodyAttributes` | Sets attributes of the `<body>` tag. Each object property is mapped to the corresponding attribute. | ## Return Values This composable does not return any value. It registers the head metadata with Unhead, which manages the actual DOM updates. ## Examples ### Basic Meta Tags ```vue [pages/about.vue] <script setup lang="ts"> useHead({ title: 'About Us', meta: [ { name: 'description', content: 'Learn more about our company' }, { property: 'og:title', content: 'About Us' }, { property: 'og:description', content: 'Learn more about our company' }, ], }) </script> ``` ### Reactive Meta Tags ```vue [pages/profile.vue] <script setup lang="ts"> const profile = ref({ name: 'John Doe' }) useHead({ title: computed(() => profile.value.name), meta: [ { name: 'description', content: computed(() => `Profile page for ${profile.value.name}`), }, ], }) </script> ``` ### Using a Function for Full Reactivity ```vue [pages/dynamic.vue] <script setup lang="ts"> const count = ref(0) useHead(() => ({ title: `Count: ${count.value}`, meta: [ { name: 'description', content: `Current count is ${count.value}` }, ], })) </script> ``` ### Adding External Scripts and Styles ```vue [pages/external.vue] <script setup lang="ts"> useHead({ link: [ { rel: 'stylesheet', href: 'https://cdn.example.com/styles.css', }, ], script: [ { src: 'https://cdn.example.com/script.js', async: true, }, ], }) </script> ``` ### Body and HTML Attributes ```vue [pages/themed.vue] <script setup lang="ts"> const isDark = ref(true) useHead({ htmlAttrs: { lang: 'en', class: computed(() => isDark.value ? 'dark' : 'light'), }, bodyAttrs: { class: 'themed-page', }, }) </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/seo-meta"} :: --- - [Source](https://github.com/unjs/unhead/blob/main/packages/vue/src/composables.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useHeadSafe" description: "The recommended way to provide head data with user input." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-head-safe" --- # useHeadSafe > The recommended way to provide head data with user input. ## Usage The `useHeadSafe` composable is a wrapper around the [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) composable that restricts the input to only allow safe values. This is the recommended way to manage head data when working with user input, as it prevents XSS attacks by sanitizing potentially dangerous attributes. ::warning When using `useHeadSafe`, potentially dangerous attributes like `innerHTML` in scripts or `http-equiv` in meta tags are automatically stripped out to prevent XSS attacks. Use this composable whenever you're working with user-generated content. :: ## Type ```ts [Signature] export function useHeadSafe (input: MaybeComputedRef<HeadSafe>): void ``` ### Allowed Attributes The following attributes are whitelisted for each head element type: ```ts const WhitelistAttributes = { htmlAttrs: ['class', 'style', 'lang', 'dir'], bodyAttrs: ['class', 'style'], meta: ['name', 'property', 'charset', 'content', 'media'], noscript: ['textContent'], style: ['media', 'textContent', 'nonce', 'title', 'blocking'], script: ['type', 'textContent', 'nonce', 'blocking'], link: ['color', 'crossorigin', 'fetchpriority', 'href', 'hreflang', 'imagesrcset', 'imagesizes', 'integrity', 'media', 'referrerpolicy', 'rel', 'sizes', 'type'], } ``` See [@unhead/vue](https://github.com/unjs/unhead/blob/main/packages/vue/src/types/safeSchema.ts) for more detailed types. ## Parameters `input`: A `MaybeComputedRef<HeadSafe>` object containing head data. You can pass all the same values as [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head), but only safe attributes will be rendered. ## Return Values This composable does not return any value. ## Example ```vue [pages/user-profile.vue] <script setup lang="ts"> // User-generated content that might contain malicious code const userBio = ref('<script>alert("xss")<' + '/script>') useHeadSafe({ title: `User Profile`, meta: [ { name: 'description', content: userBio.value, // Safely sanitized }, ], }) </script> ``` ::read-more{to="https://unhead.unjs.io/docs/typescript/head/api/composables/use-head-safe" target="_blank"} Read more on the `Unhead` documentation. :: --- - [Source](https://github.com/unjs/unhead/blob/main/packages/vue/src/composables.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useHydration" description: "Allows full control of the hydration cycle to set and receive data from the server." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-hydration" --- # useHydration > Allows full control of the hydration cycle to set and receive data from the server. `useHydration` is a built-in composable that provides a way to set data on the server side every time a new HTTP request is made and receive that data on the client side. This way `useHydration` allows you to take full control of the hydration cycle. ::note This is an advanced composable, primarily designed for use within plugins, mostly used by Nuxt modules. :: ::note `useHydration` is designed to **ensure state synchronization and restoration during SSR**. If you need to create a globally reactive state that is SSR-friendly in Nuxt, [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) is the recommended choice. :: ## Usage The data returned from the `get` function on the server is stored in `nuxtApp.payload` under the unique key provided as the first parameter to `useHydration`. During hydration, this data is then retrieved on the client, preventing redundant computations or API calls. ::code-group ```ts [With useHydration] export default defineNuxtPlugin((nuxtApp) => { const myStore = new MyStore() useHydration( 'myStoreState', () => myStore.getState(), data => myStore.setState(data), ) }) ``` ```ts [Without useHydration] export default defineNuxtPlugin((nuxtApp) => { const myStore = new MyStore() if (import.meta.server) { nuxt.hooks.hook('app:rendered', () => { nuxtApp.payload.myStoreState = myStore.getState() }) } if (import.meta.client) { nuxt.hooks.hook('app:created', () => { myStore.setState(nuxtApp.payload.myStoreState) }) } }) ``` :: ## Type ```ts [Signature] export function useHydration<T> (key: string, get: () => T, set: (value: T) => void): void ``` ## Parameters | Parameter | Type | Description | | --------- | -------------------- | ------------------------------------------------------------------------------------------------------------- | | `key` | `string` | A unique key that identifies the data in your Nuxt application. | | `get` | `() => T` | A function executed **only on the server** (called when SSR rendering is done) to set the initial value. | | `set` | `(value: T) => void` | A function executed **only on the client** (called when initial Vue instance is created) to receive the data. | ## Return Values This composable does not return any value. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/hydrate.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useLazyAsyncData" description: "This wrapper around useAsyncData triggers navigation immediately." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-lazy-async-data" --- # useLazyAsyncData > This wrapper around useAsyncData triggers navigation immediately. `useLazyAsyncData` provides a wrapper around [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) that triggers navigation before the handler is resolved by setting the `lazy` option to `true`. ::note By default, [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) blocks navigation until its async handler is resolved. `useLazyAsyncData` allows navigation to occur immediately while data fetching continues in the background. :: ## Usage ```vue [pages/index.vue] <script setup lang="ts"> const { status, data: posts } = await useLazyAsyncData('posts', () => $fetch('/api/posts')) </script> <template> <div> <div v-if="status === 'pending'"> Loading... </div> <div v-else-if="status === 'error'"> Error loading posts </div> <div v-else> {{ posts }} </div> </div> </template> ``` When using `useLazyAsyncData`, navigation will occur before fetching is complete. This means you must handle `pending` and `error` states directly within your component's template. ::warning `useLazyAsyncData` is a reserved function name transformed by the compiler, so you should not name your own function `useLazyAsyncData`. :: ## Type ```ts [Signature] export function useLazyAsyncData<DataT, ErrorT> ( handler: (ctx?: NuxtApp) => Promise<DataT>, options?: AsyncDataOptions<DataT>, ): AsyncData<DataT, ErrorT> export function useLazyAsyncData<DataT, ErrorT> ( key: string, handler: (ctx?: NuxtApp) => Promise<DataT>, options?: AsyncDataOptions<DataT>, ): AsyncData<DataT, ErrorT> ``` `useLazyAsyncData` has the same signature as [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data). ## Parameters `useLazyAsyncData` accepts the same parameters as [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data), with the `lazy` option automatically set to `true`. ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-async-data#params"} :: ## Return Values `useLazyAsyncData` returns the same values as [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data). ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-async-data#return-values"} :: ## Example ```vue [pages/index.vue] <script setup lang="ts"> /* Navigation will occur before fetching is complete. Handle 'pending' and 'error' states directly within your component's template */ const { status, data: count } = await useLazyAsyncData('count', () => $fetch('/api/count')) watch(count, (newCount) => { // Because count might start out null, you won't have access // to its contents immediately, but you can watch it. }) </script> <template> <div> {{ status === 'pending' ? 'Loading' : count }} </div> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useLazyFetch" description: "This wrapper around useFetch triggers navigation immediately." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-lazy-fetch" --- # useLazyFetch > This wrapper around useFetch triggers navigation immediately. `useLazyFetch` provides a wrapper around [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) that triggers navigation before the handler is resolved by setting the `lazy` option to `true`. ## Usage By default, [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) blocks navigation until its async handler is resolved. `useLazyFetch` allows navigation to proceed immediately, with data being fetched in the background. ```vue [pages/index.vue] <script setup lang="ts"> const { status, data: posts } = await useLazyFetch('/api/posts') </script> <template> <div v-if="status === 'pending'"> Loading ... </div> <div v-else> <div v-for="post in posts"> <!-- do something --> </div> </div> </template> ``` ::note `useLazyFetch` has the same signature as [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch). :: ::warning Awaiting `useLazyFetch` only ensures the call is initialized. On client-side navigation, data may not be immediately available, and you must handle the `pending` state in your component's template. :: ::warning `useLazyFetch` is a reserved function name transformed by the compiler, so you should not name your own function `useLazyFetch`. :: ## Type ```ts [Signature] export function useLazyFetch<DataT, ErrorT> ( url: string | Request | Ref<string | Request> | (() => string | Request), options?: UseFetchOptions<DataT>, ): Promise<AsyncData<DataT, ErrorT>> ``` ::note `useLazyFetch` is equivalent to `useFetch` with `lazy: true` option set. See [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) for full type definitions. :: ## Parameters `useLazyFetch` accepts the same parameters as [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch): - `URL` (`string | Request | Ref<string | Request> | () => string | Request`): The URL or request to fetch. - `options` (object): Same as [`useFetch` options](https://nuxt.com/docs/3.x/api/composables/use-fetch#parameters), with `lazy` automatically set to `true`. ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-fetch#parameters"} :: ## Return Values Returns the same `AsyncData` object as [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch): | Name | Type | Description | | --------- | --------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | `data` | `Ref<DataT \| undefined>` | The result of the asynchronous fetch. | | `refresh` | `(opts?: AsyncDataExecuteOptions) => Promise<void>` | Function to manually refresh the data. | | `execute` | `(opts?: AsyncDataExecuteOptions) => Promise<void>` | Alias for `refresh`. | | `error` | `Ref<ErrorT \| undefined>` | Error object if the data fetching failed. | | `status` | `Ref<'idle' \| 'pending' \| 'success' \| 'error'>` | Status of the data request. | | `pending` | `Ref<boolean>` | Boolean flag indicating whether the current request is in progress. | | `clear` | `() => void` | Resets `data` to `undefined`, `error` to `undefined`, sets `status` to `idle`, and cancels any pending requests. | ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-fetch#return-values"} :: ## Examples ### Handling Pending State ```vue [pages/index.vue] <script setup lang="ts"> /* Navigation will occur before fetching is complete. * Handle 'pending' and 'error' states directly within your component's template */ const { status, data: posts } = await useLazyFetch('/api/posts') watch(posts, (newPosts) => { // Because posts might start out null, you won't have access // to its contents immediately, but you can watch it. }) </script> <template> <div v-if="status === 'pending'"> Loading ... </div> <div v-else> <div v-for="post in posts"> <!-- do something --> </div> </div> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/fetch.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useLoadingIndicator" description: "This composable gives you access to the loading state of the app page." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-loading-indicator" --- # useLoadingIndicator > This composable gives you access to the loading state of the app page. ## Description A composable which returns the loading state of the page. Used by [`<NuxtLoadingIndicator>`](https://nuxt.com/docs/3.x/api/components/nuxt-loading-indicator) and controllable. It hooks into [`page:loading:start`](https://nuxt.com/docs/3.x/api/advanced/hooks#app-hooks-runtime) and [`page:loading:end`](https://nuxt.com/docs/3.x/api/advanced/hooks#app-hooks-runtime) to change its state. ## Parameters - `duration`: Duration of the loading bar, in milliseconds (default `2000`). - `throttle`: Throttle the appearing and hiding, in milliseconds (default `200`). - `estimatedProgress`: By default Nuxt will back off as it approaches 100%. You can provide a custom function to customize the progress estimation, which is a function that receives the duration of the loading bar (above) and the elapsed time. It should return a value between 0 and 100. ## Properties ### `isLoading` - **type**: `Readonly<ShallowRef<boolean>>` - **description**: The loading state ### `error` - **type**: `Readonly<ShallowRef<boolean>>` - **description**: The error state ### `progress` - **type**: `Readonly<ShallowRef<number>>` - **description**: The progress state. From `0` to `100`. ## Methods ### `start()` Set `isLoading` to true and start to increase the `progress` value. `start` accepts a `{ force: true }` option to skip the interval and show the loading state immediately. ### `set()` Set the `progress` value to a specific value. `set` accepts a `{ force: true }` option to skip the interval and show the loading state immediately. ### `finish()` Set the `progress` value to `100`, stop all timers and intervals then reset the loading state `500` ms later. `finish` accepts a `{ force: true }` option to skip the interval before the state is reset, and `{ error: true }` to change the loading bar color and set the error property to true. ### `clear()` Used by `finish()`. Clear all timers and intervals used by the composable. ## Example ```vue <script setup lang="ts"> const { progress, isLoading, start, finish, clear } = useLoadingIndicator({ duration: 2000, throttle: 200, // This is how progress is calculated by default estimatedProgress: (duration, elapsed) => (2 / Math.PI * 100) * Math.atan(elapsed / duration * 100 / 50), }) </script> ``` ```vue <script setup lang="ts"> const { start, set } = useLoadingIndicator() // same as set(0, { force: true }) // set the progress to 0, and show loading immediately start({ force: true }) </script> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/loading-indicator.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useNuxtApp" description: "Access the shared runtime context of the Nuxt Application." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-nuxt-app" --- # useNuxtApp > Access the shared runtime context of the Nuxt Application. `useNuxtApp` is a built-in composable that provides a way to access shared runtime context of Nuxt, also known as the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context), which is available on both client and server side (but not within Nitro routes). It helps you access the Vue app instance, runtime hooks, runtime config variables and internal states, such as `ssrContext` and `payload`. ```vue [app.vue] <script setup lang="ts"> const nuxtApp = useNuxtApp() </script> ``` If runtime context is unavailable in your scope, `useNuxtApp` will throw an exception when called. You can use [`tryUseNuxtApp`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app#tryusenuxtapp) instead for composables that do not require `nuxtApp`, or to simply check if context is available or not without an exception. <!-- note By default, the shared runtime context of Nuxt is namespaced under the [`buildId`](https://nuxt.com/docs/api/nuxt-config#buildid) option. It allows the support of multiple runtime contexts. ## Params - `appName`: an optional application name. If you do not provide it, the Nuxt `buildId` option is used. Otherwise, it must match with an existing `buildId`. --> ## Methods ### `provide (name, value)` `nuxtApp` is a runtime context that you can extend using [Nuxt plugins](https://nuxt.com/docs/3.x/directory-structure/plugins). Use the `provide` function to create Nuxt plugins to make values and helper methods available in your Nuxt application across all composables and components. `provide` function accepts `name` and `value` parameters. ```ts const nuxtApp = useNuxtApp() nuxtApp.provide('hello', name => `Hello ${name}!`) // Prints "Hello name!" console.log(nuxtApp.$hello('name')) ``` As you can see in the example above, `$hello` has become the new and custom part of `nuxtApp` context and it is available in all places where `nuxtApp` is accessible. ### `hook(name, cb)` Hooks available in `nuxtApp` allows you to customize the runtime aspects of your Nuxt application. You can use runtime hooks in Vue.js composables and [Nuxt plugins](https://nuxt.com/docs/3.x/directory-structure/plugins) to hook into the rendering lifecycle. `hook` function is useful for adding custom logic by hooking into the rendering lifecycle at a specific point. `hook` function is mostly used when creating Nuxt plugins. See [Runtime Hooks](https://nuxt.com/docs/3.x/api/advanced/hooks#app-hooks-runtime) for available runtime hooks called by Nuxt. ```ts [plugins/test.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('page:start', () => { /* your code goes here */ }) nuxtApp.hook('vue:error', (..._args) => { console.log('vue:error') // if (import.meta.client) { // console.log(..._args) // } }) }) ``` ### `callHook(name, ...args)` `callHook` returns a promise when called with any of the existing hooks. ```ts await nuxtApp.callHook('my-plugin:init') ``` ## Properties `useNuxtApp()` exposes the following properties that you can use to extend and customize your app and share state, data and variables. ### `vueApp` `vueApp` is the global Vue.js [application instance](https://vuejs.org/api/application#application-api) that you can access through `nuxtApp`. Some useful methods: - [`component()`](https://vuejs.org/api/application#app-component) - Registers a global component if passing both a name string and a component definition, or retrieves an already registered one if only the name is passed. - [`directive()`](https://vuejs.org/api/application#app-directive) - Registers a global custom directive if passing both a name string and a directive definition, or retrieves an already registered one if only the name is passed[(example)](https://nuxt.com/docs/3.x/directory-structure/plugins#vue-directives). - [`use()`](https://vuejs.org/api/application#app-use) - Installs a **[Vue.js Plugin](https://vuejs.org/guide/reusability/plugins)** [(example)](https://nuxt.com/docs/3.x/directory-structure/plugins#vue-plugins). ::read-more{icon="i-simple-icons-vuedotjs" to="https://vuejs.org/api/application.html#application-api"} :: ### `ssrContext` `ssrContext` is generated during server-side rendering and it is only available on the server side. Nuxt exposes the following properties through `ssrContext`: - `url` (string) - Current request url. - `event` ([h3js/h3](https://github.com/h3js/h3) request event) - Access the request & response of the current route. - `payload` (object) - NuxtApp payload object. ### `payload` `payload` exposes data and state variables from server side to client side. The following keys will be available on the client after they have been passed from the server side: - `serverRendered` (boolean) - Indicates if response is server-side-rendered. - `data` (object) - When you fetch the data from an API endpoint using either [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) or [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) , resulting payload can be accessed from the `payload.data`. This data is cached and helps you prevent fetching the same data in case an identical request is made more than once. ::code-group ```vue [app.vue] <script setup lang="ts"> const { data } = await useAsyncData('count', (_nuxtApp, { signal }) => $fetch('/api/count', { signal })) </script> ``` ```ts [server/api/count.ts] export default defineEventHandler((event) => { return { count: 1 } }) ``` :: After fetching the value of `count` using [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) in the example above, if you access `payload.data`, you will see `{ count: 1 }` recorded there. When accessing the same `payload.data` from [`ssrcontext`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app#ssrcontext), you can access the same value on the server side as well. - `state` (object) - When you use [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) composable in Nuxt to set shared state, this state data is accessed through `payload.state.[name-of-your-state]`. ```ts [plugins/my-plugin.ts] export const useColor = () => useState<string>('color', () => 'pink') export default defineNuxtPlugin((nuxtApp) => { if (import.meta.server) { const color = useColor() } }) ``` It is also possible to use more advanced types, such as `ref`, `reactive`, `shallowRef`, `shallowReactive` and `NuxtError`. #### Custom Reducer/Reviver Since [Nuxt v3.4](https://nuxt.com/blog/v3-4#payload-enhancements), it is possible to define your own reducer/reviver for types that are not supported by Nuxt. ::video-accordion{title="Watch a video from Alexander Lichter about serializing payloads, especially with regards to classes" videoId="8w6ffRBs8a4"} :: In the example below, we define a reducer (or a serializer) and a reviver (or deserializer) for the [Luxon](https://moment.github.io/luxon/#/) DateTime class, using a payload plugin. ```ts [plugins/date-time-payload.ts] /** * This kind of plugin runs very early in the Nuxt lifecycle, before we revive the payload. * You will not have access to the router or other Nuxt-injected properties. * * Note that the "DateTime" string is the type identifier and must * be the same on both the reducer and the reviver. */ export default definePayloadPlugin((nuxtApp) => { definePayloadReducer('DateTime', (value) => { return value instanceof DateTime && value.toJSON() }) definePayloadReviver('DateTime', (value) => { return DateTime.fromISO(value) }) }) ``` ### `isHydrating` Use `nuxtApp.isHydrating` (boolean) to check if the Nuxt app is hydrating on the client side. ```ts [components/nuxt-error-boundary.ts] export default defineComponent({ setup (_props, { slots, emit }) { const nuxtApp = useNuxtApp() onErrorCaptured((err) => { if (import.meta.client && !nuxtApp.isHydrating) { // ... } }) }, }) ``` ### `runWithContext` ::note You are likely here because you got a "Nuxt instance unavailable" message. Please use this method sparingly, and report examples that are causing issues, so that it can ultimately be solved at the framework level. :: The `runWithContext` method is meant to be used to call a function and give it an explicit Nuxt context. Typically, the Nuxt context is passed around implicitly and you do not need to worry about this. However, when working with complex `async`/`await` scenarios in middleware/plugins, you can run into instances where the current instance has been unset after an async call. ```ts [middleware/auth.ts] export default defineNuxtRouteMiddleware(async (to, from) => { const nuxtApp = useNuxtApp() let user try { user = await fetchUser() // the Vue/Nuxt compiler loses context here because of the try/catch block. } catch (e) { user = null } if (!user) { // apply the correct Nuxt context to our `navigateTo` call. return nuxtApp.runWithContext(() => navigateTo('/auth')) } }) ``` #### Usage ```ts const result = nuxtApp.runWithContext(() => functionWithContext()) ``` - `functionWithContext`: Any function that requires the context of the current Nuxt application. This context will be correctly applied automatically. `runWithContext` will return whatever is returned by `functionWithContext`. #### A Deeper Explanation of Context Vue.js Composition API (and Nuxt composables similarly) work by depending on an implicit context. During the lifecycle, Vue sets the temporary instance of the current component (and Nuxt temporary instance of nuxtApp) to a global variable and unsets it in same tick. When rendering on the server side, there are multiple requests from different users and nuxtApp running in a same global context. Because of this, Nuxt and Vue immediately unset this global instance to avoid leaking a shared reference between two users or components. What it does mean? The Composition API and Nuxt Composables are only available during lifecycle and in same tick before any async operation: ```ts // --- Vue internal --- const _vueInstance = null const getCurrentInstance = () => _vueInstance // --- // Vue / Nuxt sets a global variable referencing to current component in _vueInstance when calling setup() async function setup () { getCurrentInstance() // Works await someAsyncOperation() // Vue unsets the context in same tick before async operation! getCurrentInstance() // null } ``` The classic solution to this, is caching the current instance on first call to a local variable like `const instance = getCurrentInstance()` and use it in the next composable call but the issue is that any nested composable calls now needs to explicitly accept the instance as an argument and not depend on the implicit context of composition-api. This is design limitation with composables and not an issue per-se. To overcome this limitation, Vue does some behind the scenes work when compiling our application code and restores context after each call for `<script setup>`: ```ts const __instance = getCurrentInstance() // Generated by Vue compiler getCurrentInstance() // Works! await someAsyncOperation() // Vue unsets the context __restoreInstance(__instance) // Generated by Vue compiler getCurrentInstance() // Still works! ``` For a better description of what Vue actually does, see [unjs/unctx#2 (comment)](https://github.com/unjs/unctx/issues/2#issuecomment-942193723). #### Solution This is where `runWithContext` can be used to restore context, similarly to how `<script setup>` works. Nuxt internally uses [unjs/unctx](https://github.com/unjs/unctx) to support composables similar to Vue for plugins and middleware. This enables composables like `navigateTo()` to work without directly passing `nuxtApp` to them - bringing the DX and performance benefits of Composition API to the whole Nuxt framework. Nuxt composables have the same design as the Vue Composition API and therefore need a similar solution to magically do this transform. Check out [unjs/unctx#2](https://github.com/unjs/unctx/issues/2) (proposal), [unjs/unctx#4](https://github.com/unjs/unctx/pull/4) (transform implementation), and [nuxt/framework#3884](https://github.com/nuxt/framework/pull/3884) (Integration to Nuxt). Vue currently only supports async context restoration for `<script setup>` for async/await usage. In Nuxt, the transform support for `defineNuxtPlugin()` and `defineNuxtRouteMiddleware()` was added, which means when you use them Nuxt automatically transforms them with context restoration. #### Remaining Issues The `unjs/unctx` transformation to automatically restore context seems buggy with `try/catch` statements containing `await` which ultimately needs to be solved in order to remove the requirement of the workaround suggested above. #### Native Async Context Using a new experimental feature, it is possible to enable native async context support using [Node.js `AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage) and new unctx support to make async context available **natively** to **any nested async composable** without needing a transform or manual passing/calling with context. ::tip Native async context support works currently in Bun and Node. :: ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/experimental-features#asynccontext"} :: ## tryUseNuxtApp This function works exactly the same as `useNuxtApp`, but returns `null` if context is unavailable instead of throwing an exception. You can use it for composables that do not require `nuxtApp`, or to simply check if context is available or not without an exception. Example usage: ```ts [composable.ts] export function useStandType () { // Always works on the client if (tryUseNuxtApp()) { return useRuntimeConfig().public.STAND_TYPE } else { return process.env.STAND_TYPE } } ``` <!-- ### Params - `appName`: an optional application name. If you do not provide it, the Nuxt `buildId` option is used. Otherwise, it must match with an existing `buildId`. --> --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/nuxt.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useNuxtData" description: "Access the current cached value of data fetching composables." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-nuxt-data" --- # useNuxtData > Access the current cached value of data fetching composables. ::note `useNuxtData` gives you access to the current cached value of [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) , [`useLazyAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-lazy-async-data), [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) and [`useLazyFetch`](https://nuxt.com/docs/3.x/api/composables/use-lazy-fetch) with explicitly provided key. :: ## Usage The `useNuxtData` composable is used to access the current cached value of data-fetching composables such as `useAsyncData`, `useLazyAsyncData`, `useFetch`, and `useLazyFetch`. By providing the key used during the data fetch, you can retrieve the cached data and use it as needed. This is particularly useful for optimizing performance by reusing already-fetched data or implementing features like Optimistic Updates or cascading data updates. To use `useNuxtData`, ensure that the data-fetching composable (`useFetch`, `useAsyncData`, etc.) has been called with an explicitly provided key. ::video-accordion{title="Watch a video from LearnVue about useNuxtData" videoId="e-_u6swXRWk"} :: ## Params - `key`: The unique key that identifies the cached data. This key should match the one used during the original data fetch. ## Return Values - `data`: A reactive reference to the cached data associated with the provided key. If no cached data exists, the value will be `undefined`. This `Ref` automatically updates if the cached data changes, allowing seamless reactivity in your components. ## Example The example below shows how you can use cached data as a placeholder while the most recent data is being fetched from the server. ```vue [pages/posts.vue] <script setup lang="ts"> // We can access same data later using 'posts' key const { data } = await useFetch('/api/posts', { key: 'posts' }) </script> ``` ```vue [pages/posts/[id\\].vue] <script setup lang="ts"> // Access to the cached value of useFetch in posts.vue (parent route) const { data: posts } = useNuxtData('posts') const route = useRoute() const { data } = useLazyFetch(`/api/posts/${route.params.id}`, { key: `post-${route.params.id}`, default () { // Find the individual post from the cache and set it as the default value. return posts.value.find(post => post.id === route.params.id) }, }) </script> ``` ## Optimistic Updates The example below shows how implementing Optimistic Updates can be achieved using useNuxtData. Optimistic Updates is a technique where the user interface is updated immediately, assuming a server operation will succeed. If the operation eventually fails, the UI is rolled back to its previous state. ```vue [pages/todos.vue] <script setup lang="ts"> // We can access same data later using 'todos' key const { data } = await useAsyncData('todos', (_nuxtApp, { signal }) => $fetch('/api/todos', { signal })) </script> ``` ```vue [components/NewTodo.vue] <script setup lang="ts"> const newTodo = ref('') let previousTodos = [] // Access to the cached value of useAsyncData in todos.vue const { data: todos } = useNuxtData('todos') async function addTodo () { await $fetch('/api/addTodo', { method: 'post', body: { todo: newTodo.value, }, onRequest () { // Store the previously cached value to restore if fetch fails. previousTodos = todos.value // Optimistically update the todos. todos.value = [...todos.value, newTodo.value] }, onResponseError () { // Rollback the data if the request failed. todos.value = previousTodos }, async onResponse () { // Invalidate todos in the background if the request succeeded. await refreshNuxtData('todos') }, }) } </script> ``` ## Type ```ts [Signature] export function useNuxtData<DataT = any> (key: string): { data: Ref<DataT | null> } ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "usePreviewMode" description: "Use usePreviewMode to check and control preview mode in Nuxt" canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-preview-mode" --- # `usePreviewMode` Preview mode allows you to see how your changes would be displayed on a live site without revealing them to users. You can use the built-in `usePreviewMode` composable to access and control preview state in Nuxt. If the composable detects preview mode it will automatically force any updates necessary for [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) to rerender preview content. ```ts const { enabled, state } = usePreviewMode() ``` ## Options ### Custom `enable` check You can specify a custom way to enable preview mode. By default the `usePreviewMode` composable will enable preview mode if there is a `preview` param in url that is equal to `true` (for example, `http://localhost:3000?preview=true`). You can wrap the `usePreviewMode` into custom composable, to keep options consistent across usages and prevent any errors. ```ts export function useMyPreviewMode () { const route = useRoute() return usePreviewMode({ shouldEnable: () => { return !!route.query.customPreview }, }) } ``` ### Modify default state `usePreviewMode` will try to store the value of a `token` param from url in state. You can modify this state and it will be available for all [`usePreviewMode`](https://nuxt.com/docs/3.x/api/composables/use-preview-mode) calls. ```ts const data1 = ref('data1') const { enabled, state } = usePreviewMode({ getState: (currentState) => { return { data1, data2: 'data2' } }, }) ``` ::note The `getState` function will append returned values to current state, so be careful not to accidentally overwrite important state. :: ### Customize the `onEnable` and `onDisable` callbacks By default, when `usePreviewMode` is enabled, it will call `refreshNuxtData()` to re-fetch all data from the server. When preview mode is disabled, the composable will attach a callback to call `refreshNuxtData()` to run after a subsequent router navigation. You can specify custom callbacks to be triggered by providing your own functions for the `onEnable` and `onDisable` options. ```ts const { enabled, state } = usePreviewMode({ onEnable: () => { console.log('preview mode has been enabled') }, onDisable: () => { console.log('preview mode has been disabled') }, }) ``` ## Example The example below creates a page where part of a content is rendered only in preview mode. ```vue [pages/some-page.vue] <script setup> const { enabled, state } = usePreviewMode() const { data } = await useFetch('/api/preview', { query: { apiKey: state.token, }, }) </script> <template> <div> Some base content <p v-if="enabled"> Only preview content: {{ state.token }} <br> <button @click="enabled = false"> disable preview mode </button> </p> </div> </template> ``` Now you can generate your site and serve it: ```bash [Terminal] npx nuxt generate npx nuxt preview ``` Then you can see your preview page by adding the query param `preview` to the end of the page you want to see once, for example `http://localhost:3000/?preview=true`. ::note `usePreviewMode` should be tested locally with `nuxt generate` and then `nuxt preview` rather than `nuxt dev`. (The [preview command](https://nuxt.com/docs/3.x/api/commands/preview) is not related to preview mode.) :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/preview.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRequestEvent" description: "Access the incoming request event with the useRequestEvent composable." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-request-event" --- # useRequestEvent > Access the incoming request event with the useRequestEvent composable. Within the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context) you can use `useRequestEvent` to access the incoming request. ```ts // Get underlying request event const event = useRequestEvent() // Get the URL const url = event?.path ``` ::tip In the browser, `useRequestEvent` will return `undefined`. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRequestFetch" description: "Forward the request context and headers for server-side fetch requests with the useRequestFetch composable." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-request-fetch" --- # useRequestFetch > Forward the request context and headers for server-side fetch requests with the useRequestFetch composable. You can use `useRequestFetch` to forward the request context and headers when making server-side fetch requests. When making a client-side fetch request, the browser automatically sends the necessary headers. However, when making a request during server-side rendering, due to security considerations, we need to forward the headers manually. ::note Headers that are **not meant to be forwarded** will **not be included** in the request. These headers include, for example: `transfer-encoding`, `connection`, `keep-alive`, `upgrade`, `expect`, `host`, `accept` :: ::tip The [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) composable uses `useRequestFetch` under the hood to automatically forward the request context and headers. :: ::code-group ```vue [pages/index.vue] <script setup lang="ts"> // This will forward the user's headers to the `/api/cookies` event handler // Result: { cookies: { foo: 'bar' } } const requestFetch = useRequestFetch() const { data: forwarded } = await useAsyncData(() => requestFetch('/api/cookies')) // This will NOT forward anything // Result: { cookies: {} } const { data: notForwarded } = await useAsyncData((_nuxtApp, { signal }) => $fetch('/api/cookies', { signal })) </script> ``` ```ts [server/api/cookies.ts] export default defineEventHandler((event) => { const cookies = parseCookies(event) return { cookies } }) ``` :: ::tip In the browser during client-side navigation, `useRequestFetch` will behave just like regular [`$fetch`](https://nuxt.com/docs/3.x/api/utils/dollarfetch). :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRequestHeader" description: "Use useRequestHeader to access a certain incoming request header." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-request-header" --- # useRequestHeader > Use useRequestHeader to access a certain incoming request header. You can use the built-in [`useRequestHeader`](https://nuxt.com/docs/3.x/api/composables/use-request-header) composable to access any incoming request header within your pages, components, and plugins. ```ts // Get the authorization request header const authorization = useRequestHeader('authorization') ``` ::tip In the browser, `useRequestHeader` will return `undefined`. :: ## Example We can use `useRequestHeader` to easily figure out if a user is authorized or not. The example below reads the `authorization` request header to find out if a person can access a restricted resource. ```ts [middleware/authorized-only.ts] export default defineNuxtRouteMiddleware((to, from) => { if (!useRequestHeader('authorization')) { return navigateTo('/not-authorized') } }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRequestHeaders" description: "Use useRequestHeaders to access the incoming request headers." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-request-headers" --- # useRequestHeaders > Use useRequestHeaders to access the incoming request headers. You can use built-in [`useRequestHeaders`](https://nuxt.com/docs/3.x/api/composables/use-request-headers) composable to access the incoming request headers within your pages, components, and plugins. ```ts // Get all request headers const headers = useRequestHeaders() // Get only cookie request header const { cookie } = useRequestHeaders(['cookie']) ``` ::tip In the browser, `useRequestHeaders` will return an empty object. :: ## Example We can use `useRequestHeaders` to access and proxy the initial request's `authorization` header to any future internal requests during SSR. The example below adds the `authorization` request header to an isomorphic `$fetch` call. ```vue [pages/some-page.vue] <script setup lang="ts"> const { data } = await useFetch('/api/confidential', { headers: useRequestHeaders(['authorization']), }) </script> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRequestURL" description: "Access the incoming request URL with the useRequestURL composable." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-request-url" --- # useRequestURL > Access the incoming request URL with the useRequestURL composable. `useRequestURL` is a helper function that returns an [URL object](https://developer.mozilla.org/en-US/docs/Web/API/URL/URL) working on both server-side and client-side. ::important When utilizing [Hybrid Rendering](https://nuxt.com/docs/3.x/guide/concepts/rendering#hybrid-rendering) with cache strategies, all incoming request headers are dropped when handling the cached responses via the [Nitro caching layer](https://nitro.build/guide/cache) (meaning `useRequestURL` will return `localhost` for the `host`). You can define the [`cache.varies` option](https://nitro.build/guide/cache#options) to specify headers that will be considered when caching and serving the responses, such as `host` and `x-forwarded-host` for multi-tenant environments. :: ::code-group ```vue [pages/about.vue] <script setup lang="ts"> const url = useRequestURL() </script> <template> <p>URL is: {{ url }}</p> <p>Path is: {{ url.pathname }}</p> </template> ``` ```html [Result in development] <p>URL is: http://localhost:3000/about</p> <p>Path is: /about</p> ``` :: ::tip{icon="i-simple-icons-mdnwebdocs" to="https://developer.mozilla.org/en-US/docs/Web/API/URL#instance_properties" target="_blank"} Read about the URL instance properties on the MDN documentation. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/url.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useResponseHeader" description: "Use useResponseHeader to set a server response header." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-response-header" --- # useResponseHeader > Use useResponseHeader to set a server response header. ::important This composable is available in Nuxt v3.14+. :: You can use the built-in [`useResponseHeader`](https://nuxt.com/docs/3.x/api/composables/use-response-header) composable to set any server response header within your pages, components, and plugins. ```ts // Set a custom response header const header = useResponseHeader('X-My-Header') header.value = 'my-value' ``` ## Example We can use `useResponseHeader` to easily set a response header on a per-page basis. ```vue [pages/test.vue] <script setup> // pages/test.vue const header = useResponseHeader('X-My-Header') header.value = 'my-value' </script> <template> <h1>Test page with custom header</h1> <p>The response from the server for this "/test" page will have a custom "X-My-Header" header.</p> </template> ``` We can use `useResponseHeader` for example in Nuxt [middleware](https://nuxt.com/docs/3.x/directory-structure/middleware) to set a response header for all pages. ```ts [middleware/my-header-middleware.ts] export default defineNuxtRouteMiddleware((to, from) => { const header = useResponseHeader('X-My-Always-Header') header.value = `I'm Always here!` }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRoute" description: "The useRoute composable returns the current route." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-route" --- # useRoute > The useRoute composable returns the current route. ::note Within the template of a Vue component, you can access the route using `$route`. :: The `useRoute` composable is a wrapper around the identically named composable from `vue-router`, providing access to the current route in a Nuxt application. The key difference is that in Nuxt, the composable ensures that the route is updated **only after** the page content has changed after navigation. In contrast, the `vue-router` version updates the route **immediately**, which can lead to synchronization issues between different parts of the template that rely on the route metadata, for example. ## Example In the following example, we call an API via [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) using a dynamic page parameter - `slug` - as part of the URL. ```html [~/pages/[slug\\].vue] <script setup lang="ts"> const route = useRoute() const { data: mountain } = await useFetch(`/api/mountains/${route.params.slug}`) </script> <template> <div> <h1>{{ mountain.title }}</h1> <p>{{ mountain.description }}</p> </div> </template> ``` If you need to access the route query parameters (for example `example` in the path `/test?example=true`), then you can use `useRoute().query` instead of `useRoute().params`. ## API Apart from dynamic parameters and query parameters, `useRoute()` also provides the following computed references related to the current route: - `fullPath`: encoded URL associated with the current route that contains path, query and hash - `hash`: decoded hash section of the URL that starts with a # - `query`: access route query parameters - `matched`: array of normalized matched routes with current route location - `meta`: custom data attached to the record - `name`: unique name for the route record - `path`: encoded pathname section of the URL - `redirectedFrom`: route location that was attempted to access before ending up on the current route location ## Common Pitfalls ### Route Synchronization Issues It’s important to use the `useRoute()` composable from Nuxt rather than the one from `vue-router` to avoid synchronization issues during page navigation. Importing `useRoute` directly from `vue-router` bypasses Nuxt's implementation. ```ts twoslash // ❌ do not use `useRoute` from `vue-router` // @errors: 2300 import { useRoute } from 'vue-router' // ✅ use Nuxt's `useRoute` composable import { useRoute } from '#app' ``` ### Calling `useRoute` in Middleware Using `useRoute` in middleware is not recommended because it can lead to unexpected behavior. There is no concept of a "current route" in middleware. The `useRoute()` composable should only be used in the setup function of a Vue component or in a Nuxt plugin. ::warning This applies to any composable that uses `useRoute()` internally too. :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/middleware"} Read more about accessing the route in the middleware section. :: ### Hydration Issues with `route.fullPath` Browsers don't send [URL fragments](https://url.spec.whatwg.org/#concept-url-fragment) (for example `#foo`) when making requests. So using `route.fullPath` to affect the template can trigger hydration issues because this will include the fragment on client but not the server. ::read-more{icon="i-simple-icons-vuedotjs" to="https://router.vuejs.org/api/type-aliases/RouteLocationNormalizedLoaded.html"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRouteAnnouncer" description: "This composable observes the page title changes and updates the announcer message accordingly." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-route-announcer" --- # useRouteAnnouncer > This composable observes the page title changes and updates the announcer message accordingly. ::important This composable is available in Nuxt v3.12+. :: ## Description A composable which observes the page title changes and updates the announcer message accordingly. Used by [`<NuxtRouteAnnouncer>`](https://nuxt.com/docs/3.x/api/components/nuxt-route-announcer) and controllable. It hooks into Unhead's `dom:rendered` hook to read the page's title and set it as the announcer message. ## Parameters - `politeness`: Sets the urgency for screen reader announcements: `off` (disable the announcement), `polite` (waits for silence), or `assertive` (interrupts immediately). (default `polite`). ## Properties ### `message` - **type**: `Ref<string>` - **description**: The message to announce ### `politeness` - **type**: `Ref<string>` - **description**: Screen reader announcement urgency level `off`, `polite`, or `assertive` ## Methods ### `set(message, politeness = "polite")` Sets the message to announce with its urgency level. ### `polite(message)` Sets the message with `politeness = "polite"` ### `assertive(message)` Sets the message with `politeness = "assertive"` ## Example ```vue [pages/index.vue] <script setup lang="ts"> const { message, politeness, set, polite, assertive } = useRouteAnnouncer({ politeness: 'assertive', }) </script> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/route-announcer.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRouter" description: "The useRouter composable returns the router instance." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-router" --- # useRouter > The useRouter composable returns the router instance. ```vue [pages/index.vue] <script setup lang="ts"> const router = useRouter() </script> ``` If you only need the router instance within your template, use `$router`: ```vue [pages/index.vue] <template> <button @click="$router.back()"> Back </button> </template> ``` If you have a `pages/` directory, `useRouter` is identical in behavior to the one provided by `vue-router`. ::read-more{icon="i-simple-icons-vuedotjs" to="https://router.vuejs.org/api/interfaces/router#Properties-currentRoute-" target="_blank"} Read `vue-router` documentation about the `Router` interface. :: ## Basic Manipulation - [`addRoute()`](https://router.vuejs.org/api/interfaces/router#addRoute-): Add a new route to the router instance. `parentName` can be provided to add new route as the child of an existing route. - [`removeRoute()`](https://router.vuejs.org/api/interfaces/router#removeRoute-): Remove an existing route by its name. - [`getRoutes()`](https://router.vuejs.org/api/interfaces/router#getRoutes-): Get a full list of all the route records. - [`hasRoute()`](https://router.vuejs.org/api/interfaces/router#hasRoute-): Checks if a route with a given name exists. - [`resolve()`](https://router.vuejs.org/api/interfaces/router#resolve-): Returns the normalized version of a route location. Also includes an `href` property that includes any existing base. ```ts [Example] const router = useRouter() router.addRoute({ name: 'home', path: '/home', component: Home }) router.removeRoute('home') router.getRoutes() router.hasRoute('home') router.resolve({ name: 'home' }) ``` ::note `router.addRoute()` adds route details into an array of routes and it is useful while building [Nuxt plugins](https://nuxt.com/docs/3.x/directory-structure/plugins) while `router.push()` on the other hand, triggers a new navigation immediately and it is useful in pages, Vue components and composable. :: ## Based on History API - [`back()`](https://router.vuejs.org/api/interfaces/router#back-): Go back in history if possible, same as `router.go(-1)`. - [`forward()`](https://router.vuejs.org/api/interfaces/router#forward-): Go forward in history if possible, same as `router.go(1)`. - [`go()`](https://router.vuejs.org/api/interfaces/router#go-): Move forward or backward through the history without the hierarchical restrictions enforced in `router.back()` and `router.forward()`. - [`push()`](https://router.vuejs.org/api/interfaces/router#push-): Programmatically navigate to a new URL by pushing an entry in the history stack. **It is recommended to use [`navigateTo`](https://nuxt.com/docs/3.x/api/utils/navigate-to) instead.** - [`replace()`](https://router.vuejs.org/api/interfaces/router#replace-): Programmatically navigate to a new URL by replacing the current entry in the routes history stack. **It is recommended to use [`navigateTo`](https://nuxt.com/docs/3.x/api/utils/navigate-to) instead.** ```ts [Example] const router = useRouter() router.back() router.forward() router.go(3) router.push({ path: '/home' }) router.replace({ hash: '#bio' }) ``` ::read-more{icon="i-simple-icons-mdnwebdocs" to="https://developer.mozilla.org/en-US/docs/Web/API/History" target="_blank"} Read more about the browser's History API. :: ## Navigation Guards `useRouter` composable provides `afterEach`, `beforeEach` and `beforeResolve` helper methods that acts as navigation guards. However, Nuxt has a concept of **route middleware** that simplifies the implementation of navigation guards and provides a better developer experience. ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/middleware"} :: ## Promise and Error Handling - [`isReady()`](https://router.vuejs.org/api/interfaces/router#isReady-): Returns a Promise that resolves when the router has completed the initial navigation. - [`onError`](https://router.vuejs.org/api/interfaces/router#onError-): Adds an error handler that is called every time a non caught error happens during navigation. ::read-more ```yaml [props] icon: i-simple-icons-vuedotjs to: https://router.vuejs.org/api/interfaces/router#Methods- title: Vue Router Docs target: _blank ``` :: ## Universal Router Instance If you do not have a `pages/` folder, then [`useRouter`](https://nuxt.com/docs/3.x/api/composables/use-router) will return a universal router instance with similar helper methods, but be aware that not all features may be supported or behave in exactly the same way as with `vue-router`. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRuntimeConfig" description: "Access runtime config variables with the useRuntimeConfig composable." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-runtime-config" --- # useRuntimeConfig > Access runtime config variables with the useRuntimeConfig composable. ## Usage ```vue [app.vue] <script setup lang="ts"> const config = useRuntimeConfig() </script> ``` ```ts [server/api/foo.ts] export default defineEventHandler((event) => { const config = useRuntimeConfig(event) }) ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/runtime-config"} :: ## Define Runtime Config The example below shows how to set a public API base URL and a secret API token that is only accessible on the server. We should always define `runtimeConfig` variables inside `nuxt.config`. ```ts [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { // Private keys are only available on the server apiSecret: '123', // Public keys that are exposed to the client public: { apiBase: process.env.NUXT_PUBLIC_API_BASE || '/api', }, }, }) ``` ::note Variables that need to be accessible on the server are added directly inside `runtimeConfig`. Variables that need to be accessible on both the client and the server are defined in `runtimeConfig.public`. :: ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/runtime-config"} :: ## Access Runtime Config To access runtime config, we can use `useRuntimeConfig()` composable: ```ts [server/api/test.ts] export default defineEventHandler(async (event) => { const config = useRuntimeConfig(event) // Access public variables const result = await $fetch(`/test`, { baseURL: config.public.apiBase, headers: { // Access a private variable (only available on the server) Authorization: `Bearer ${config.apiSecret}`, }, }) return result }) ``` In this example, since `apiBase` is defined within the `public` namespace, it is universally accessible on both server and client-side, while `apiSecret` **is only accessible on the server-side**. ## Environment Variables It is possible to update runtime config values using a matching environment variable name prefixed with `NUXT_`. ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/runtime-config"} :: ### Using the `.env` File We can set the environment variables inside the `.env` file to make them accessible during **development** and **build/generate**. ```ini [.env] NUXT_PUBLIC_API_BASE = "https://api.localhost:5555" NUXT_API_SECRET = "123" ``` ::note Any environment variables set within `.env` file are accessed using `process.env` in the Nuxt app during **development** and **build/generate**. :: ::warning In **production runtime**, you should use platform environment variables and `.env` is not used. :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/env"} :: ## `app` namespace Nuxt uses `app` namespace in runtime-config with keys including `baseURL` and `cdnURL`. You can customize their values at runtime by setting environment variables. ::note This is a reserved namespace. You should not introduce additional keys inside `app`. :: ### `app.baseURL` By default, the `baseURL` is set to `'/'`. However, the `baseURL` can be updated at runtime by setting the `NUXT_APP_BASE_URL` as an environment variable. Then, you can access this new base URL using `config.app.baseURL`: ```ts [/plugins/my-plugin.ts] export default defineNuxtPlugin((NuxtApp) => { const config = useRuntimeConfig() // Access baseURL universally const baseURL = config.app.baseURL }) ``` ### `app.cdnURL` This example shows how to set a custom CDN url and access them using `useRuntimeConfig()`. You can use a custom CDN for serving static assets inside `.output/public` using the `NUXT_APP_CDN_URL` environment variable. And then access the new CDN url using `config.app.cdnURL`. ```ts [server/api/foo.ts] export default defineEventHandler((event) => { const config = useRuntimeConfig(event) // Access cdnURL universally const cdnURL = config.app.cdnURL }) ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/runtime-config"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/nuxt.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useRuntimeHook" description: "Registers a runtime hook in a Nuxt application and ensures it is properly disposed of when the scope is destroyed." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-runtime-hook" --- # useRuntimeHook > Registers a runtime hook in a Nuxt application and ensures it is properly disposed of when the scope is destroyed. ::important This composable is available in Nuxt v3.14+. :: ```ts [signature] function useRuntimeHook<THookName extends keyof RuntimeNuxtHooks> ( name: THookName, fn: RuntimeNuxtHooks[THookName] extends HookCallback ? RuntimeNuxtHooks[THookName] : never, ): void ``` ## Usage ### Parameters - `name`: The name of the runtime hook to register. You can see the full list of [runtime Nuxt hooks here](https://nuxt.com/docs/3.x/api/advanced/hooks#app-hooks-runtime). - `fn`: The callback function to execute when the hook is triggered. The function signature varies based on the hook name. ### Returns The composable doesn't return a value, but it automatically unregisters the hook when the component's scope is destroyed. ## Example ```vue twoslash [pages/index.vue] <script setup lang="ts"> // Register a hook that runs every time a link is prefetched, but which will be // automatically cleaned up (and not called again) when the component is unmounted useRuntimeHook('link:prefetch', (link) => { console.log('Prefetching', link) }) </script> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/runtime-hook.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useSeoMeta" description: "The useSeoMeta composable lets you define your site's SEO meta tags as a flat object with full TypeScript support." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-seo-meta" --- # useSeoMeta > The useSeoMeta composable lets you define your site's SEO meta tags as a flat object with full TypeScript support. This helps you avoid common mistakes, such as using `name` instead of `property`, as well as typos - with over 100+ meta tags fully typed. ::important This is the recommended way to add meta tags to your site as it is XSS safe and has full TypeScript support. :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/seo-meta"} :: ## Usage ```vue [app.vue] <script setup lang="ts"> useSeoMeta({ title: 'My Amazing Site', ogTitle: 'My Amazing Site', description: 'This is my amazing site, let me tell you all about it.', ogDescription: 'This is my amazing site, let me tell you all about it.', ogImage: 'https://example.com/image.png', twitterCard: 'summary_large_image', }) </script> ``` When inserting tags that are reactive, you should use the computed getter syntax (`() => value`): ```vue [app.vue] <script setup lang="ts"> const title = ref('My title') useSeoMeta({ title, description: () => `This is a description for the ${title.value} page`, }) </script> ``` ## Parameters There are over 100 parameters. See the [full list of parameters in the source code](https://github.com/harlan-zw/zhead/blob/main/packages/zhead/src/metaFlat.ts#L1035). ::read-more{to="https://nuxt.com/docs/3.x/getting-started/seo-meta"} :: ## Performance In most instances, SEO meta tags don't need to be reactive as search engine robots primarily scan the initial page load. For better performance, you can wrap your `useSeoMeta` calls in a server-only condition when the meta tags don't need to be reactive: ```vue [app.vue] <script setup lang="ts"> if (import.meta.server) { // These meta tags will only be added during server-side rendering useSeoMeta({ robots: 'index, follow', description: 'Static description that does not need reactivity', ogImage: 'https://example.com/image.png', // other static meta tags... }) } const dynamicTitle = ref('My title') // Only use reactive meta tags outside the condition when necessary useSeoMeta({ title: () => dynamicTitle.value, ogTitle: () => dynamicTitle.value, }) </script> ``` This previously used the [`useServerSeoMeta`](https://nuxt.com/docs/3.x/api/composables/use-server-seo-meta) composable, but it has been deprecated in favor of this approach. --- - [Source](https://github.com/unjs/unhead/blob/main/packages/vue/src/composables.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useServerSeoMeta" description: "The useServerSeoMeta composable lets you define your site's SEO meta tags as a flat object with full TypeScript support." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-server-seo-meta" --- # useServerSeoMeta > The useServerSeoMeta composable lets you define your site's SEO meta tags as a flat object with full TypeScript support. Just like [`useSeoMeta`](https://nuxt.com/docs/3.x/api/composables/use-seo-meta), `useServerSeoMeta` composable lets you define your site's SEO meta tags as a flat object with full TypeScript support. ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-seo-meta"} :: In most instances, the meta doesn't need to be reactive as robots will only scan the initial load. So we recommend using [`useServerSeoMeta`](https://nuxt.com/docs/3.x/api/composables/use-server-seo-meta) as a performance-focused utility that will not do anything (or return a `head` object) on the client. ```vue [app.vue] <script setup lang="ts"> useServerSeoMeta({ robots: 'index, follow', }) </script> ``` Parameters are exactly the same as with [`useSeoMeta`](https://nuxt.com/docs/3.x/api/composables/use-seo-meta) ::read-more{to="https://nuxt.com/docs/3.x/getting-started/seo-meta"} :: --- - [Source](https://github.com/unjs/unhead/blob/main/packages/vue/src/composables.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "useState" description: "The useState composable creates a reactive and SSR-friendly shared state." canonical_url: "https://nuxt.com/docs/3.x/api/composables/use-state" --- # useState > The useState composable creates a reactive and SSR-friendly shared state. ## Usage ```ts // Create a reactive state and set default value const count = useState('counter', () => Math.round(Math.random() * 100)) ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/state-management"} :: ::important Because the data inside `useState` will be serialized to JSON, it is important that it does not contain anything that cannot be serialized, such as classes, functions or symbols. :: ::warning `useState` is a reserved function name transformed by the compiler, so you should not name your own function `useState`. :: ::video-accordion{title="Watch a video from Alexander Lichter about why and when to use useState" videoId="mv0WcBABcIk"} :: ## Using `shallowRef` If you don't need your state to be deeply reactive, you can combine `useState` with [`shallowRef`](https://vuejs.org/api/reactivity-advanced#shallowref). This can improve performance when your state contains large objects and arrays. ```ts const state = useState('my-shallow-state', () => shallowRef({ deep: 'not reactive' })) // isShallow(state) === true ``` ## Type ```ts [Signature] export function useState<T> (init?: () => T | Ref<T>): Ref<T> export function useState<T> (key: string, init?: () => T | Ref<T>): Ref<T> ``` - `key`: A unique key ensuring that data fetching is properly de-duplicated across requests. If you do not provide a key, then a key that is unique to the file and line number of the instance of [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) will be generated for you. - `init`: A function that provides initial value for the state when not initiated. This function can also return a `Ref`. - `T`: (typescript only) Specify the type of state ## Troubleshooting ### `Cannot stringify arbitrary non-POJOs` This error occurs when you try to store a non-serializable payload with `useState`, such as class instances. If you want to store class instances with `useState` that are not supported by Nuxt, you can use [`definePayloadPlugin`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app#custom-reducerreviver) to add a custom serializer and deserializer for your classes. ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-nuxt-app#payload"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/state.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Auto-imports" description: "Nuxt Kit provides a set of utilities to help you work with auto-imports. These functions allow you to register your own utils, composables and Vue APIs." canonical_url: "https://nuxt.com/docs/3.x/api/kit/autoimports" --- # Auto-imports > Nuxt Kit provides a set of utilities to help you work with auto-imports. These functions allow you to register your own utils, composables and Vue APIs. Nuxt auto-imports helper functions, composables and Vue APIs to use across your application without explicitly importing them. Based on the directory structure, every Nuxt application can also use auto-imports for its own composables and plugins. With Nuxt Kit you can also add your own auto-imports. `addImports` and `addImportsDir` allow you to add imports to the Nuxt application. `addImportsSources` allows you to add listed imports from 3rd party packages to the Nuxt application. These utilities are powered by [`unimport`](https://github.com/unjs/unimport), which provides the underlying auto-import mechanism used in Nuxt. ::note These functions are designed for registering your own utils, composables and Vue APIs. For pages, components and plugins, please refer to the specific sections: [Pages](https://nuxt.com/docs/3.x/api/kit/pages), [Components](https://nuxt.com/docs/3.x/api/kit/components), [Plugins](https://nuxt.com/docs/3.x/api/kit/plugins). :: ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/expanding-nuxt-s-auto-imports?friend=nuxt" target="_blank"} Watch Vue School video about Auto-imports Nuxt Kit utilities. :: ## `addImports` Add imports to the Nuxt application. It makes your imports available in the Nuxt app context without the need to import them manually. ::tip To add imports for the Nitro server context, refer to the [`addServerImports`](https://nuxt.com/docs/3.x/api/kit/nitro#addserverimports) function. :: ### Usage ```ts twoslash import { addImports, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { const names = [ 'useStoryblok', 'useStoryblokApi', 'useStoryblokBridge', 'renderRichText', 'RichTextSchema', ] names.forEach(name => addImports({ name, as: name, from: '@storyblok/vue' }), ) }, }) ``` ### Type ```ts function addImports (imports: Import | Import[]): void ``` ### Parameters `imports`: An object or an array of objects with the following properties: | Property | Type | Required | Description | | ---------- | --------------------- | -------- | --------------------------------------------------------------------------------------------------------------- | | `name` | `string` | `true` | Import name to be detected. | | `from` | `string` | `true` | Module specifier to import from. | | `priority` | `number` | `false` | Priority of the import; if multiple imports have the same name, the one with the highest priority will be used. | | `disabled` | `boolean` | `false` | If this import is disabled. | | `meta` | `Record<string, any>` | `false` | Metadata of the import. | | `type` | `boolean` | `false` | If this import is a pure type import. | | `typeFrom` | `string` | `false` | Use this as the `from` value when generating type declarations. | | `as` | `string` | `false` | Import as this name. | ## `addImportsDir` Add imports from a directory to the Nuxt application. It will automatically import all files from the directory and make them available in the Nuxt application without the need to import them manually. ### Usage ```ts twoslash import { addImportsDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: '@vueuse/motion', configKey: 'motion', }, setup (options, nuxt) { const resolver = createResolver(import.meta.url) addImportsDir(resolver.resolve('./runtime/composables')) }, }) ``` ### Type ```ts function addImportsDir (dirs: string | string[], options?: { prepend?: boolean }): void ``` ### Parameters | Property | Type | Required | Description | | --------- | ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- | | `dirs` | `string \| string[]`{lang="ts"} | `true` | A string or an array of strings with the path to the directory to import from. | | `options` | `{ prepend?: boolean }`{lang="ts"} | `false` | Options to pass to the import. If `prepend` is set to `true`, the imports will be prepended to the list of imports. | ## `addImportsSources` Add listed imports to the Nuxt application. ### Usage ```ts twoslash import { addImportsSources, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addImportsSources([ { package: '@vueuse/core' }, { from: 'h3', imports: [ 'defineEventHandler', 'getQuery', 'getRouterParams', 'readBody', 'sendRedirect', ], }, ]) }, }) ``` ### Type ```ts function addImportsSources (importSources: Preset | Preset[]): void ``` ### Parameters **importSources**: An object or an array of objects with the following properties: - InlinePreset | Property | Type | Required | Description | | --------- | ------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------- | | `from` | `string` | `true` | Module specifier to import from. | | `imports` | `PresetImport \| ImportSource[]`{lang="ts"} | `true` | An object or an array of objects, which can be import names, import objects or import sources. | - PackagePreset | Property | Type | Required | Description | | --------- | -------- | -------- | -------------------- | | `package` | `string` | `true` | Name of the package. | --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/imports.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Builder" description: "Nuxt Kit provides a set of utilities to help you work with the builder. These functions allow you to extend the Vite and webpack configurations." canonical_url: "https://nuxt.com/docs/3.x/api/kit/builder" --- # Builder > Nuxt Kit provides a set of utilities to help you work with the builder. These functions allow you to extend the Vite and webpack configurations. Nuxt have builders based on [Vite](https://github.com/nuxt/nuxt/tree/main/packages/vite) and [webpack](https://github.com/nuxt/nuxt/tree/main/packages/webpack). You can extend the config passed to each one using `extendViteConfig` and `extendWebpackConfig` functions. You can also add additional plugins via `addVitePlugin`, `addWebpackPlugin` and `addBuildPlugin`. ## `extendViteConfig` Extends the Vite configuration. Callback function can be called multiple times, when applying to both client and server builds. ::warning This hook is now deprecated, and we recommend using a Vite plugin instead with a `config` hook, or — for environment-specific configuration — the `applyToEnvironment` hook. :: ### Usage ```ts twoslash import { defineNuxtModule, extendViteConfig } from '@nuxt/kit' export default defineNuxtModule({ setup () { extendViteConfig((config) => { config.optimizeDeps ||= {} config.optimizeDeps.include ||= [] config.optimizeDeps.include.push('cross-fetch') }) }, }) ``` For environment-specific configuration in Nuxt 5+, use `addVitePlugin()` instead: ```ts twoslash import { addVitePlugin, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { // For global configuration (affects all environments) addVitePlugin(() => ({ name: 'my-global-plugin', config (config) { // This runs before environment setup config.optimizeDeps ||= {} config.optimizeDeps.include ||= [] config.optimizeDeps.include.push('cross-fetch') }, })) // For environment-specific configuration addVitePlugin(() => ({ name: 'my-client-plugin', applyToEnvironment (environment) { return environment.name === 'client' }, configEnvironment (name, config) { // This only affects the client environment config.optimizeDeps ||= {} config.optimizeDeps.include ||= [] config.optimizeDeps.include.push('client-only-package') }, })) }, }) ``` ::warning **Important:** The `config` hook runs before `applyToEnvironment` and modifies the global configuration. Use `configEnvironment` for environment-specific configuration changes. :: ### Type ```ts twoslash // @errors: 2391 import type { UserConfig as ViteConfig } from 'vite' import type { ExtendViteConfigOptions } from '@nuxt/kit' // ---cut--- function extendViteConfig (callback: ((config: ViteConfig) => void), options?: ExtendViteConfigOptions): void ``` ::read-more{to="https://vite.dev/config/" target="_blank" icon="i-simple-icons-vite"} Check out the Vite website for more information about its configuration. :: ### Parameters **`callback`**: A callback function that will be called with the Vite configuration object. **`options`**: Options to pass to the callback function. This object can have the following properties: | Property | Type | Required | Description | | --------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `dev` | `boolean` | `false` | If set to `true`, the callback function will be called when building in development mode. | | `build` | `boolean` | `false` | If set to `true`, the callback function will be called when building in production mode. | | `server` | `boolean` | `false` | If set to `true`, the callback function will be called when building the server bundle. **Deprecated in Nuxt 5+.** Use `addVitePlugin()` with `applyToEnvironment()` instead. | | `client` | `boolean` | `false` | If set to `true`, the callback function will be called when building the client bundle. **Deprecated in Nuxt 5+.** Use `addVitePlugin()` with `applyToEnvironment()` instead. | | `prepend` | `boolean` | `false` | If set to `true`, the callback function will be prepended to the array with `unshift()` instead of `push()`. | ## `extendWebpackConfig` Extends the webpack configuration. Callback function can be called multiple times, when applying to both client and server builds. ### Usage ```ts twoslash import { defineNuxtModule, extendWebpackConfig } from '@nuxt/kit' export default defineNuxtModule({ setup () { extendWebpackConfig((config) => { config.module!.rules!.push({ test: /\.txt$/, use: 'raw-loader', }) }) }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { Configuration as WebpackConfig } from 'webpack' import type { ExtendWebpackConfigOptions } from '@nuxt/kit' // ---cut--- function extendWebpackConfig (callback: ((config: WebpackConfig) => void), options?: ExtendWebpackConfigOptions): void ``` ::read-more{to="https://webpack.js.org/configuration/" target="_blank" icon="i-simple-icons-webpack"} Check out webpack website for more information about its configuration. :: ### Parameters **`callback`**: A callback function that will be called with the webpack configuration object. **`options`**: Options to pass to the callback function. This object can have the following properties: | Property | Type | Required | Description | | --------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------ | | `dev` | `boolean` | `false` | If set to `true`, the callback function will be called when building in development mode. | | `build` | `boolean` | `false` | If set to `true`, the callback function will be called when building in production mode. | | `server` | `boolean` | `false` | If set to `true`, the callback function will be called when building the server bundle. | | `client` | `boolean` | `false` | If set to `true`, the callback function will be called when building the client bundle. | | `prepend` | `boolean` | `false` | If set to `true`, the callback function will be prepended to the array with `unshift()` instead of `push()`. | ## `addVitePlugin` Append Vite plugin to the config. ::warning In Nuxt 5+, plugins registered with `server: false` or `client: false` options will not have their `config` or `configResolved` hooks called. Instead, use the `applyToEnvironment()` method instead for environment-specific plugins. :: ### Usage ```ts twoslash // @errors: 2307 // ---cut--- import { addVitePlugin, defineNuxtModule } from '@nuxt/kit' import { svg4VuePlugin } from 'vite-plugin-svg4vue' export default defineNuxtModule({ meta: { name: 'nuxt-svg-icons', configKey: 'nuxtSvgIcons', }, defaults: { svg4vue: { assetsDirName: 'assets/icons', }, }, setup (options) { addVitePlugin(svg4VuePlugin(options.svg4vue)) // or, to add a vite plugin to only one environment addVitePlugin(() => ({ name: 'my-client-plugin', applyToEnvironment (environment) { return environment.name === 'client' }, // ... rest of your client-only plugin })) }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { Plugin as VitePlugin } from 'vite' import type { ExtendViteConfigOptions } from '@nuxt/kit' // ---cut--- function addVitePlugin (pluginOrGetter: VitePlugin | VitePlugin[] | (() => VitePlugin | VitePlugin[]), options?: ExtendViteConfigOptions): void ``` ::tip See [Vite website](https://vite.dev/guide/api-plugin) for more information about Vite plugins. You can also use [this repository](https://github.com/vitejs/awesome-vite#plugins) to find a plugin that suits your needs. :: ### Parameters **`pluginOrGetter`**: A Vite plugin instance or an array of Vite plugin instances. If a function is provided, it must return a Vite plugin instance or an array of Vite plugin instances. The function can also be async or return a Promise, which is useful for lazy-loading plugins: ```ts twoslash // @errors: 2307 import { addVitePlugin, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { // Lazy load the plugin - only imported when the build actually runs addVitePlugin(() => import('my-vite-plugin').then(r => r.default())) }, }) ``` **`options`**: Options to pass to the callback function. This object can have the following properties: | Property | Type | Required | Description | | --------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | | `dev` | `boolean` | `false` | If set to `true`, the callback function will be called when building in development mode. | | `build` | `boolean` | `false` | If set to `true`, the callback function will be called when building in production mode. | | `server` | `boolean` | `false` | If set to `true`, the callback function will be called when building the server bundle. **Deprecated in Nuxt 5+.** Use `applyToEnvironment()` instead. | | `client` | `boolean` | `false` | If set to `true`, the callback function will be called when building the client bundle. **Deprecated in Nuxt 5+.** Use `applyToEnvironment()` instead. | | `prepend` | `boolean` | `false` | If set to `true`, the callback function will be prepended to the array with `unshift()` instead of `push()`. | ## `addWebpackPlugin` Append webpack plugin to the config. ### Usage ```ts import EslintWebpackPlugin from 'eslint-webpack-plugin' import { addWebpackPlugin, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'nuxt-eslint', configKey: 'eslint', }, defaults: nuxt => ({ include: [`${nuxt.options.srcDir}/**/*.{js,jsx,ts,tsx,vue}`], lintOnStart: true, }), setup (options, nuxt) { const webpackOptions = { ...options, context: nuxt.options.srcDir, files: options.include, lintDirtyModulesOnly: !options.lintOnStart, } addWebpackPlugin(new EslintWebpackPlugin(webpackOptions), { server: false }) }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { WebpackPluginInstance } from 'webpack' import type { ExtendWebpackConfigOptions } from '@nuxt/kit' // ---cut--- function addWebpackPlugin (pluginOrGetter: WebpackPluginInstance | WebpackPluginInstance[] | (() => WebpackPluginInstance | WebpackPluginInstance[]), options?: ExtendWebpackConfigOptions): void ``` ::tip See [webpack website](https://webpack.js.org/concepts/plugins/) for more information about webpack plugins. You can also use [this collection](https://webpack.js.org/awesome-webpack/#webpack-plugins) to find a plugin that suits your needs. :: ### Parameters **`pluginOrGetter`**: A webpack plugin instance or an array of webpack plugin instances. If a function is provided, it must return a webpack plugin instance or an array of webpack plugin instances. The function can also be async or return a Promise, enabling lazy-loading of plugins. **`options`**: Options to pass to the callback function. This object can have the following properties: | Property | Type | Required | Description | | --------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------ | | `dev` | `boolean` | `false` | If set to `true`, the callback function will be called when building in development mode. | | `build` | `boolean` | `false` | If set to `true`, the callback function will be called when building in production mode. | | `server` | `boolean` | `false` | If set to `true`, the callback function will be called when building the server bundle. | | `client` | `boolean` | `false` | If set to `true`, the callback function will be called when building the client bundle. | | `prepend` | `boolean` | `false` | If set to `true`, the callback function will be prepended to the array with `unshift()` instead of `push()`. | ## `addBuildPlugin` Builder-agnostic version of `addVitePlugin` and `addWebpackPlugin`. It will add the plugin to both Vite and webpack configurations if they are present. ### Type ```ts twoslash // @errors: 2391 import type { ExtendConfigOptions } from '@nuxt/kit' import type { Plugin as VitePlugin } from 'vite' import type { WebpackPluginInstance } from 'webpack' import type { RspackPluginInstance } from '@rspack/core' interface AddBuildPluginFactory { vite?: () => VitePlugin | VitePlugin[] webpack?: () => WebpackPluginInstance | WebpackPluginInstance[] rspack?: () => RspackPluginInstance | RspackPluginInstance[] } // ---cut--- function addBuildPlugin (pluginFactory: AddBuildPluginFactory, options?: ExtendConfigOptions): void ``` ### Parameters **`pluginFactory`**: A factory function that returns an object with `vite` and/or `webpack` properties. These properties must be functions that return a Vite plugin instance or an array of Vite plugin instances and/or a webpack plugin instance or an array of webpack plugin instances. **`options`**: Options to pass to the callback function. This object can have the following properties: | Property | Type | Required | Description | | --------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------ | | `dev` | `boolean` | `false` | If set to `true`, the callback function will be called when building in development mode. | | `build` | `boolean` | `false` | If set to `true`, the callback function will be called when building in production mode. | | `server` | `boolean` | `false` | If set to `true`, the callback function will be called when building the server bundle. | | `client` | `boolean` | `false` | If set to `true`, the callback function will be called when building the client bundle. | | `prepend` | `boolean` | `false` | If set to `true`, the callback function will be prepended to the array with `unshift()` instead of `push()`. | --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/build.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Compatibility" description: "Nuxt Kit provides a set of utilities to help you check the compatibility of your modules with different Nuxt versions." canonical_url: "https://nuxt.com/docs/3.x/api/kit/compatibility" --- # Compatibility > Nuxt Kit provides a set of utilities to help you check the compatibility of your modules with different Nuxt versions. Nuxt Kit utilities can be used in Nuxt 3, Nuxt 2 with Bridge and even Nuxt 2 without Bridge. To make sure your module is compatible with all versions, you can use the `checkNuxtCompatibility`, `assertNuxtCompatibility` and `hasNuxtCompatibility` functions. They will check if the current Nuxt version meets the constraints you provide. Also you can use `isNuxt2`, `isNuxt3` and `getNuxtVersion` functions for more granular checks. ## `checkNuxtCompatibility` Checks if constraints are met for the current Nuxt version. If not, returns an array of messages. Nuxt 2 version also checks for `bridge` support. ### Usage ```ts twoslash import { checkNuxtCompatibility, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ async setup (_options, nuxt) { const issues = await checkNuxtCompatibility({ nuxt: '^2.16.0' }, nuxt) if (issues.length) { console.warn('Nuxt compatibility issues found:\n' + issues.toString()) } else { // do something } }, }) ``` ### Type ```ts function checkNuxtCompatibility (constraints: NuxtCompatibility, nuxt?: Nuxt): Promise<NuxtCompatibilityIssues> ``` ### Parameters **`constraints`**: Version and builder constraints to check against. It accepts the following properties: | Property | Type | Required | Description | | -------- | -------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | `nuxt` | `string` | `false` | Nuxt version in semver format. Versions may be defined in Node.js way, for example: `>=2.15.0 <3.0.0`. | | `bridge` | `Record<string, string \| false>`{lang="ts"} | `false` | Specifies version constraints or disables compatibility for specific Nuxt builders like `vite`, `webpack`, or `rspack`. Use `false` to disable. | **`nuxt`**: Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. ## `assertNuxtCompatibility` Asserts that constraints are met for the current Nuxt version. If not, throws an error with the list of issues as string. ### Type ```ts twoslash // @errors: 2391 import type { Nuxt, NuxtCompatibility } from '@nuxt/schema' // ---cut--- function assertNuxtCompatibility (constraints: NuxtCompatibility, nuxt?: Nuxt): Promise<true> ``` ### Parameters **`constraints`**: Version and builder constraints to check against. Refer to the [constraints table in `checkNuxtCompatibility`](https://nuxt.com/docs/3.x/api/kit/compatibility#parameters) for details. **`nuxt`**: Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. ## `hasNuxtCompatibility` Checks if constraints are met for the current Nuxt version. Return `true` if all constraints are met, otherwise returns `false`. Nuxt 2 version also checks for `bridge` support. ### Usage ```ts twoslash import { defineNuxtModule, hasNuxtCompatibility } from '@nuxt/kit' export default defineNuxtModule({ async setup (_options, nuxt) { const usingNewPostcss = await hasNuxtCompatibility({ nuxt: '^2.16.0' }, nuxt) if (usingNewPostcss) { // do something } else { // do something else } }, }) ``` ### Type ```ts function hasNuxtCompatibility (constraints: NuxtCompatibility, nuxt?: Nuxt): Promise<boolean> ``` ### Parameters **`constraints`**: Version and builder constraints to check against. Refer to the [constraints table in `checkNuxtCompatibility`](https://nuxt.com/docs/3.x/api/kit/compatibility#parameters) for details. **`nuxt`**: Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. ## `isNuxtMajorVersion` Check if current Nuxt instance is of specified major version ### Usage ```ts twoslash import { defineNuxtModule, isNuxtMajorVersion } from '@nuxt/kit' export default defineNuxtModule({ setup () { if (isNuxtMajorVersion(3)) { // do something for Nuxt 3 } else { // do something else for other versions } }, }) ``` ### Type ```ts function isNuxtMajorVersion (major: number, nuxt?: Nuxt): boolean ``` ### Parameters **`major`**: Major version to check against. **`nuxt`**: Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. ## `isNuxt3` Checks if the current Nuxt version is 3.x. ::note Use `isNuxtMajorVersion(2, nuxt)` instead. This may be removed in @nuxt/kit v5 or a future major version. :: ### Type ```ts function isNuxt3 (nuxt?: Nuxt): boolean ``` ### Parameters **`nuxt`**: Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. ## `isNuxt2` Checks if the current Nuxt version is 2.x. ::note Use `isNuxtMajorVersion(2, nuxt)` instead. This may be removed in @nuxt/kit v5 or a future major version. :: ### Type ```ts function isNuxt2 (nuxt?: Nuxt): boolean ``` ### Parameters **`nuxt`**: Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. ## `getNuxtVersion` Returns the current Nuxt version. ### Type ```ts function getNuxtVersion (nuxt?: Nuxt): string ``` ### Parameters **`nuxt`**: Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/compatibility.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Components" description: "Nuxt Kit provides a set of utilities to help you work with components. You can register components globally or locally, and also add directories to be scanned for components." canonical_url: "https://nuxt.com/docs/3.x/api/kit/components" --- # Components > Nuxt Kit provides a set of utilities to help you work with components. You can register components globally or locally, and also add directories to be scanned for components. Components are the building blocks of your Nuxt application. They are reusable Vue instances that can be used to create a user interface. In Nuxt, components from the components directory are automatically imported by default. However, if you need to import components from an alternative directory or wish to selectively import them as needed, `@nuxt/kit` provides the `addComponentsDir` and `addComponent` methods. These utils allow you to customize the component configuration to better suit your needs. ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/injecting-components-and-component-directories?friend=nuxt" target="_blank"} Watch Vue School video about injecting components. :: ## `addComponentsDir` Register a directory to be scanned for components and imported only when used. Keep in mind, that this does not register components globally, until you specify `global: true` option. ### Usage ```ts export default defineNuxtModule({ meta: { name: '@nuxt/ui', configKey: 'ui', }, setup () { addComponentsDir({ path: resolve('./runtime/components'), prefix: 'U', pathPrefix: false, }) }, }) ``` ### Type ```ts function addComponentsDir (dir: ComponentsDir, opts: { prepend?: boolean } = {}): void ``` ### Parameters `dir` An object with the following properties: | Property | Type | Required | Description | | ----------------- | ---------------------------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `path` | `string` | `true` | Path (absolute or relative) to the directory containing your components. You can use Nuxt aliases (~ or @) to refer to directories inside project or directly use an npm package path similar to require. | | `pattern` | `string \| string[]`{lang="ts"} | `false` | Accept Pattern that will be run against specified path. | | `ignore` | `string[]` | `false` | Ignore patterns that will be run against specified path. | | `prefix` | `string` | `false` | Prefix all matched components with this string. | | `pathPrefix` | `boolean` | `false` | Prefix component name by its path. | | `prefetch` | `boolean` | `false` | These properties (prefetch/preload) are used in production to configure how components with Lazy prefix are handled by webpack via its magic comments. Learn more on [webpack documentation](https://webpack.js.org/api/module-methods/#magic-comments) | | `preload` | `boolean` | `false` | These properties (prefetch/preload) are used in production to configure how components with Lazy prefix are handled by webpack via its magic comments. Learn more on [webpack documentation](https://webpack.js.org/api/module-methods/#magic-comments) | | `isAsync` | `boolean` | `false` | This flag indicates, component should be loaded async (with a separate chunk) regardless of using Lazy prefix or not. | | `extendComponent` | `(component: Component) => Promise<Component \| void> \| (Component \| void)`{lang="ts"} | `false` | A function that will be called for each component found in the directory. It accepts a component object and should return a component object or a promise that resolves to a component object. | | `global` | `boolean` | `false` | If enabled, registers components to be globally available. | | `island` | `boolean` | `false` | If enabled, registers components as islands. You can read more about islands in [`<NuxtIsland/>`](https://nuxt.com/docs/3.x/api/components/nuxt-island) component description. | | `watch` | `boolean` | `false` | Watch specified path for changes, including file additions and file deletions. | | `extensions` | `string[]` | `false` | Extensions supported by Nuxt builder. | | `transpile` | `'auto' \| boolean`{lang="ts"} | `false` | Transpile specified path using build.transpile. If set to `'auto'`, it will set `transpile: true` if `node_modules/` is in path. | `opts` | Property | Type | Required | Description | | --------- | --------- | -------- | ---------------------------------------------------------------------------------------------------- | | `prepend` | `boolean` | `false` | If set to `true`, the directory will be prepended to the array with `unshift()` instead of `push()`. | ## `addComponent` Register a component to be automatically imported. ### Usage ```ts import { addComponent, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: '@nuxt/image', configKey: 'image', }, setup () { const resolver = createResolver(import.meta.url) addComponent({ name: 'NuxtImg', filePath: resolver.resolve('./runtime/components/NuxtImg.vue'), }) addComponent({ name: 'NuxtPicture', filePath: resolver.resolve('./runtime/components/NuxtPicture.vue'), }) }, }) ``` ### Type ```ts function addComponent (options: AddComponentOptions): void ``` ### Parameters `options`: An object with the following properties: | Property | Type | Required | Description | | ----------------- | ------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | `string` | `true` | Component name. | | `filePath` | `string` | `true` | Path to the component. | | `declarationPath` | `string` | `false` | Path to component's declaration file. It is used to generate components' [type templates](https://nuxt.com/docs/3.x/api/kit/templates#addtypetemplate); if not provided, `filePath` is used instead. | | `pascalName` | `string` | `false` | Pascal case component name. If not provided, it will be generated from the component name. | | `kebabName` | `string` | `false` | Kebab case component name. If not provided, it will be generated from the component name. | | `export` | `string` | `false` | Specify named or default export. If not provided, it will be set to `'default'`. | | `shortPath` | `string` | `false` | Short path to the component. If not provided, it will be generated from the component path. | | `chunkName` | `string` | `false` | Chunk name for the component. If not provided, it will be generated from the component name. | | `prefetch` | `boolean` | `false` | These properties (prefetch/preload) are used in production to configure how components with Lazy prefix are handled by webpack via its magic comments. Learn more on [webpack documentation](https://webpack.js.org/api/module-methods/#magic-comments) | | `preload` | `boolean` | `false` | These properties (prefetch/preload) are used in production to configure how components with Lazy prefix are handled by webpack via its magic comments. Learn more on [webpack documentation](https://webpack.js.org/api/module-methods/#magic-comments) | | `global` | `boolean` | `false` | If enabled, registers component to be globally available. | | `island` | `boolean` | `false` | If enabled, registers component as island. You can read more about islands in [`<NuxtIsland/>`](https://nuxt.com/docs/3.x/api/components/nuxt-island) component description. | | `mode` | `'client' \| 'server' \| 'all'`{lang="ts"} | `false` | This options indicates if component should render on client, server or both. By default, it will render on both client and server. | | `priority` | `number` | `false` | Priority of the component, if multiple components have the same name, the one with the highest priority will be used. | ### Examples If you want to auto-import a component from an npm package, and the component is a named export (rather than the default), you can use the `export` option to specify it. ```ts import { addComponent, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { // import { MyComponent as MyAutoImportedComponent } from 'my-npm-package' addComponent({ name: 'MyAutoImportedComponent', export: 'MyComponent', filePath: 'my-npm-package', }) }, }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/components.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Context" description: "Nuxt Kit provides a set of utilities to help you work with context." canonical_url: "https://nuxt.com/docs/3.x/api/kit/context" --- # Context > Nuxt Kit provides a set of utilities to help you work with context. Nuxt modules allow you to enhance Nuxt's capabilities. They offer a structured way to keep your code organized and modular. If you're looking to break down your module into smaller components, Nuxt offers the `useNuxt` and `tryUseNuxt` functions. These functions enable you to conveniently access the Nuxt instance from the context without having to pass it as an argument. ::note When you're working with the `setup` function in Nuxt modules, Nuxt is already provided as the second argument. This means you can access it directly without needing to call `useNuxt()`. :: ## `useNuxt` Get the Nuxt instance from the context. It will throw an error if Nuxt is not available. ### Usage ```ts import { useNuxt } from '@nuxt/kit' const setupSomeFeature = () => { const nuxt = useNuxt() // You can now use the nuxt instance console.log(nuxt.options) } ``` ### Type ```ts twoslash // @errors: 2391 import type { Nuxt } from '@nuxt/schema' // ---cut--- function useNuxt (): Nuxt ``` ### Return Value The `useNuxt` function returns the Nuxt instance, which contains all the options and methods available in Nuxt. | Property | Type | Description | | ---------- | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `options` | `NuxtOptions` | The resolved Nuxt configuration. | | `hooks` | `Hookable<NuxtHooks>` | The Nuxt hook system. Allows registering and listening to lifecycle events. | | `hook` | `(name: string, (...args: any[]) => Promise<void> \| void) => () => void` | Shortcut for `nuxt.hooks.hook`. Registers a single callback for a specific lifecycle hook. | | `callHook` | `(name: string, ...args: any[]) => Promise<any>` | Shortcut for `nuxt.hooks.callHook`. Triggers a lifecycle hook manually and runs all registered callbacks. | | `addHooks` | `(configHooks: NestedHooks) => () => void` | Shortcut for `nuxt.hooks.addHooks`. Registers multiple hooks at once. | ### Examples ::code-group ```ts twoslash [setupTranspilation.ts] import { useNuxt } from '@nuxt/kit' export const setupTranspilation = () => { const nuxt = useNuxt() if (nuxt.options.builder === '@nuxt/webpack-builder') { nuxt.options.build.transpile ||= [] nuxt.options.build.transpile.push('xstate') } } ``` ```ts twoslash [module.ts] // @module: esnext // @filename: setupTranspilation.ts export const setupTranspilation = () => {} // @filename: module.ts import { defineNuxtModule } from '@nuxt/kit' // ---cut--- import { setupTranspilation } from './setupTranspilation' export default defineNuxtModule({ setup () { setupTranspilation() }, }) ``` :: ## `tryUseNuxt` Get the Nuxt instance from the context. It will return `null` if Nuxt is not available. ### Usage ```ts twoslash import { tryUseNuxt } from '@nuxt/kit' function setupSomething () { const nuxt = tryUseNuxt() if (nuxt) { // You can now use the nuxt instance console.log(nuxt.options) } else { console.log('Nuxt is not available') } } ``` ### Type ```ts twoslash // @errors: 2391 import type { Nuxt } from '@nuxt/schema' // ---cut--- function tryUseNuxt (): Nuxt | null ``` ### Return Value The `tryUseNuxt` function returns the Nuxt instance if available, or `null` if Nuxt is not available. The Nuxt instance as described in the `useNuxt` section. ### Examples ::code-group ```ts twoslash [requireSiteConfig.ts] declare module 'nuxt/schema' { interface NuxtOptions { siteConfig: SiteConfig } } // ---cut--- import { tryUseNuxt } from '@nuxt/kit' interface SiteConfig { title?: string } export const requireSiteConfig = (): SiteConfig => { const nuxt = tryUseNuxt() if (!nuxt) { return {} } return nuxt.options.siteConfig } ``` ```ts twoslash [module.ts] // @module: esnext // @filename: requireSiteConfig.ts interface SiteConfig { title?: string } export const requireSiteConfig = (): SiteConfig => { return {} } // @filename: module.ts // ---cut--- import { defineNuxtModule, useNuxt } from '@nuxt/kit' import { requireSiteConfig } from './requireSiteConfig' export default defineNuxtModule({ setup (_, nuxt) { const config = requireSiteConfig() nuxt.options.app.head.title = config.title }, }) ``` :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/context.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Examples" description: "Examples of Nuxt Kit utilities in use." canonical_url: "https://nuxt.com/docs/3.x/api/kit/examples" --- # Examples > Examples of Nuxt Kit utilities in use. ## Accessing Nuxt Vite Config If you are building an integration that needs access to the runtime Vite or webpack config that Nuxt uses, it is possible to extract this using Kit utilities. Some examples of projects doing this already: - [histoire](https://github.com/histoire-dev/histoire/blob/main/packages/histoire-plugin-nuxt/src/index.ts) - [nuxt-vitest](https://github.com/danielroe/nuxt-vitest/blob/main/packages/nuxt-vitest/src/config.ts) - [@storybook-vue/nuxt](https://github.com/storybook-vue/storybook-nuxt/blob/main/packages/storybook-nuxt/src/preset.ts) Here is a brief example of how you might access the Vite config from a project; you could implement a similar approach to get the webpack configuration. ```js import { buildNuxt, loadNuxt } from '@nuxt/kit' // https://github.com/nuxt/nuxt/issues/14534 async function getViteConfig () { const nuxt = await loadNuxt({ cwd: process.cwd(), dev: false, overrides: { ssr: false } }) return new Promise((resolve, reject) => { nuxt.hook('vite:extend', (config) => { resolve(config) throw new Error('_stop_') }) buildNuxt(nuxt).catch((err) => { if (!err.toString().includes('_stop_')) { reject(err) } }) }).finally(() => nuxt.close()) } const viteConfig = await getViteConfig() console.log(viteConfig) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Head" description: "Nuxt Kit provides utilities to help you manage head configuration in modules." canonical_url: "https://nuxt.com/docs/3.x/api/kit/head" --- # Head > Nuxt Kit provides utilities to help you manage head configuration in modules. ## `setGlobalHead` Sets global head configuration for your Nuxt application. This utility allows modules to programmatically configure meta tags, links, scripts, and other head elements that will be applied across all pages. The provided head configuration will be merged with any existing head configuration using deep merging, with your provided values taking precedence. ::tip This is particularly useful for modules that need to inject global meta tags, stylesheets, or scripts into the application head. :: ### Type ```ts twoslash // @errors: 2391 // ---cut--- import type { SerializableHead } from '@unhead/vue/types' interface AppHeadMetaObject extends SerializableHead { charset?: string viewport?: string } function setGlobalHead (head: AppHeadMetaObject): void ``` ### Parameters #### `head` {#head} **Type**: `AppHeadMetaObject` An object containing head configuration. All properties are optional and will be merged with existing configuration: - `charset`: Character encoding for the document - `viewport`: Viewport meta tag configuration - `meta`: Array of meta tag objects - `link`: Array of link tag objects (stylesheets, icons, etc.) - `style`: Array of inline style tag objects - `script`: Array of script tag objects - `noscript`: Array of noscript tag objects - `title`: Default page title - `titleTemplate`: Template for formatting page titles - `bodyAttrs`: Attributes to add to the `<body>` tag - `htmlAttrs`: Attributes to add to the `<html>` tag ### Examples #### Adding Global Meta Tags ```ts import { defineNuxtModule, setGlobalHead } from '@nuxt/kit' export default defineNuxtModule({ setup () { setGlobalHead({ meta: [ { name: 'theme-color', content: '#ffffff' }, { name: 'author', content: 'Your Name' }, ], }) }, }) ``` #### Injecting Global Stylesheets ```ts import { defineNuxtModule, setGlobalHead } from '@nuxt/kit' export default defineNuxtModule({ setup () { setGlobalHead({ link: [ { rel: 'stylesheet', href: 'https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap', }, ], }) }, }) ``` #### Adding Global Scripts ```ts import { defineNuxtModule, setGlobalHead } from '@nuxt/kit' export default defineNuxtModule({ setup () { setGlobalHead({ script: [ { src: 'https://cdn.example.com/analytics.js', async: true, defer: true, }, ], }) }, }) ``` #### Setting HTML Attributes ```ts import { defineNuxtModule, setGlobalHead } from '@nuxt/kit' export default defineNuxtModule({ setup () { setGlobalHead({ htmlAttrs: { lang: 'en', dir: 'ltr', }, bodyAttrs: { class: 'custom-body-class', }, }) }, }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/head.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Layers" description: "Nuxt Kit provides utilities to help you work with layers and their directory structures." canonical_url: "https://nuxt.com/docs/3.x/api/kit/layers" --- # Layers > Nuxt Kit provides utilities to help you work with layers and their directory structures. Nuxt layers provide a powerful way to share and extend functionality across projects. When working with layers in modules, you often need to access directory paths from each layer. Nuxt Kit provides the `getLayerDirectories` utility to access resolved directory paths for all layers in your Nuxt application. ## `getLayerDirectories` Get the resolved directory paths for all layers in a Nuxt application. This function provides a structured way to access layer directories without directly accessing the private `nuxt.options._layers` property. ### Usage ```ts twoslash import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit' export default defineNuxtModule({ setup () { const layerDirs = getLayerDirectories() // Access directories from all layers for (const [index, layer] of layerDirs.entries()) { console.log(`Layer ${index}:`) console.log(` Root: ${layer.root}`) console.log(` App: ${layer.app}`) console.log(` Server: ${layer.server}`) console.log(` Pages: ${layer.appPages}`) // ... other directories } }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { Nuxt } from '@nuxt/schema' // ---cut--- function getLayerDirectories (nuxt?: Nuxt): LayerDirectories[] interface LayerDirectories { /** Nuxt rootDir (`/` by default) */ readonly root: string /** Nitro source directory (`/server` by default) */ readonly server: string /** Local modules directory (`/modules` by default) */ readonly modules: string /** Shared directory (`/shared` by default) */ readonly shared: string /** Public directory (`/public` by default) */ readonly public: string /** Nuxt srcDir (`/app/` by default) */ readonly app: string /** Layouts directory (`/layouts` by default) */ readonly appLayouts: string /** Middleware directory (`/middleware` by default) */ readonly appMiddleware: string /** Pages directory (`/pages` by default) */ readonly appPages: string /** Plugins directory (`/plugins` by default) */ readonly appPlugins: string } ``` ### Parameters **`nuxt`** (optional): The Nuxt instance to get layers from. If not provided, the function will use the current Nuxt context. ### Return Value The `getLayerDirectories` function returns an array of `LayerDirectories` objects, one for each layer in the application. **Layer Priority Ordering**: The layers are ordered by priority, where: - The **first layer** is the user/project layer (highest priority) - **Earlier layers override later layers** in the array - **Base layers appear last** in the array (lowest priority) This ordering matches Nuxt's layer resolution system, where user-defined configurations and files take precedence over those from base layers. **`LayerDirectories`**: An object containing the resolved directory paths for a layer. | Property | Type | Description | | --------------- | -------- | ------------------------------------------------------------ | | `root` | `string` | The root directory of the layer (equivalent to `rootDir`) | | `server` | `string` | The server directory for Nitro server-side code | | `modules` | `string` | The local modules directory | | `shared` | `string` | The shared directory for code used by both client and server | | `app` | `string` | The source directory of the layer (equivalent to `srcDir`) | | `public` | `string` | The public directory for static assets | | `appLayouts` | `string` | The layouts directory for Vue layout components | | `appMiddleware` | `string` | The middleware directory for route middleware | | `appPages` | `string` | The pages directory for file-based routing | | `appPlugins` | `string` | The plugins directory for Nuxt plugins | ### Examples **Processing files from all layers:** ```ts twoslash // @errors: 2307 // ---cut--- import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit' import { resolve } from 'pathe' import { globby } from 'globby' export default defineNuxtModule({ async setup () { const layerDirs = getLayerDirectories() // Find all component files across layers // Note: layerDirs[0] is the user layer (highest priority) // Later layers in the array have lower priority const componentFiles = [] for (const [index, layer] of layerDirs.entries()) { const files = await globby('**/*.vue', { cwd: resolve(layer.app, 'components'), absolute: true, }) console.log(`Layer ${index} (${index === 0 ? 'user' : 'base'}):`, files.length, 'components') componentFiles.push(...files) } }, }) ``` **Adding templates from multiple layers:** ```ts twoslash import { addTemplate, defineNuxtModule, getLayerDirectories } from '@nuxt/kit' import { basename, resolve } from 'pathe' import { existsSync } from 'node:fs' export default defineNuxtModule({ setup () { const layerDirs = getLayerDirectories() // Add a config file from each layer that has one for (const dirs of layerDirs) { const configPath = resolve(dirs.app, 'my-module.config.ts') if (existsSync(configPath)) { addTemplate({ filename: `my-module-${basename(dirs.root)}.config.ts`, src: configPath, }) } } }, }) ``` **Respecting layer priority:** ```ts twoslash import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit' import { resolve } from 'pathe' import { existsSync, readFileSync } from 'node:fs' export default defineNuxtModule({ setup () { const layerDirs = getLayerDirectories() // Find the first (highest priority) layer that has a specific config file // This respects the layer priority system let configContent = null for (const dirs of layerDirs) { const configPath = resolve(dirs.app, 'my-config.json') if (existsSync(configPath)) { configContent = readFileSync(configPath, 'utf-8') console.log(`Using config from layer: ${dirs.root}`) break // Use the first (highest priority) config found } } // Alternative: Collect configs from all layers, with user layer taking precedence const allConfigs = {} for (const dirs of layerDirs.reverse()) { // Process from lowest to highest priority const configPath = resolve(dirs.app, 'my-config.json') if (existsSync(configPath)) { const config = JSON.parse(readFileSync(configPath, 'utf-8')) Object.assign(allConfigs, config) // Later assignments override earlier ones } } }, }) ``` **Checking for layer-specific directories:** ```ts twoslash import { defineNuxtModule, getLayerDirectories } from '@nuxt/kit' import { existsSync } from 'node:fs' import { resolve } from 'pathe' export default defineNuxtModule({ setup () { const layerDirs = getLayerDirectories() // Find layers that have a specific custom directory const layersWithAssets = layerDirs.filter((layer) => { return existsSync(resolve(layer.app, 'assets')) }) console.log(`Found ${layersWithAssets.length} layers with assets directory`) }, }) ``` ::note The `getLayerDirectories` function includes caching via a WeakMap to avoid recomputing directory paths for the same layers repeatedly, improving performance when called multiple times. :: ::note Directory paths returned by this function always include a trailing slash for consistency. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/layers.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Layout" description: "Nuxt Kit provides a set of utilities to help you work with layouts." canonical_url: "https://nuxt.com/docs/3.x/api/kit/layout" --- # Layout > Nuxt Kit provides a set of utilities to help you work with layouts. Layouts is used to be a wrapper around your pages. It can be used to wrap your pages with common components, for example, a header and a footer. Layouts can be registered using `addLayout` utility. ## `addLayout` Register template as layout and add it to the layouts. ::note In Nuxt 2 `error` layout can also be registered using this utility. In Nuxt 3+ `error` layout [replaced](https://nuxt.com/docs/3.x/getting-started/error-handling#error-page) with `error.vue` page in project root. :: ### Usage ```ts twoslash import { addLayout, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addLayout({ src: resolve('templates/custom-layout.ts'), filename: 'custom-layout.ts', }, 'custom') }, }) ``` ### Type ```ts function addLayout (layout: NuxtTemplate | string, name: string): void ``` ### Parameters **`layout`**: A template object or a string with the path to the template. If a string is provided, it will be converted to a template object with `src` set to the string value. If a template object is provided, it must have the following properties: | Property | Type | Required | Description | | ------------- | ------------------------------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `src` | `string` | `false` | Path to the template. If `src` is not provided, `getContents` must be provided instead. | | `filename` | `string` | `false` | Filename of the template. If `filename` is not provided, it will be generated from the `src` path. In this case, the `src` option is required. | | `dst` | `string` | `false` | Path to the destination file. If `dst` is not provided, it will be generated from the `filename` path and nuxt `buildDir` option. | | `options` | `Record<string, any>`{lang="ts"} | `false` | Options to pass to the template. | | `getContents` | `(data) => string \| Promise<string>`{lang="ts"} | `false` | A function that will be called with the `options` object. It should return a string or a promise that resolves to a string. If `src` is provided, this function will be ignored. | | `write` | `boolean` | `false` | If set to `true`, the template will be written to the destination file. Otherwise, the template will be used only in virtual filesystem. | **`name`**: The name to register the layout under (e.g., `default`, `custom`, etc.). ### Example This will register a layout named `custom` that wraps pages with a header and footer. ```ts twoslash import { addLayout, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addLayout({ write: true, filename: 'my-layout.vue', getContents: () => `<template> <div> <header>My Header</header> <slot /> <footer>My Footer</footer> </div> </template>`, }, 'custom') }, }) ``` You can then use this layout in your pages: ```vue [pages/about.vue] <script setup lang="ts"> definePageMeta({ layout: 'custom', }) </script> <template> <div>About Page</div> </template> ``` ::warning Due to the lack of support for virtual `.vue` files by `@vitejs/plugin-vue`, you can work around this limitation by passing `write: true` to the first argument of `addLayout`. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/layout.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Logging" description: "Nuxt Kit provides a set of utilities to help you work with logging. These functions allow you to log messages with extra features." canonical_url: "https://nuxt.com/docs/3.x/api/kit/logging" --- # Logging > Nuxt Kit provides a set of utilities to help you work with logging. These functions allow you to log messages with extra features. Nuxt provides a logger instance that you can use to log messages with extra features. `useLogger` allows you to get a logger instance. ## `useLogger` Returns a logger instance. It uses [consola](https://github.com/unjs/consola) under the hood. ### Usage ```ts twoslash import { defineNuxtModule, useLogger } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { const logger = useLogger('my-module') logger.info('Hello from my module!') }, }) ``` ### Type ```ts function useLogger (tag?: string, options?: Partial<ConsolaOptions>): ConsolaInstance ``` ### Parameters **`tag`**: A tag to suffix all log messages with, displayed on the right near the timestamp. **`options`**: Consola configuration options. ### Examples ```ts twoslash import { defineNuxtModule, useLogger } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { const logger = useLogger('my-module', { level: options.quiet ? 0 : 3 }) logger.info('Hello from my module!') }, }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/logger.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Modules" description: "Nuxt Kit provides a set of utilities to help you create and use modules. You can use these utilities to create your own modules or to reuse existing modules." canonical_url: "https://nuxt.com/docs/3.x/api/kit/modules" --- # Modules > Nuxt Kit provides a set of utilities to help you create and use modules. You can use these utilities to create your own modules or to reuse existing modules. Modules are the building blocks of Nuxt. Kit provides a set of utilities to help you create and use modules. You can use these utilities to create your own modules or to reuse existing modules. For example, you can use the `defineNuxtModule` function to define a module and specify dependencies using the `moduleDependencies` option. ## `defineNuxtModule` Define a Nuxt module, automatically merging defaults with user provided options, installing any hooks that are provided, and calling an optional setup function for full control. ### Usage ```ts twoslash import { defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, defaults: { enabled: true, }, setup (options) { if (options.enabled) { console.log('My Nuxt module is enabled!') } }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { ModuleDefinition, ModuleOptions, NuxtModule } from '@nuxt/schema' // ---cut--- export function defineNuxtModule<TOptions extends ModuleOptions> ( definition?: ModuleDefinition<TOptions, Partial<TOptions>, false> | NuxtModule<TOptions, Partial<TOptions>, false>, ): NuxtModule<TOptions, TOptions, false> export function defineNuxtModule<TOptions extends ModuleOptions> (): { with: <TOptionsDefaults extends Partial<TOptions>> ( definition: ModuleDefinition<TOptions, TOptionsDefaults, true> | NuxtModule<TOptions, TOptionsDefaults, true>, ) => NuxtModule<TOptions, TOptionsDefaults, true> } ``` ### Parameters **definition**: A module definition object or a module function. The module definition object should contain the following properties: | Property | Type | Required | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `meta` | `ModuleMeta` | `false` | Metadata of the module. It defines the module name, version, config key and compatibility. | | `defaults` | `T \| ((nuxt: Nuxt) => T)`{lang="ts"} | `false` | Default options for the module. If a function is provided, it will be called with the Nuxt instance as the first argument. | | `schema` | `T` | `false` | Schema for the module options. If provided, options will be applied to the schema. | | `hooks` | `Partial<NuxtHooks>`{lang="ts"} | `false` | Hooks to be installed for the module. If provided, the module will install the hooks. | | `moduleDependencies` | `Record<string, ModuleDependency> \| ((nuxt: Nuxt) => Record<string, ModuleDependency>)`{lang="ts"} | `false` | Dependencies on other modules with version constraints and configuration. Can be an object or a function that receives the Nuxt instance. See [example](https://nuxt.com/docs/3.x/api/kit/modules#specifying-module-dependencies). | | `onInstall` | `(nuxt: Nuxt) => Awaitable<void>`{lang="ts"} | `false` | Lifecycle hook called when the module is first installed. Requires `meta.name` and `meta.version` to be defined. | | `onUpgrade` | `(nuxt: Nuxt, options: T, previousVersion: string) => Awaitable<void>`{lang="ts"} | `false` | Lifecycle hook called when the module is upgraded to a newer version. Requires `meta.name` and `meta.version` to be defined. | | `setup` | `(this: void, resolvedOptions: T, nuxt: Nuxt) => Awaitable<void \| false \| ModuleSetupInstallResult>`{lang="ts"} | `false` | Setup function for the module. If provided, the module will call the setup function. | ### Examples #### Using `configKey` to Make Your Module Configurable When defining a Nuxt module, you can set a `configKey` to specify how users should configure the module in their `nuxt.config`. ```ts import { defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, defaults: { // Module options enabled: true, }, setup (options) { if (options.enabled) { console.log('My Nuxt module is enabled!') } }, }) ``` Users can provide options for this module under the corresponding key in `nuxt.config`. ```ts export default defineNuxtConfig({ myModule: { enabled: false, }, }) ``` Users can also completely disable a module by setting the config key to `false`. This prevents the module's setup function from running while still generating types for module options. ```ts export default defineNuxtConfig({ // Disable the module entirely myModule: false, }) ``` ::tip This is particularly useful when you want to disable modules inherited from [Nuxt layers](https://nuxt.com/docs/3.x/guide/going-further/layers#disabling-modules-from-layers). :: #### Defining Module Compatibility Requirements If you're developing a Nuxt module and using APIs that are only supported in specific Nuxt versions, it's highly recommended to include `compatibility.nuxt`. ```ts export default defineNuxtModule({ meta: { name: '@nuxt/icon', configKey: 'icon', compatibility: { // Required nuxt version in semver format. nuxt: '>=3.0.0', // or use '^3.0.0' }, }, setup () { const resolver = createResolver(import.meta.url) // Implement }, }) ``` If the user tries to use your module with an incompatible Nuxt version, they will receive a warning in the console. ```terminal WARN Module @nuxt/icon is disabled due to incompatibility issues: - [nuxt] Nuxt version ^3.1.0 is required but currently using 3.0.0 ``` #### Type Safety for Resolved Options with `.with()` When you need type safety for your resolved/merged module options, you can use the `.with()` method. This enables TypeScript to properly infer the relationship between your module's defaults and the final resolved options that your setup function receives. ```ts import { defineNuxtModule } from '@nuxt/kit' // Define your module options interface interface ModuleOptions { apiKey: string baseURL: string timeout?: number retries?: number } export default defineNuxtModule<ModuleOptions>().with({ meta: { name: '@nuxtjs/my-api', configKey: 'myApi', }, defaults: { baseURL: 'https://api.example.com', timeout: 5000, retries: 3, }, setup (resolvedOptions, nuxt) { // resolvedOptions is properly typed as: // { // apiKey: string // Required, no default provided // baseURL: string // Required, has default value // timeout: number // Optional, has default value // retries: number // Optional, has default value // } console.log(resolvedOptions.baseURL) // ✅ TypeScript knows this is always defined console.log(resolvedOptions.timeout) // ✅ TypeScript knows this is always defined console.log(resolvedOptions.retries) // ✅ TypeScript knows this is always defined }, }) ``` Without using `.with()`, the `resolvedOptions` parameter would be typed as the raw `ModuleOptions` interface, where `timeout` and `retries` could be `undefined` even when defaults are provided. The `.with()` method enables TypeScript to understand that default values make those properties non-optional in the resolved options. #### Using Lifecycle Hooks for Module Installation and Upgrade You can define lifecycle hooks that run when your module is first installed or upgraded to a new version. These hooks are useful for performing one-time setup tasks, database migrations, or cleanup operations. ::important For lifecycle hooks to work, you **must** provide both `meta.name` and `meta.version` in your module definition. The hooks use these values to track the module's installation state in the project's `.nuxtrc` file. :: Lifecycle hooks run before the main `setup` function, and if a hook throws an error, it's logged but doesn't stop the build process. **`onInstall`** runs only once when the module is first added to a project. **`onUpgrade`** runs each time the module version increases (using semver comparison) — but only once for each version bump. ##### Example ```ts import { defineNuxtModule } from '@nuxt/kit' import semver from 'semver' export default defineNuxtModule({ meta: { name: 'my-awesome-module', version: '1.2.0', // Required for lifecycle hooks configKey: 'myAwesomeModule', }, defaults: { apiKey: '', enabled: true, }, onInstall (nuxt) { // This runs only when the module is first installed console.log('Setting up my-awesome-module for the first time!') // You might want to: // - Create initial configuration files // - Set up database schemas // - Display welcome messages // - Perform initial data migration }, onUpgrade (nuxt, options, previousVersion) { // This runs when the module is upgraded to a newer version console.log(`Upgrading my-awesome-module from ${previousVersion} to 1.2.0`) // You might want to: // - Migrate configuration files // - Update database schemas // - Clean up deprecated files // - Display upgrade notes if (semver.lt(previousVersion, '1.1.0')) { console.log('⚠️ Breaking changes in 1.1.0 - please check the migration guide') } }, setup (options, nuxt) { // Regular setup logic runs on every build if (options.enabled) { // Configure the module } }, }) ``` #### Specifying Module Dependencies You can use the `moduleDependencies` option to declare dependencies on other modules. This provides a robust way to ensure proper setup order, version compatibility, and configuration management. The `moduleDependencies` option can be either an object or a function that receives the Nuxt instance: ##### Example ```ts import { defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', }, moduleDependencies: { '@nuxtjs/tailwindcss': { // Specify a version constraint (semver format) version: '>=6.0.0', // Configuration that overrides user settings overrides: { exposeConfig: true, }, // Configuration that sets defaults but respects user settings defaults: { config: { darkMode: 'class', }, }, }, '@nuxtjs/fontaine': { // Optional dependencies won't be installed but ensure that options // can be set if they _are_ installed optional: true, defaults: { fonts: [ { family: 'Roboto', fallbacks: ['Impact'], }, ], }, }, }, setup (options, nuxt) { }, }) ``` You can also use a function to dynamically determine dependencies based on the Nuxt configuration: ```ts import { defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', }, moduleDependencies (nuxt) { const dependencies: Record<string, any> = { '@nuxtjs/tailwindcss': { version: '>=6.0.0', }, } // Conditionally add dependencies based on Nuxt config if (nuxt.options.experimental?.someFeature) { dependencies['@nuxtjs/fontaine'] = { optional: true, } } return dependencies }, setup (options, nuxt) { // Your setup logic runs after all dependencies are initialized }, }) ``` ## `installModule` ::callout{type="warning"} **Deprecated:** Use the [`moduleDependencies`](https://nuxt.com/docs/3.x/api/kit/modules#specifying-module-dependencies) option in `defineNuxtModule` instead. The `installModule` function will be removed (or may become non-blocking) in a future version. :: Install specified Nuxt module programmatically. This is helpful when your module depends on other modules. You can pass the module options as an object to `inlineOptions` and they will be passed to the module's `setup` function. ### Usage ```ts twoslash import { defineNuxtModule, installModule } from '@nuxt/kit' export default defineNuxtModule({ async setup () { // will install @nuxtjs/fontaine with Roboto font and Impact fallback await installModule('@nuxtjs/fontaine', { // module configuration fonts: [ { family: 'Roboto', fallbacks: ['Impact'], fallbackName: 'fallback-a', }, ], }) }, }) ``` ### Type ```ts async function installModule (moduleToInstall: string | NuxtModule, inlineOptions?: any, nuxt?: Nuxt) ``` ### Parameters | Property | Type | Required | Description | | ----------------- | --------------------------------- | -------- | --------------------------------------------------------------------------------------------- | | `moduleToInstall` | `string \| NuxtModule`{lang="ts"} | `true` | The module to install. Can be either a string with the module name or a module object itself. | | `inlineOptions` | `any` | `false` | An object with the module options to be passed to the module's `setup` function. | | `nuxt` | `Nuxt` | `false` | Nuxt instance. If not provided, it will be retrieved from the context via `useNuxt()` call. | ### Examples ```ts import { defineNuxtModule, installModule } from '@nuxt/kit' export default defineNuxtModule({ async setup (options, nuxt) { // will install @nuxtjs/fontaine with Roboto font and Impact fallback await installModule('@nuxtjs/fontaine', { // module configuration fonts: [ { family: 'Roboto', fallbacks: ['Impact'], fallbackName: 'fallback-a', }, ], }) }, }) ``` --- - [Source](https://github.com/nuxt/nuxt/tree/main/packages/kit/src/module) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nitro" description: "Nuxt Kit provides a set of utilities to help you work with Nitro. These functions allow you to add server handlers, plugins, and prerender routes." canonical_url: "https://nuxt.com/docs/3.x/api/kit/nitro" --- # Nitro > Nuxt Kit provides a set of utilities to help you work with Nitro. These functions allow you to add server handlers, plugins, and prerender routes. Nitro is an open source TypeScript framework to build ultra-fast web servers. Nuxt uses Nitro as its server engine. You can use `useNitro` to access the Nitro instance, `addServerHandler` to add a server handler, `addDevServerHandler` to add a server handler to be used only in development mode, `addServerPlugin` to add a plugin to extend Nitro's runtime behavior, and `addPrerenderRoutes` to add routes to be prerendered by Nitro. ## `addServerHandler` Adds a Nitro server handler. Use this if you want to create server middleware or a custom route. ### Usage ```ts twoslash import { addServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { const { resolve } = createResolver(import.meta.url) addServerHandler({ route: '/robots.txt', handler: resolve('./runtime/robots.get'), }) }, }) ``` ### Type ```ts function addServerHandler (handler: NitroEventHandler): void ``` ### Parameters **handler**: A handler object with the following properties: | Property | Type | Required | Description | | ------------ | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | `handler` | `string` | `true` | Path to event handler. | | `route` | `string` | `false` | Path prefix or route. If an empty string used, will be used as a middleware. | | `middleware` | `boolean` | `false` | Specifies this is a middleware handler. Middleware are called on every route and should normally return nothing to pass to the next handlers. | | `lazy` | `boolean` | `false` | Use lazy loading to import the handler. This is useful when you only want to load the handler on demand. | | `method` | `string` | `false` | Router method matcher. If handler name contains method name, it will be used as a default value. | ### Examples #### Basic Usage You can use `addServerHandler` to add a server handler from your module. ::code-group ```ts twoslash [module.ts] import { addServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { const { resolve } = createResolver(import.meta.url) addServerHandler({ route: '/robots.txt', handler: resolve('./runtime/robots.get'), }) }, }) ``` ```ts twoslash [runtime/robots.get.ts] export default defineEventHandler(() => { return { body: `User-agent: *\nDisallow: /`, } }) ``` :: When you access `/robots.txt`, it will return the following response: ```txt User-agent: * Disallow: / ``` ## `addDevServerHandler` Adds a Nitro server handler to be used only in development mode. This handler will be excluded from production build. ### Usage ```ts twoslash import { defineEventHandler } from 'h3' import { addDevServerHandler, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addDevServerHandler({ handler: defineEventHandler(() => { return { body: `Response generated at ${new Date().toISOString()}`, } }), route: '/_handler', }) }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { NitroDevEventHandler } from 'nitropack/types' // ---cut--- function addDevServerHandler (handler: NitroDevEventHandler): void ``` ### Parameters **handler**: A handler object with the following properties: | Property | Type | Required | Description | | --------- | -------------- | -------- | ---------------------------------------------------------------------------- | | `handler` | `EventHandler` | `true` | Event handler. | | `route` | `string` | `false` | Path prefix or route. If an empty string used, will be used as a middleware. | ### Examples #### Basic Usage In some cases, you may want to create a server handler specifically for development purposes, such as a Tailwind config viewer. ```ts import { joinURL } from 'ufo' import { addDevServerHandler, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ async setup (options, nuxt) { const route = joinURL(nuxt.options.app?.baseURL, '/_tailwind') // @ts-expect-error - tailwind-config-viewer does not have correct types const createServer = await import('tailwind-config-viewer/server/index.js').then(r => r.default || r) as any const viewerDevMiddleware = createServer({ tailwindConfigProvider: () => options, routerPrefix: route }).asMiddleware() addDevServerHandler({ route, handler: viewerDevMiddleware }) }, }) ``` ## `useNitro` Returns the Nitro instance. ::warning You can call `useNitro()` only after `ready` hook. :: ::note Changes to the Nitro instance configuration are not applied. :: ### Usage ```ts import { defineNuxtModule, useNitro } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { const resolver = createResolver(import.meta.url) nuxt.hook('ready', () => { const nitro = useNitro() // Do something with Nitro instance }) }, }) ``` ### Type ```ts function useNitro (): Nitro ``` ## `addServerPlugin` Add plugin to extend Nitro's runtime behavior. ::tip You can read more about Nitro plugins in the [Nitro documentation](https://nitro.build/guide/plugins). :: ::warning It is necessary to explicitly import `defineNitroPlugin` from `nitropack/runtime` within your plugin file. The same requirement applies to utilities such as `useRuntimeConfig`. :: ### Usage ```ts twoslash import { addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addServerPlugin(resolve('./runtime/plugin.ts')) }, }) ``` ### Type ```ts function addServerPlugin (plugin: string): void ``` ### Parameters | Property | Type | Required | Description | | -------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------- | | `plugin` | `string` | `true` | Path to the plugin. The plugin must export a default function that accepts the Nitro instance as an argument. | ### Examples ::code-group ```ts [module.ts] import { addServerPlugin, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addServerPlugin(resolve('./runtime/plugin.ts')) }, }) ``` ```ts [runtime/plugin.ts] export default defineNitroPlugin((nitroApp) => { nitroApp.hooks.hook('request', (event) => { console.log('on request', event.path) }) nitroApp.hooks.hook('beforeResponse', (event, { body }) => { console.log('on response', event.path, { body }) }) nitroApp.hooks.hook('afterResponse', (event, { body }) => { console.log('on after response', event.path, { body }) }) }) ``` :: ## `addPrerenderRoutes` Add routes to be prerendered to Nitro. ### Usage ```ts import { addPrerenderRoutes, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'nuxt-sitemap', configKey: 'sitemap', }, defaults: { sitemapUrl: '/sitemap.xml', prerender: true, }, setup (options) { if (options.prerender) { addPrerenderRoutes(options.sitemapUrl) } }, }) ``` ### Type ```ts function addPrerenderRoutes (routes: string | string[]): void ``` ### Parameters | Property | Type | Required | Description | | -------- | ------------------------------- | -------- | ------------------------------------------- | | `routes` | `string \| string[]`{lang="ts"} | `true` | A route or an array of routes to prerender. | ## `addServerImports` Add imports to the server. It makes your imports available in Nitro without the need to import them manually. ::warning If you want to provide a utility that works in both server and client contexts and is usable in the [`shared/`](https://nuxt.com/docs/3.x/directory-structure/shared) directory, the function must be imported from the same source file for both [`addImports`](https://nuxt.com/docs/3.x/api/kit/autoimports#addimports) and `addServerImports` and should have identical signature. That source file should not import anything context-specific (i.e., Nitro context, Nuxt app context) or else it might cause errors during type-checking. :: ### Usage ```ts twoslash import { addServerImports, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { const names = [ 'useStoryblok', 'useStoryblokApi', 'useStoryblokBridge', 'renderRichText', 'RichTextSchema', ] names.forEach(name => addServerImports({ name, as: name, from: '@storyblok/vue' }), ) }, }) ``` ### Type ```ts function addServerImports (dirs: Import | Import[]): void ``` ### Parameters `imports`: An object or an array of objects with the following properties: | Property | Type | Required | Description | | ---------- | --------------------- | -------- | --------------------------------------------------------------------------------------------------------------- | | `name` | `string` | `true` | Import name to be detected. | | `from` | `string` | `true` | Module specifier to import from. | | `priority` | `number` | `false` | Priority of the import; if multiple imports have the same name, the one with the highest priority will be used. | | `disabled` | `boolean` | `false` | If this import is disabled. | | `meta` | `Record<string, any>` | `false` | Metadata of the import. | | `type` | `boolean` | `false` | If this import is a pure type import. | | `typeFrom` | `string` | `false` | Use this as the `from` value when generating type declarations. | | `as` | `string` | `false` | Import as this name. | ## `addServerImportsDir` Add a directory to be scanned for auto-imports by Nitro. ### Usage ```ts twoslash import { addServerImportsDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerImportsDir(resolve('./runtime/server/composables')) }, }) ``` ### Type ```ts function addServerImportsDir (dirs: string | string[], opts: { prepend?: boolean }): void ``` ### Parameters | Property | Type | Required | Description | | -------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- | | `dirs` | `string \| string[]`{lang="ts"} | `true` | A directory or an array of directories to register to be scanned by Nitro. | | `opts` | `{ prepend?: boolean }` | `false` | Options for the import directory. If `prepend` is `true`, the directory is added to the beginning of the scan list. | ### Examples You can use `addServerImportsDir` to add a directory to be scanned by Nitro. This is useful when you want Nitro to auto-import functions from a custom server directory. ::code-group ```ts twoslash [module.ts] import { addServerImportsDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerImportsDir(resolve('./runtime/server/composables')) }, }) ``` ```ts twoslash [runtime/server/composables/index.ts] export function useApiSecret () { const { apiSecret } = useRuntimeConfig() return apiSecret } ``` :: You can then use the `useApiSecret` function in your server code: ```ts twoslash [runtime/server/api/hello.ts] const useApiSecret = (): string => '' // ---cut--- export default defineEventHandler(() => { const apiSecret = useApiSecret() // Do something with the apiSecret }) ``` ## `addServerScanDir` Add directories to be scanned by Nitro. It will check for subdirectories, which will be registered just like the `~~/server` folder is. ::note Only `~~/server/api`, `~~/server/routes`, `~~/server/middleware`, and `~~/server/utils` are scanned. :: ### Usage ```ts twoslash import { addServerScanDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerScanDir(resolve('./runtime/server')) }, }) ``` ### Type ```ts function addServerScanDir (dirs: string | string[], opts: { prepend?: boolean }): void ``` ### Parameters | Property | Type | Required | Description | | -------- | ------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- | | `dirs` | `string \| string[]`{lang="ts"} | `true` | A directory or an array of directories to register to be scanned for by Nitro as server dirs. | | `opts` | `{ prepend?: boolean }` | `false` | Options for the import directory. If `prepend` is `true`, the directory is added to the beginning of the scan list. | ### Examples You can use `addServerScanDir` to add a directory to be scanned by Nitro. This is useful when you want to add a custom server directory. ::code-group ```ts twoslash [module.ts] import { addServerScanDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ meta: { name: 'my-module', configKey: 'myModule', }, setup (options) { const { resolve } = createResolver(import.meta.url) addServerScanDir(resolve('./runtime/server')) }, }) ``` ```ts twoslash [runtime/server/utils/index.ts] export function hello () { return 'Hello from server utils!' } ``` :: You can then use the `hello` function in your server code. ```ts twoslash [runtime/server/api/hello.ts] function hello () { return 'Hello from server utils!' } // ---cut--- export default defineEventHandler(() => { return hello() // Hello from server utils! }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/nitro.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Pages" description: "Nuxt Kit provides a set of utilities to help you create and use pages. You can use these utilities to manipulate the pages configuration or to define route rules." canonical_url: "https://nuxt.com/docs/3.x/api/kit/pages" --- # Pages > Nuxt Kit provides a set of utilities to help you create and use pages. You can use these utilities to manipulate the pages configuration or to define route rules. ## `extendPages` In Nuxt, routes are automatically generated based on the structure of the files in the `pages` directory. However, there may be scenarios where you'd want to customize these routes. For instance, you might need to add a route for a dynamic page not generated by Nuxt, remove an existing route, or modify the configuration of a route. For such customizations, Nuxt offers the `extendPages` feature, which allows you to extend and alter the pages configuration. ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/extend-and-alter-nuxt-pages?friend=nuxt" target="_blank"} Watch Vue School video about extendPages. :: ### Usage ```ts twoslash import { createResolver, defineNuxtModule, extendPages } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { const { resolve } = createResolver(import.meta.url) extendPages((pages) => { pages.unshift({ name: 'prismic-preview', path: '/preview', file: resolve('runtime/preview.vue'), }) }) }, }) ``` ### Type ```ts function extendPages (callback: (pages: NuxtPage[]) => void): void ``` ### Parameters **callback**: A function that will be called with the pages configuration. You can alter this array by adding, deleting, or modifying its elements. Note: You should modify the provided pages array directly, as changes made to a copied array will not be reflected in the configuration. | Property | Type | Required | Description | | ---------- | -------------------------------- | -------- | ----------------------------------------------------------------------------------------- | | `name` | `string` | `false` | The name of the route. Useful for programmatic navigation and identifying routes. | | `path` | `string` | `false` | The route URL path. If not set, Nuxt will infer it from the file location. | | `file` | `string` | `false` | Path to the Vue file that should be used as the component for the route. | | `meta` | `Record<string, any>`{lang="ts"} | `false` | Custom metadata for the route. Can be used in layouts, middlewares, or navigation guards. | | `alias` | `string[] \| string`{lang="ts"} | `false` | One or more alias paths for the route. Useful for supporting multiple URLs. | | `redirect` | `RouteLocationRaw`{lang="ts"} | `false` | Redirect rule for the route. Supports named routes, objects, or string paths. | | `children` | `NuxtPage[]`{lang="ts"} | `false` | Nested child routes under this route for layout or view nesting. | ## `extendRouteRules` Nuxt is powered by the [Nitro](https://nitro.build/) server engine. With Nitro, you can incorporate high-level logic directly into your configuration, which is useful for actions like redirects, proxying, caching, and appending headers to routes. This configuration works by associating route patterns with specific route settings. ::tip You can read more about Nitro route rules in the [Nitro documentation](https://nitro.build/guide/routing#route-rules). :: ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/adding-route-rules-and-route-middlewares?friend=nuxt" target="_blank"} Watch Vue School video about adding route rules and route middelwares. :: ### Usage ```ts twoslash import { createResolver, defineNuxtModule, extendPages, extendRouteRules } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { const { resolve } = createResolver(import.meta.url) extendPages((pages) => { pages.unshift({ name: 'preview-new', path: '/preview-new', file: resolve('runtime/preview.vue'), }) }) extendRouteRules('/preview', { redirect: { to: '/preview-new', statusCode: 302, }, }) extendRouteRules('/preview-new', { cache: { maxAge: 60 * 60 * 24 * 7, }, }) }, }) ``` ### Type ```ts function extendRouteRules (route: string, rule: NitroRouteConfig, options?: ExtendRouteRulesOptions): void ``` ### Parameters **route**: A route pattern to match against. **rule**: A route rule configuration to apply to the matched route. ::tip About route rules configurations, you can get more detail in [Hybrid Rendering > Route Rules](https://nuxt.com/docs/3.x/guide/concepts/rendering#route-rules). :: **options**: A object to pass to the route configuration. If `override` is set to `true`, it will override the existing route configuration. | Name | Type | Default | Description | | ---------- | --------- | ------- | -------------------------------------------- | | `override` | `boolean` | `false` | Override route rule config, default is false | ## `addRouteMiddleware` Registers route middlewares to be available for all routes or for specific routes. Route middlewares can be also defined in plugins via [`addRouteMiddleware`](https://nuxt.com/docs/3.x/api/utils/add-route-middleware) composable. ::tip Read more about route middlewares in the [Route middleware documentation](https://nuxt.com/docs/3.x/getting-started/routing#route-middleware). :: ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/adding-route-rules-and-route-middlewares?friend=nuxt" target="_blank"} Watch Vue School video about adding route rules and route middelwares. :: ### Usage ::code-group ```ts twoslash [module.ts] import { addRouteMiddleware, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addRouteMiddleware({ name: 'auth', path: resolve('runtime/auth'), global: true, }, { prepend: true }) }, }) ``` ```ts twoslash [runtime/auth.ts] function isAuthenticated (): boolean { return false } // ---cut--- export default defineNuxtRouteMiddleware((to, from) => { // isAuthenticated() is an example method verifying if a user is authenticated if (to.path !== '/login' && isAuthenticated() === false) { return navigateTo('/login') } }) ``` :: ### Type ```ts function addRouteMiddleware (input: NuxtMiddleware | NuxtMiddleware[], options?: AddRouteMiddlewareOptions): void ``` ### Parameters **input**: A middleware object or an array of middleware objects with the following properties: | Property | Type | Required | Description | | -------- | --------- | -------- | --------------------------------------------------- | | `name` | `string` | `true` | The name of the middleware. | | `path` | `string` | `true` | The file path to the middleware. | | `global` | `boolean` | `false` | If set to `true`, applies middleware to all routes. | **options**: An object with the following properties: | Property | Type | Default | Description | | ---------- | --------- | ------- | ----------------------------------------------------------- | | `override` | `boolean` | `false` | If `true`, replaces middleware with the same name. | | `prepend` | `boolean` | `false` | If `true`, prepends middleware before existing middlewares. | --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/pages.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Plugins" description: "Nuxt Kit provides a set of utilities to help you create and use plugins. You can add plugins or plugin templates to your module using these functions." canonical_url: "https://nuxt.com/docs/3.x/api/kit/plugins" --- # Plugins > Nuxt Kit provides a set of utilities to help you create and use plugins. You can add plugins or plugin templates to your module using these functions. Plugins are self-contained code that usually add app-level functionality to Vue. In Nuxt, plugins are automatically imported from the `plugins/` directory. However, if you need to ship a plugin with your module, Nuxt Kit provides the `addPlugin` and `addPluginTemplate` methods. These utils allow you to customize the plugin configuration to better suit your needs. ## `addPlugin` Registers a Nuxt plugin and adds it to the plugins array. ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/injecting-plugins?friend=nuxt" target="_blank"} Watch Vue School video about `addPlugin`. :: ### Usage ```ts twoslash import { addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addPlugin({ src: resolve('runtime/plugin.js'), mode: 'client', }) }, }) ``` ### Type ```ts function addPlugin (plugin: NuxtPlugin | string, options?: AddPluginOptions): NuxtPlugin ``` ### Parameters **`plugin`**: A plugin object or a string with the path to the plugin. If a string is provided, it will be converted to a plugin object with `src` set to the string value. If a plugin object is provided, it must have the following properties: | Property | Type | Required | Description | | -------- | ------------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `src` | `string` | `true` | Path to the plugin file. | | `mode` | `'all' \| 'server' \| 'client'`{lang="ts"} | `false` | If set to `'all'`, the plugin will be included in both client and server bundles. If set to `'server'`, the plugin will only be included in the server bundle. If set to `'client'`, the plugin will only be included in the client bundle. You can also use `.client` and `.server` modifiers when specifying `src` option to use plugin only in client or server side. | | `order` | `number` | `false` | Order of the plugin. This allows more granular control over plugin order and should only be used by advanced users. Lower numbers run first, and user plugins default to `0`. It's recommended to set `order` to a number between `-20` for `pre`-plugins (plugins that run before Nuxt plugins) and `20` for `post`-plugins (plugins that run after Nuxt plugins). | ::warning Avoid using `order` unless necessary. Use `append` if you simply need to register plugins after Nuxt defaults. :: **`options`**: Optional object with the following properties: | Property | Type | Required | Description | | -------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------- | | `append` | `boolean` | `false` | If `true`, the plugin will be appended to the plugins array. If `false`, it will be prepended. Defaults to `false`. | ### Examples ::code-group ```ts [module.ts] import { addPlugin, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const { resolve } = createResolver(import.meta.url) addPlugin({ src: resolve('runtime/plugin.js'), mode: 'client', }) }, }) ``` ```ts [runtime/plugin.ts] export default defineNuxtPlugin((nuxtApp) => { const colorMode = useColorMode() nuxtApp.hook('app:mounted', () => { if (colorMode.preference !== 'dark') { colorMode.preference = 'dark' } }) }) ``` :: ## `addPluginTemplate` Adds a template and registers as a nuxt plugin. This is useful for plugins that need to generate code at build time. ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/injecting-plugin-templates?friend=nuxt" target="_blank"} Watch Vue School video about `addPluginTemplate`. :: ### Usage ```ts twoslash import { addPluginTemplate, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options) { addPluginTemplate({ filename: 'module-plugin.mjs', getContents: () => `import { defineNuxtPlugin } from '#app/nuxt' export default defineNuxtPlugin({ name: 'module-plugin', setup (nuxtApp) { ${options.log ? 'console.log("Plugin install")' : ''} } })`, }) }, }) ``` ### Type ```ts function addPluginTemplate (pluginOptions: NuxtPluginTemplate, options?: AddPluginOptions): NuxtPlugin ``` ### Parameters **`pluginOptions`**: A plugin template object with the following properties: | Property | Type | Required | Description | | ------------- | --------------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `src` | `string` | `false` | Path to the template. If `src` is not provided, `getContents` must be provided instead. | | `filename` | `string` | `false` | Filename of the template. If `filename` is not provided, it will be generated from the `src` path. In this case, the `src` option is required. | | `dst` | `string` | `false` | Path to the destination file. If `dst` is not provided, it will be generated from the `filename` path and nuxt `buildDir` option. | | `mode` | `'all' \| 'server' \| 'client'`{lang="ts"} | `false` | If set to `'all'`, the plugin will be included in both client and server bundles. If set to `'server'`, the plugin will only be included in the server bundle. If set to `'client'`, the plugin will only be included in the client bundle. You can also use `.client` and `.server` modifiers when specifying `src` option to use plugin only in client or server side. | | `options` | `Record<string, any>`{lang="ts"} | `false` | Options to pass to the template. | | `getContents` | `(data: Record<string, any>) => string \| Promise<string>`{lang="ts"} | `false` | A function that will be called with the `options` object. It should return a string or a promise that resolves to a string. If `src` is provided, this function will be ignored. | | `write` | `boolean` | `false` | If set to `true`, the template will be written to the destination file. Otherwise, the template will be used only in virtual filesystem. | | `order` | `number` | `false` | Order of the plugin. This allows more granular control over plugin order and should only be used by advanced users. Lower numbers run first, and user plugins default to `0`. It's recommended to set `order` to a number between `-20` for `pre`-plugins (plugins that run before Nuxt plugins) and `20` for `post`-plugins (plugins that run after Nuxt plugins). | ::warning Prefer using `getContents` for dynamic plugin generation. Avoid setting `order` unless necessary. :: **`options`**: Optional object with the following properties: | Property | Type | Required | Description | | -------- | --------- | -------- | ------------------------------------------------------------------------------------------------------------------- | | `append` | `boolean` | `false` | If `true`, the plugin will be appended to the plugins array. If `false`, it will be prepended. Defaults to `false`. | ### Examples #### Generate a plugin template with different options Use `addPluginTemplate` when you need to generate plugin code dynamically at build time. This allows you to generate different plugin contents based on the options passed to it. For example, Nuxt internally uses this function to generate Vue app configurations. ```ts twoslash [module.ts] import { addPluginTemplate, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (_, nuxt) { if (nuxt.options.vue.config && Object.values(nuxt.options.vue.config).some(v => v !== null && v !== undefined)) { addPluginTemplate({ filename: 'vue-app-config.mjs', write: true, getContents: () => `import { defineNuxtPlugin } from '#app/nuxt' export default defineNuxtPlugin({ name: 'nuxt:vue-app-config', enforce: 'pre', setup (nuxtApp) { ${Object.keys(nuxt.options.vue.config!) .map(k => `nuxtApp.vueApp.config[${JSON.stringify(k)}] = ${JSON.stringify(nuxt.options.vue.config![k as 'idPrefix'])}`) .join('\n') } } })`, }) } }, }) ``` This generates different plugin code depending on the provided configuration. ::code-group ```ts [nuxt.config.ts] export default defineNuxtConfig({ vue: { config: { idPrefix: 'nuxt', }, }, }) ``` ```ts [#build/vue-app-config.mjs] import { defineNuxtPlugin } from '#app/nuxt' export default defineNuxtPlugin({ name: 'nuxt:vue-app-config', enforce: 'pre', setup (nuxtApp) { nuxtApp.vueApp.config.idPrefix = 'nuxt' }, }) ``` :: #### Using an EJS template to generate a plugin You can also use an EJS template to generate your plugin. Options can be passed through the `options` property and then used within the EJS template to generate the plugin content. ::code-group ```ts [module.ts] import { addPluginTemplate, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { const { resolve } = createResolver(import.meta.url) addPluginTemplate({ src: resolve('templates/plugin.ejs'), filename: 'plugin.mjs', options: { ssr: nuxt.options.ssr, }, }) }, }) ``` ```ejs [templates/plugin.ejs] import { VueFire, useSSRInitialState } from 'vuefire' import { defineNuxtPlugin } from '#imports' export default defineNuxtPlugin((nuxtApp) => { const firebaseApp = nuxtApp.$firebaseApp nuxtApp.vueApp.use(VueFire, { firebaseApp }) <% if(options.ssr) { %> if (import.meta.server) { nuxtApp.payload.vuefire = useSSRInitialState(undefined, firebaseApp) } else if (nuxtApp.payload?.vuefire) { useSSRInitialState(nuxtApp.payload.vuefire, firebaseApp) } <% } %> }) ``` :: ::warning If you set `compatibilityVersion` to `4`, Nuxt no longer uses `lodash.template` to compile templates by default. You can still enable it via the `experimental.compileTemplate` option, but support for EJS templates will be removed entirely in the next major version. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/plugin.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Programmatic Usage" description: "Nuxt Kit provides a set of utilities to help you work with Nuxt programmatically. These functions allow you to load Nuxt, build Nuxt, and load Nuxt configuration." canonical_url: "https://nuxt.com/docs/3.x/api/kit/programmatic" --- # Programmatic Usage > Nuxt Kit provides a set of utilities to help you work with Nuxt programmatically. These functions allow you to load Nuxt, build Nuxt, and load Nuxt configuration. Programmatic usage can be helpful when you want to use Nuxt programmatically, for example, when building a [CLI tool](https://github.com/nuxt/cli) or [test utils](https://github.com/nuxt/test-utils). ## `loadNuxt` Load Nuxt programmatically. It will load the Nuxt configuration, instantiate and return the promise with Nuxt instance. ### Type ```ts function loadNuxt (loadOptions?: LoadNuxtOptions): Promise<Nuxt> ``` ### Parameters **`loadOptions`**: Loading conditions for Nuxt. `loadNuxt` uses [`c12`](https://github.com/unjs/c12) under the hood, so it accepts the same options as `c12.loadConfig` with some additional options: | Property | Type | Required | Description | | -------- | --------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `dev` | `boolean` | `false` | If set to `true`, Nuxt will be loaded in development mode. | | `ready` | `boolean` | `true` | If set to `true`, Nuxt will be ready to use after the `loadNuxt` call. If set to `false`, you will need to call `nuxt.ready()` to make sure Nuxt is ready to use. | ## `buildNuxt` Build Nuxt programmatically. It will invoke the builder (currently [@nuxt/vite-builder](https://github.com/nuxt/nuxt/tree/main/packages/vite) or [@nuxt/webpack-builder](https://github.com/nuxt/nuxt/tree/main/packages/webpack)) to bundle the application. ### Type ```ts function buildNuxt (nuxt: Nuxt): Promise<any> ``` ### Parameters **`nuxt`**: Nuxt instance to build. It can be retrieved from the context via `useNuxt()` call. ## `loadNuxtConfig` Load Nuxt configuration. It will return the promise with the configuration object. ### Type ```ts function loadNuxtConfig (options: LoadNuxtConfigOptions): Promise<NuxtOptions> ``` ### Parameters **`options`**: Options to pass in [`c12`](https://github.com/unjs/c12#options) `loadConfig` call. ## `writeTypes` Generates `tsconfig.json` and writes it to the project buildDir. ### Type ```ts function writeTypes (nuxt?: Nuxt): void ``` ### Parameters **`nuxt`**: Nuxt instance to build. It can be retrieved from the context via `useNuxt()` call. --- - [Source](https://github.com/nuxt/nuxt/tree/main/packages/kit/src/loader) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Resolving" description: "Nuxt Kit provides a set of utilities to help you resolve paths. These functions allow you to resolve paths relative to the current module, with unknown name or extension." canonical_url: "https://nuxt.com/docs/3.x/api/kit/resolving" --- # Resolving > Nuxt Kit provides a set of utilities to help you resolve paths. These functions allow you to resolve paths relative to the current module, with unknown name or extension. Sometimes you need to resolve a path relative to the current module without knowing the name or extension. For example, you may want to add a plugin that is located in the same directory as the module. To handle these cases, Nuxt provides a set of utilities to resolve paths. `resolvePath` and `resolveAlias` are used to resolve paths relative to the current module. `findPath` is used to find the first existing file in a given set of paths. `createResolver` is used to create a resolver relative to the base path. ## `resolvePath` Resolves the full path to a file or directory, respecting Nuxt alias and extensions options. If a path could not be resolved, a normalized input path will be returned. ### Usage ```ts import { defineNuxtModule, resolvePath } from '@nuxt/kit' export default defineNuxtModule({ async setup () { const entrypoint = await resolvePath('@unhead/vue') console.log(`Unhead entrypoint is ${entrypoint}`) }, }) ``` ### Type ```ts function resolvePath (path: string, options?: ResolvePathOptions): Promise<string> ``` ### Parameters **`path`**: A path to resolve. **`options`**: Options to pass to the resolver. This object can have the following properties: | Property | Type | Required | Description | | -------------------- | ----------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | | `cwd` | `string` | `false` | Base for resolving paths from. Default is Nuxt rootDir. | | `alias` | `Record<string, string>`{lang="ts"} | `false` | An object of aliases. Default is Nuxt configured aliases. | | `extensions` | `string[]` | `false` | The file extensions to try. Default is Nuxt configured extensions. | | `virtual` | `boolean` | `false` | Whether to resolve files that exist in the Nuxt VFS (for example, as a Nuxt template). | | `fallbackToOriginal` | `boolean` | `false` | Whether to fallback to the original path if the resolved path does not exist instead of returning the normalized input path. | ### Examples ```ts import { defineNuxtModule, resolvePath } from '@nuxt/kit' import { join } from 'pathe' const headlessComponents: ComponentGroup[] = [ { relativePath: 'combobox/combobox.js', chunkName: 'headlessui/combobox', exports: [ 'Combobox', 'ComboboxLabel', 'ComboboxButton', 'ComboboxInput', 'ComboboxOptions', 'ComboboxOption', ], }, ] export default defineNuxtModule({ meta: { name: 'nuxt-headlessui', configKey: 'headlessui', }, defaults: { prefix: 'Headless', }, async setup (options) { const entrypoint = await resolvePath('@headlessui/vue') const root = join(entrypoint, '../components') for (const group of headlessComponents) { for (const e of group.exports) { addComponent( { name: e, export: e, filePath: join(root, group.relativePath), chunkName: group.chunkName, mode: 'all', }, ) } } }, }) ``` ## `resolveAlias` Resolves path aliases respecting Nuxt alias options. ### Type ```ts function resolveAlias (path: string, alias?: Record<string, string>): string ``` ### Parameters **`path`**: A path to resolve. **`alias`**: An object of aliases. If not provided, it will be read from `nuxt.options.alias`. ## `findPath` Try to resolve first existing file in a given set of paths. ### Usage ```ts import { defineNuxtModule, findPath } from '@nuxt/kit' import { join } from 'pathe' export default defineNuxtModule({ async setup (_, nuxt) { // Resolve main (app.vue) const mainComponent = await findPath([ join(nuxt.options.srcDir, 'App'), join(nuxt.options.srcDir, 'app'), ]) }, }) ``` ### Type ```ts function findPath (paths: string | string[], options?: ResolvePathOptions, pathType: 'file' | 'dir'): Promise<string | null> ``` ### Parameters **`paths`**: A path or an array of paths to resolve. **`options`**: Options to pass to the resolver. This object can have the following properties: | Property | Type | Required | Description | | -------------------- | ----------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------- | | `cwd` | `string` | `false` | Base for resolving paths from. Default is Nuxt rootDir. | | `alias` | `Record<string, string>`{lang="ts"} | `false` | An object of aliases. Default is Nuxt configured aliases. | | `extensions` | `string[]` | `false` | The file extensions to try. Default is Nuxt configured extensions. | | `virtual` | `boolean` | `false` | Whether to resolve files that exist in the Nuxt VFS (for example, as a Nuxt template). | | `fallbackToOriginal` | `boolean` | `false` | Whether to fallback to the original path if the resolved path does not exist instead of returning the normalized input path. | ## `createResolver` Creates resolver relative to base path. ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/resolving-paths-and-injecting-assets-to-the-app?friend=nuxt" target="_blank"} Watch Vue School video about createResolver. :: ### Usage ```ts import { createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup (_, nuxt) { const { resolve, resolvePath } = createResolver(import.meta.url) }, }) ``` ### Type ```ts function createResolver (basePath: string | URL): Resolver ``` ### Parameters **`basePath`**: A base path to resolve from. It can be a string or a URL. ### Return Value The `createResolver` function returns an object with the following properties: | Property | Type | Description | | ------------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | `resolve` | `(path: string) => string`{lang="ts"} | A function that resolves a path relative to the base path. | | `resolvePath` | `(path: string, options?: ResolvePathOptions) => Promise<string>`{lang="ts"} | A function that resolves a path relative to the base path and respects Nuxt alias and extensions options. | ### Examples ```ts import { createResolver, defineNuxtModule, isNuxt2 } from '@nuxt/kit' export default defineNuxtModule({ setup (options, nuxt) { const resolver = createResolver(import.meta.url) nuxt.hook('modules:done', () => { if (isNuxt2()) { addPlugin(resolver.resolve('./runtime/plugin.vue2')) } else { addPlugin(resolver.resolve('./runtime/plugin.vue3')) } }) }, }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/resolve.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Runtime Config" description: "Nuxt Kit provides a set of utilities to help you access and modify Nuxt runtime configuration." canonical_url: "https://nuxt.com/docs/3.x/api/kit/runtime-config" --- # Runtime Config > Nuxt Kit provides a set of utilities to help you access and modify Nuxt runtime configuration. ## `useRuntimeConfig` At build-time, it is possible to access the resolved Nuxt [runtime config](https://nuxt.com/docs/3.x/guide/going-further/runtime-config). ### Type ```ts function useRuntimeConfig (): Record<string, unknown> ``` ## `updateRuntimeConfig` It is also possible to update runtime configuration. This will be merged with the existing runtime configuration, and if Nitro has already been initialized it will trigger an HMR event to reload the Nitro runtime config. ### Type ```ts function updateRuntimeConfig (config: Record<string, unknown>): void | Promise<void> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/runtime-config.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Templates" description: "Nuxt Kit provides a set of utilities to help you work with templates. These functions allow you to generate extra files during development and build time." canonical_url: "https://nuxt.com/docs/3.x/api/kit/templates" --- # Templates > Nuxt Kit provides a set of utilities to help you work with templates. These functions allow you to generate extra files during development and build time. Templates allow you to generate extra files during development and build time. These files will be available in virtual filesystem and can be used in plugins, layouts, components, etc. `addTemplate` and `addTypeTemplate` allow you to add templates to the Nuxt application. `updateTemplates` allows you to regenerate templates that match the filter. ## `addTemplate` Renders given template during build into the virtual file system, and optionally to disk in the project `buildDir` ### Usage ```ts twoslash import { addTemplate, defineNuxtModule } from '@nuxt/kit' import { defu } from 'defu' export default defineNuxtModule({ setup (options, nuxt) { const globalMeta = defu(nuxt.options.app.head, { charset: options.charset, viewport: options.viewport, }) addTemplate({ filename: 'meta.config.mjs', getContents: () => 'export default ' + JSON.stringify({ globalMeta, mixinKey: 'setup' }), }) }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { NuxtTemplate, ResolvedNuxtTemplate } from '@nuxt/schema' // ---cut--- function addTemplate (template: NuxtTemplate | string): ResolvedNuxtTemplate ``` ### Parameters **template**: A template object or a string with the path to the template. If a string is provided, it will be converted to a template object with `src` set to the string value. If a template object is provided, it must have the following properties: | Property | Type | Required | Description | | ------------- | --------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `src` | `string` | `false` | Path to the template. If `src` is not provided, `getContents` must be provided instead. | | `filename` | `string` | `false` | Filename of the template. If `filename` is not provided, it will be generated from the `src` path. In this case, the `src` option is required. | | `dst` | `string` | `false` | Path to the destination file. If `dst` is not provided, it will be generated from the `filename` path and nuxt `buildDir` option. | | `options` | `Options` | `false` | Options to pass to the template. | | `getContents` | `(data: Options) => string \| Promise<string>`{lang="ts"} | `false` | A function that will be called with the `options` object. It should return a string or a promise that resolves to a string. If `src` is provided, this function will be ignored. | | `write` | `boolean` | `false` | If set to `true`, the template will be written to the destination file. Otherwise, the template will be used only in virtual filesystem. | ### Examples #### Creating a Virtual File for Runtime Plugin In this example, we merge an object inside a module and consume the result in a runtime plugin. ```ts twoslash [module.ts] import { addTemplate, defineNuxtModule } from '@nuxt/kit' import { defu } from 'defu' export default defineNuxtModule({ setup (options, nuxt) { const globalMeta = defu(nuxt.options.app.head, { charset: options.charset, viewport: options.viewport, }) addTemplate({ filename: 'meta.config.mjs', getContents: () => 'export default ' + JSON.stringify({ globalMeta, mixinKey: 'setup' }), }) }, }) ``` In the module above, we generate a virtual file named `meta.config.mjs`. In the runtime plugin, we can import it using the `#build` alias: ```ts [runtime/plugin.ts] import { createHead as createServerHead } from '@unhead/vue/server' import { createHead as createClientHead } from '@unhead/vue/client' import { defineNuxtPlugin } from '#imports' // @ts-expect-error - virtual file import metaConfig from '#build/meta.config.mjs' export default defineNuxtPlugin((nuxtApp) => { const createHead = import.meta.server ? createServerHead : createClientHead const head = createHead() head.push(metaConfig.globalMeta) nuxtApp.vueApp.use(head) }) ``` ## `addTypeTemplate` Renders given template during build into the project buildDir, then registers it as types. ### Usage ```ts twoslash import { addTypeTemplate, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addTypeTemplate({ filename: 'types/markdown.d.ts', getContents: () => `declare module '*.md' { import type { ComponentOptions } from 'vue' const Component: ComponentOptions export default Component }`, }) }, }) ``` ### Type ```ts function addTypeTemplate (template: NuxtTypeTemplate | string, context?: { nitro?: boolean, nuxt?: boolean }): ResolvedNuxtTemplate ``` ### Parameters **template**: A template object or a string with the path to the template. If a string is provided, it will be converted to a template object with `src` set to the string value. If a template object is provided, it must have the following properties: | Property | Type | Required | Description | | ------------- | --------------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `src` | `string` | `false` | Path to the template. If `src` is not provided, `getContents` must be provided instead. | | `filename` | `string` | `false` | Filename of the template. If `filename` is not provided, it will be generated from the `src` path. In this case, the `src` option is required. | | `dst` | `string` | `false` | Path to the destination file. If `dst` is not provided, it will be generated from the `filename` path and nuxt `buildDir` option. | | `options` | `Options` | `false` | Options to pass to the template. | | `getContents` | `(data: Options) => string \| Promise<string>`{lang="ts"} | `false` | A function that will be called with the `options` object. It should return a string or a promise that resolves to a string. If `src` is provided, this function will be ignored. | **context**: An optional context object can be passed to control where the type is added. If omitted, the type will only be added to the Nuxt context. This object supports the following properties: | Property | Type | Required | Description | | -------- | --------- | -------- | -------------------------------------------------------------- | | `nuxt` | `boolean` | `false` | If set to `true`, the type will be added to the Nuxt context. | | `nitro` | `boolean` | `false` | If set to `true`, the type will be added to the Nitro context. | ### Examples #### Adding Type Templates to the Nitro Context By default, -- only adds the type declarations to the Nuxt context. To also add them to the Nitro context, set nitro to true. ```ts twoslash import { addTypeTemplate, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addTypeTemplate({ filename: 'types/auth.d.ts', getContents: () => `declare module '#auth-utils' { interface User { id: string; name: string; } }`, }, { nitro: true, }) }, }) ``` This allows the `#auth-utils` module to be used within the Nitro context. ```ts [server/api/auth.ts] import type { User } from '#auth-utils' export default eventHandler(() => { const user: User = { id: '123', name: 'John Doe', } // do something with the user return user }) ``` ## `addServerTemplate` Adds a virtual file that can be used within the Nuxt Nitro server build. ### Usage ```ts twoslash import { addServerTemplate, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addServerTemplate({ filename: '#my-module/test.mjs', getContents () { return 'export const test = 123' }, }) }, }) ``` ### Type ```ts twoslash // @errors: 2391 import type { NuxtServerTemplate } from '@nuxt/schema' // ---cut--- function addServerTemplate (template: NuxtServerTemplate): NuxtServerTemplate ``` ### Parameters **template**: A template object. It must have the following properties: | Property | Type | Required | Description | | ------------- | -------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------- | | `filename` | `string` | `true` | Filename of the template. | | `getContents` | `() => string \| Promise<string>`{lang="ts"} | `true` | A function that will be called with the `options` object. It should return a string or a promise that resolves to a string. | ### Examples ### Creating a Virtual File for Nitro In this example, we create a virtual file that can be used within the Nuxt Nitro server build. ```ts twoslash import { addServerTemplate, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { addServerTemplate({ filename: '#my-module/test.mjs', getContents () { return 'export const test = 123' }, }) }, }) ``` And then in a runtime file ```ts [server/api/test.ts] import { test } from '#my-module/test.js' export default eventHandler(() => { return test }) ``` ## `updateTemplates` Regenerate templates that match the filter. If no filter is provided, all templates will be regenerated. ### Usage ```ts import { defineNuxtModule, updateTemplates } from '@nuxt/kit' import { resolve } from 'pathe' export default defineNuxtModule({ setup (options, nuxt) { const updateTemplatePaths = [ resolve(nuxt.options.srcDir, 'pages'), ] // watch and rebuild routes template list when one of the pages changes nuxt.hook('builder:watch', async (event, relativePath) => { if (event === 'change') { return } const path = resolve(nuxt.options.srcDir, relativePath) if (updateTemplatePaths.some(dir => path.startsWith(dir))) { await updateTemplates({ filter: template => template.filename === 'routes.mjs', }) } }) }, }) ``` ### Type ```ts async function updateTemplates (options: UpdateTemplatesOptions): void ``` ### Parameters **options**: Options to pass to the template. This object can have the following property: | Property | Type | Required | Description | | -------- | -------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `filter` | `(template: ResolvedNuxtTemplate) => boolean`{lang="ts"} | `false` | A function that will be called with the `template` object. It should return a boolean indicating whether the template should be regenerated. If `filter` is not provided, all templates will be regenerated. | --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/kit/src/template.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt Configuration" description: "Discover all the options you can use in your nuxt.config.ts file." canonical_url: "https://nuxt.com/docs/3.x/api/nuxt-config" --- # Nuxt Configuration > Discover all the options you can use in your nuxt.config.ts file. ::note{icon="i-simple-icons-github" to="https://github.com/nuxt/nuxt/tree/main/packages/schema/src/config" target="_blank"} This file is auto-generated from Nuxt source code. :: ## alias You can improve your DX by defining additional aliases to access custom directories within your JavaScript and CSS. - **Type**: `object` - **Default** ```json { "~": "/<srcDir>/", "@": "/<srcDir>/", "~~": "/<rootDir>/", "@@": "/<rootDir>/", "#shared": "/<rootDir>/shared/", "assets": "/<srcDir>/assets/", "public": "/<srcDir>/public/", "#server": "/<srcDir>/server/", "#build": "/<rootDir>/.nuxt/", "#internal/nuxt/paths": "/<rootDir>/.nuxt/paths.mjs" } ``` ::callout **Note**: Within a webpack context (image sources, CSS - but not JavaScript) you *must* access your alias by prefixing it with `~`. :: ::callout **Note**: These aliases will be automatically added to the generated `.nuxt/tsconfig.json` so you can get full type support and path auto-complete. In case you need to extend options provided by `./.nuxt/tsconfig.json` further, make sure to add them here or within the `typescript.tsConfig` property in `nuxt.config`. :: **Example**: ```js export default { alias: { 'images': fileURLToPath(new URL('./assets/images', import.meta.url)), 'style': fileURLToPath(new URL('./assets/style', import.meta.url)), 'data': fileURLToPath(new URL('./assets/other/data', import.meta.url)) } } ``` <!-- ```html <template> <img src="~images/main-bg.jpg"> </template> <script> import data from 'data/test.json' </script> <style> // Uncomment the below //@import '~style/variables.scss'; //@import '~style/utils.scss'; //@import '~style/base.scss'; body { background-image: url('~images/main-bg.jpg'); } </style> ``` --> ## analyzeDir The directory where Nuxt will store the generated files when running `nuxt analyze`. If a relative path is specified, it will be relative to your `rootDir`. - **Type**: `string` - **Default:** `"/<rootDir>/.nuxt/analyze"` ## app Nuxt App configuration. ### `baseURL` The base path of your Nuxt application. For example: - **Type**: `string` - **Default:** `"/"` **Example**: ```ts export default defineNuxtConfig({ app: { baseURL: '/prefix/' } }) ``` <!-- This can also be set at runtime by setting the NUXT_APP_BASE_URL environment variable. --> **Example**: ```bash NUXT_APP_BASE_URL=/prefix/ node .output/server/index.mjs ``` <!-- ::note Relative paths (like `./`) are not supported directly in `nuxt.config.ts` due to Nitro limitations. For static hosting with relative asset paths, use one of these approaches: --> <!-- ```bash # Option 1: Environment variable at build time NUXT_APP_BASE_URL=./ npm run generate ``` --> <!-- ```ts // Option 2: Nitro runtime config export default defineNuxtConfig({ nitro: { runtimeConfig: { app: { baseURL: './', }, }, }, }) ``` :: --> ### `buildAssetsDir` The folder name for the built site assets, relative to `baseURL` (or `cdnURL` if set). This is set at build time and should not be customized at runtime. - **Type**: `string` - **Default:** `"/_nuxt/"` ### `cdnURL` An absolute URL to serve the public folder from (production-only). For example: - **Type**: `string` - **Default:** `""` **Example**: ```ts export default defineNuxtConfig({ app: { cdnURL: 'https://mycdn.org/' } }) ``` <!-- This can be set to a different value at runtime by setting the `NUXT_APP_CDN_URL` environment variable. --> **Example**: ```bash NUXT_APP_CDN_URL=https://mycdn.org/ node .output/server/index.mjs ``` ### `head` Set default configuration for `<head>` on every page. - **Type**: `object` - **Default** ```json { "meta": [ { "name": "viewport", "content": "width=device-width, initial-scale=1" }, { "charset": "utf-8" } ], "link": [], "style": [], "script": [], "noscript": [] } ``` **Example**: ```js app: { head: { meta: [ // <meta name="viewport" content="width=device-width, initial-scale=1"> { name: 'viewport', content: 'width=device-width, initial-scale=1' } ], script: [ // <script src="https://myawesome-lib.js"></script> { src: 'https://awesome-lib.js' } ], link: [ // <link rel="stylesheet" href="https://myawesome-lib.css"> { rel: 'stylesheet', href: 'https://awesome-lib.css' } ], // please note that this is an area that is likely to change style: [ // <style>:root { color: red }</style> { textContent: ':root { color: red }' } ], noscript: [ // <noscript>JavaScript is required</noscript> { textContent: 'JavaScript is required' } ] } } ``` ### `keepalive` Default values for KeepAlive configuration between pages. This can be overridden with `definePageMeta` on an individual page. Only JSON-serializable values are allowed. - **Type**: `boolean` - **Default:** `false` **See**: [Vue KeepAlive](https://vuejs.org/api/built-in-components.html#keepalive) ### `layoutTransition` Default values for layout transitions. This can be overridden with `definePageMeta` on an individual page. Only JSON-serializable values are allowed. - **Type**: `boolean` - **Default:** `false` **See**: [Vue Transition docs](https://vuejs.org/api/built-in-components.html#transition) ### `pageTransition` Default values for page transitions. This can be overridden with `definePageMeta` on an individual page. Only JSON-serializable values are allowed. - **Type**: `boolean` - **Default:** `false` **See**: [Vue Transition docs](https://vuejs.org/api/built-in-components.html#transition) ### `rootAttrs` Customize Nuxt root element id. - **Type**: `object` - **Default** ```json { "id": "__nuxt" } ``` ### `rootId` Customize Nuxt root element id. - **Type**: `string` - **Default:** `"__nuxt"` ### `rootTag` Customize Nuxt root element tag. - **Type**: `string` - **Default:** `"div"` ### `spaLoaderAttrs` Customize Nuxt Nuxt SpaLoader element attributes. #### `id` - **Type**: `string` - **Default:** `"__nuxt-loader"` ### `spaLoaderTag` Customize Nuxt SpaLoader element tag. - **Type**: `string` - **Default:** `"div"` ### `teleportAttrs` Customize Nuxt Teleport element attributes. - **Type**: `object` - **Default** ```json { "id": "teleports" } ``` ### `teleportId` Customize Nuxt Teleport element id. - **Type**: `string` - **Default:** `"teleports"` ### `teleportTag` Customize Nuxt Teleport element tag. - **Type**: `string` - **Default:** `"div"` ### `viewTransition` Default values for view transitions. This only has an effect when **experimental** support for View Transitions is [enabled in your nuxt.config file](https://nuxt.com/docs/3.x/getting-started/transitions#view-transitions-api-experimental). This can be overridden with `definePageMeta` on an individual page. - **Type**: `boolean` - **Default:** `false` **See**: [Nuxt View Transition API docs](https://nuxt.com/docs/getting-started/transitions#view-transitions-api-experimental) ## appConfig Additional app configuration For programmatic usage and type support, you can directly provide app config with this option. It will be merged with `app.config` file as default value. ### `nuxt` ## appId For multi-app projects, the unique id of the Nuxt application. Defaults to `nuxt-app`. - **Type**: `string` - **Default:** `"nuxt-app"` ## build Shared build configuration. ### `analyze` Nuxt allows visualizing your bundles and how to optimize them. Set to `true` to enable bundle analysis, or pass an object with options: [for webpack](https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin) or [for vite](https://github.com/btd/rollup-plugin-visualizer#options). - **Type**: `object` - **Default** ```json { "template": "treemap", "projectRoot": "/<rootDir>", "filename": "/<rootDir>/.nuxt/analyze/{name}.html" } ``` **Example**: ```js analyze: { analyzerMode: 'static' } ``` ### `templates` It is recommended to use `addTemplate` from `@nuxt/kit` instead of this option. - **Type**: `array` **Example**: ```js templates: [ { src: '~/modules/support/plugin.js', // `src` can be absolute or relative dst: 'support.js', // `dst` is relative to project `.nuxt` dir } ] ``` ### `transpile` If you want to transpile specific dependencies with Babel, you can add them here. Each item in transpile can be a package name, a function, a string or regex object matching the dependency's file name. You can also use a function to conditionally transpile. The function will receive an object (). - **Type**: `array` **Example**: ```js transpile: [({ isLegacy }) => isLegacy && 'ky'] ``` ## buildDir Define the directory where your built Nuxt files will be placed. Many tools assume that `.nuxt` is a hidden directory (because it starts with a `.`). If that is a problem, you can use this option to prevent that. - **Type**: `string` - **Default:** `"/<rootDir>/.nuxt"` **Example**: ```js export default { buildDir: 'nuxt-build' } ``` ## buildId A unique identifier matching the build. This may contain the hash of the current state of the project. - **Type**: `string` - **Default:** `"cf819bfb-dece-40e8-ad97-a4091ee7fe95"` ## builder The builder to use for bundling the Vue part of your application. - **Type**: `string` - **Default:** `"@nuxt/vite-builder"` ## compatibilityDate Specify a compatibility date for your app. This is used to control the behavior of presets in Nitro, Nuxt Image and other modules that may change behavior without a major version bump. We plan to improve the tooling around this feature in the future. ## components Configure Nuxt component auto-registration. Any components in the directories configured here can be used throughout your pages, layouts (and other components) without needing to explicitly import them. - **Type**: `object` - **Default** ```json { "dirs": [ { "path": "~/components/global", "global": true }, "~/components" ] } ``` **See**: [`components/` directory documentation](https://nuxt.com/docs/guide/directory-structure/components) ## css You can define the CSS files/modules/libraries you want to set globally (included in every page). Nuxt will automatically guess the file type by its extension and use the appropriate pre-processor. You will still need to install the required loader if you need to use them. - **Type**: `array` **Example**: ```js css: [ // Load a Node.js module directly (here it's a Sass file). 'bulma', // CSS file in the project '~/assets/css/main.css', // SCSS file in the project '~/assets/css/main.scss' ] ``` ## debug Set to `true` to enable debug mode. At the moment, it prints out hook names and timings on the server, and logs hook arguments as well in the browser. You can also set this to an object to enable specific debug options. - **Type**: `boolean` - **Default:** `false` ## dev Whether Nuxt is running in development mode. Normally, you should not need to set this. - **Type**: `boolean` - **Default:** `false` ## devServer ### `cors` Set CORS options for the dev server #### `origin` - **Type**: `array` - **Default** ```json [ {} ] ``` ### `host` Dev server listening host ### `https` Whether to enable HTTPS. - **Type**: `boolean` - **Default:** `false` **Example**: ```ts export default defineNuxtConfig({ devServer: { https: { key: './server.key', cert: './server.crt' } } }) ``` ### `loadingTemplate` Template to show a loading screen - **Type**: `function` ### `port` Dev server listening port - **Type**: `number` - **Default:** `3000` ### `url` Listening dev server URL. This should not be set directly as it will always be overridden by the dev server with the full URL (for module and internal use). - **Type**: `string` - **Default:** `"http://localhost:3000"` ## devServerHandlers Nitro development-only server handlers. - **Type**: `array` **See**: [Nitro server routes documentation](https://nitro.build/guide/routing) ## devtools Enable Nuxt DevTools for development. Breaking changes for devtools might not reflect on the version of Nuxt. **See**: [Nuxt DevTools](https://devtools.nuxt.com/) for more information. ## dir Customize default directory structure used by Nuxt. It is better to stick with defaults unless needed. ### `app` - **Type**: `string` - **Default:** `"app"` ### `assets` The assets directory (aliased as `~assets` in your build). - **Type**: `string` - **Default:** `"assets"` ### `layouts` The layouts directory, each file of which will be auto-registered as a Nuxt layout. - **Type**: `string` - **Default:** `"layouts"` ### `middleware` The middleware directory, each file of which will be auto-registered as a Nuxt middleware. - **Type**: `string` - **Default:** `"middleware"` ### `modules` The modules directory, each file in which will be auto-registered as a Nuxt module. - **Type**: `string` - **Default:** `"modules"` ### `pages` The directory which will be processed to auto-generate your application page routes. - **Type**: `string` - **Default:** `"pages"` ### `plugins` The plugins directory, each file of which will be auto-registered as a Nuxt plugin. - **Type**: `string` - **Default:** `"plugins"` ### `public` The directory containing your static files, which will be directly accessible via the Nuxt server and copied across into your `dist` folder when your app is generated. - **Type**: `string` - **Default:** `"public"` ### `shared` The shared directory. This directory is shared between the app and the server. - **Type**: `string` - **Default:** `"shared"` ### `static` - **Type**: `string` - **Default:** `"public"` ## esbuild ### `options` Configure shared esbuild options used within Nuxt and passed to other builders, such as Vite or Webpack. #### `jsxFactory` - **Type**: `string` - **Default:** `"h"` #### `jsxFragment` - **Type**: `string` - **Default:** `"Fragment"` #### `target` - **Type**: `string` - **Default:** `"esnext"` #### `tsconfigRaw` - **Type**: `object` ## experimental ### `alwaysRunFetchOnKeyChange` Whether to run `useFetch` when the key changes, even if it is set to `immediate: false` and it has not been triggered yet. `useFetch` and `useAsyncData` will always run when the key changes if `immediate: true` or if it has been already triggered. - **Type**: `boolean` - **Default:** `true` ### `appManifest` Use app manifests to respect route rules on client-side. - **Type**: `boolean` - **Default:** `true` ### `asyncContext` Enable native async context to be accessible for nested composables - **Type**: `boolean` - **Default:** `false` **See**: [Nuxt PR #20918](https://github.com/nuxt/nuxt/pull/20918) ### `asyncEntry` Set to true to generate an async entry point for the Vue bundle (for module federation support). - **Type**: `boolean` - **Default:** `false` ### `browserDevtoolsTiming` Enable timings for Nuxt application hooks in the performance panel of Chromium-based browsers. This feature adds performance markers for Nuxt hooks, allowing you to track their execution time in the browser's Performance tab. This is particularly useful for debugging performance issues. - **Type**: `boolean` - **Default:** `false` **Example**: ```ts // nuxt.config.ts export default defineNuxtConfig({ experimental: { // Enable performance markers for Nuxt hooks in browser devtools browserDevtoolsTiming: true } }) ``` **See**: [PR #29922](https://github.com/nuxt/nuxt/pull/29922) **See**: [Chrome DevTools Performance API](https://developer.chrome.com/docs/devtools/performance/extension#tracks) ### `buildCache` Cache Nuxt/Nitro build artifacts based on a hash of the configuration and source files. This only works for source files within `srcDir` and `serverDir` for the Vue/Nitro parts of your app. - **Type**: `boolean` - **Default:** `false` ### `checkOutdatedBuildInterval` Set the time interval (in ms) to check for new builds. Disabled when `experimental.appManifest` is `false`. Set to `false` to disable. - **Type**: `number` - **Default:** `3600000` ### `chromeDevtoolsProjectSettings` Enable integration with Chrome DevTools Workspaces for Nuxt projects. - **Type**: `boolean` - **Default:** `true` **See**: [Chrome DevTools Project Settings](https://docs.google.com/document/d/1rfKPnxsNuXhnF7AiQZhu9kIwdiMS5hnAI05HBwFuBSM) ### `clientFallback` Whether to enable the experimental `<NuxtClientFallback>` component for rendering content on the client if there's an error in SSR. - **Type**: `boolean` - **Default:** `false` ### `clientNodeCompat` Automatically polyfill Node.js imports in the client build using `unenv`. - **Type**: `boolean` - **Default:** `false` **See**: [unenv](https://github.com/unjs/unenv) <!-- **Note:** To make globals like `Buffer` work in the browser, you need to manually inject them. --> <!-- ```ts import { Buffer } from 'node:buffer' globalThis.Buffer = globalThis.Buffer || Buffer ``` --> ### `compileTemplate` Whether to use `lodash.template` to compile Nuxt templates. This flag will be removed with the release of v4 and exists only for advance testing within Nuxt v3.12+ or in [the nightly release channel](https://nuxt.com/docs/3.x/guide/going-further/nightly-release-channel). - **Type**: `boolean` - **Default:** `true` ### `componentIslands` Experimental component islands support with `<NuxtIsland>` and `.island.vue` files. By default it is set to 'auto', which means it will be enabled only when there are islands, server components or server pages in your app. - **Type**: `string` - **Default:** `"auto"` ### `configSchema` Config schema support - **Type**: `boolean` - **Default:** `true` **See**: [Nuxt Issue #15592](https://github.com/nuxt/nuxt/issues/15592) ### `cookieStore` Enables CookieStore support to listen for cookie updates (if supported by the browser) and refresh `useCookie` ref values. - **Type**: `boolean` - **Default:** `true` **See**: [CookieStore](https://developer.mozilla.org/en-US/docs/Web/API/CookieStore) ### `crossOriginPrefetch` Enable cross-origin prefetch using the Speculation Rules API. - **Type**: `boolean` - **Default:** `false` ### `debugModuleMutation` Record mutations to `nuxt.options` in module context, helping to debug configuration changes made by modules during the Nuxt initialization phase. When enabled, Nuxt will track which modules modify configuration options, making it easier to trace unexpected configuration changes. - **Type**: `boolean` - **Default:** `false` **Example**: ```ts // nuxt.config.ts export default defineNuxtConfig({ experimental: { // Enable tracking of config mutations by modules debugModuleMutation: true } }) ``` **See**: [PR #30555](https://github.com/nuxt/nuxt/pull/30555) ### `decorators` Enable to use experimental decorators in Nuxt and Nitro. - **Type**: `boolean` - **Default:** `false` **See**: <https://github.com/tc39/proposal-decorators> ### `defaults` This allows specifying the default options for core Nuxt components and composables. These options will likely be moved elsewhere in the future, such as into `app.config` or into the `app/` directory. #### `nuxtLink` ##### `componentName` - **Type**: `string` - **Default:** `"NuxtLink"` ##### `prefetch` - **Type**: `boolean` - **Default:** `true` ##### `prefetchOn` ###### `visibility` - **Type**: `boolean` - **Default:** `true` #### `useAsyncData` Options that apply to `useAsyncData` (and also therefore `useFetch`) ##### `deep` - **Type**: `boolean` - **Default:** `true` ##### `errorValue` - **Type**: `string` - **Default:** `"null"` ##### `value` - **Type**: `string` - **Default:** `"null"` #### `useFetch` ### `emitRouteChunkError` Emit `app:chunkError` hook when there is an error loading vite/webpack chunks. By default, Nuxt will also perform a reload of the new route when a chunk fails to load when navigating to a new route (`automatic`). Setting `automatic-immediate` will lead Nuxt to perform a reload of the current route right when a chunk fails to load (instead of waiting for navigation). You can disable automatic handling by setting this to `false`, or handle chunk errors manually by setting it to `manual`. - **Type**: `string` - **Default:** `"automatic"` **See**: [Nuxt PR #19038](https://github.com/nuxt/nuxt/pull/19038) ### `enforceModuleCompatibility` Whether Nuxt should stop if a Nuxt module is incompatible. - **Type**: `boolean` - **Default:** `false` ### `entryImportMap` - **Type**: `boolean` - **Default:** `true` ### `externalVue` Externalize `vue`, `@vue/*` and `vue-router` when building. - **Type**: `boolean` - **Default:** `true` **See**: [Nuxt Issue #13632](https://github.com/nuxt/nuxt/issues/13632) ### `extraPageMetaExtractionKeys` Configure additional keys to extract from the page metadata when using `scanPageMeta`. This allows modules to access additional metadata from the page metadata. It's recommended to augment the NuxtPage types with your keys. - **Type**: `array` ### `extractAsyncDataHandlers` - **Type**: `boolean` - **Default:** `false` ### `granularCachedData` Whether to call and use the result from `getCachedData` on manual refresh for `useAsyncData` and `useFetch`. - **Type**: `boolean` - **Default:** `false` ### `headNext` Use new experimental head optimisations: - Add the capo.js head plugin in order to render tags in of the head in a more performant way. - Uses the hash hydration plugin to reduce initial hydration - **Type**: `boolean` - **Default:** `true` **See**: [Nuxt Discussion #22632](https://github.com/nuxt/nuxt/discussions/22632) ### `inlineRouteRules` Allow defining `routeRules` directly within your `~/pages` directory using `defineRouteRules`. Rules are converted (based on the path) and applied for server requests. For example, a rule defined in `~/pages/foo/bar.vue` will be applied to `/foo/bar` requests. A rule in `~/pages/foo/[id].vue` will be applied to `/foo/**` requests. For more control, such as if you are using a custom `path` or `alias` set in the page's `definePageMeta`, you should set `routeRules` directly within your `nuxt.config`. - **Type**: `boolean` - **Default:** `false` ### `lazyHydration` Enable automatic configuration of hydration strategies for `<Lazy>` components. This feature intelligently determines when to hydrate lazy components based on visibility, idle time, or other triggers, improving performance by deferring hydration of components until they're needed. - **Type**: `boolean` - **Default:** `true` **Example**: ```ts // nuxt.config.ts export default defineNuxtConfig({ experimental: { lazyHydration: true // Enable smart hydration strategies for Lazy components } }) // In your Vue components <template> <Lazy> <ExpensiveComponent /> </Lazy> </template> ``` **See**: [PR #26468](https://github.com/nuxt/nuxt/pull/26468) ### `localLayerAliases` Resolve `~`, `~~`, `@` and `@@` aliases located within layers with respect to their layer source and root directories. - **Type**: `boolean` - **Default:** `true` ### `navigationRepaint` Wait for a single animation frame before navigation, which gives an opportunity for the browser to repaint, acknowledging user interaction. It can reduce INP when navigating on prerendered routes. - **Type**: `boolean` - **Default:** `true` ### `nitroAutoImports` - **Type**: `boolean` - **Default:** `true` ### `noVueServer` Disable vue server renderer endpoint within nitro. - **Type**: `boolean` - **Default:** `false` ### `normalizeComponentNames` Ensure that auto-generated Vue component names match the full component name you would use to auto-import the component. - **Type**: `boolean` - **Default:** `false` ### `parseErrorData` Whether to parse `error.data` when rendering a server error page. - **Type**: `boolean` - **Default:** `false` ### `payloadExtraction` When this option is enabled (by default) payload of pages that are prerendered are extracted - **Type**: `boolean` - **Default:** `true` ### `pendingWhenIdle` For `useAsyncData` and `useFetch`, whether `pending` should be `true` when data has not yet started to be fetched. - **Type**: `boolean` - **Default:** `true` ### `polyfillVueUseHead` Whether or not to add a compatibility layer for modules, plugins or user code relying on the old `@vueuse/head` API. This is disabled to reduce the client-side bundle by \~0.5kb. - **Type**: `boolean` - **Default:** `false` ### `purgeCachedData` Whether to clean up Nuxt static and asyncData caches on route navigation. Nuxt will automatically purge cached data from `useAsyncData` and `nuxtApp.static.data`. This helps prevent memory leaks and ensures fresh data is loaded when needed, but it is possible to disable it. - **Type**: `boolean` - **Default:** `true` **Example**: ```ts // nuxt.config.ts export default defineNuxtConfig({ experimental: { // Disable automatic cache cleanup (default is true) purgeCachedData: false } }) ``` **See**: [PR #31379](https://github.com/nuxt/nuxt/pull/31379) ### `relativeWatchPaths` Whether to provide relative paths in the `builder:watch` hook. This flag will be removed with the release of v4 and exists only for advance testing within Nuxt v3.12+ or in [the nightly release channel](https://nuxt.com/docs/3.x/guide/going-further/nightly-release-channel). - **Type**: `boolean` - **Default:** `true` ### `renderJsonPayloads` Render JSON payloads with support for revivifying complex types. - **Type**: `boolean` - **Default:** `true` ### `resetAsyncDataToUndefined` Whether `clear` and `clearNuxtData` should reset async data to its *default* value or update it to `null`/`undefined`. - **Type**: `boolean` - **Default:** `true` ### `respectNoSSRHeader` Allow disabling Nuxt SSR responses by setting the `x-nuxt-no-ssr` header. - **Type**: `boolean` - **Default:** `false` ### `restoreState` Whether to restore Nuxt app state from `sessionStorage` when reloading the page after a chunk error or manual `reloadNuxtApp()` call. To avoid hydration errors, it will be applied only after the Vue app has been mounted, meaning there may be a flicker on initial load. Consider carefully before enabling this as it can cause unexpected behavior, and consider providing explicit keys to `useState` as auto-generated keys may not match across builds. - **Type**: `boolean` - **Default:** `false` ### `runtimeBaseURL` - **Type**: `boolean` - **Default:** `false` ### `scanPageMeta` Allow exposing some route metadata defined in `definePageMeta` at build-time to modules (alias, name, path, redirect, props, middleware). This only works with static or strings/arrays rather than variables or conditional assignment. - **Type**: `boolean` - **Default:** `true` **See**: [Nuxt Issues #24770](https://github.com/nuxt/nuxt/issues/24770) ### `serverAppConfig` - **Type**: `boolean` - **Default:** `true` ### `sharedPrerenderData` Automatically share payload *data* between pages that are prerendered. This can result in a significant performance improvement when prerendering sites that use `useAsyncData` or `useFetch` and fetch the same data in different pages. It is particularly important when enabling this feature to make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` should do this automatically for you.) - **Type**: `boolean` - **Default:** `false` **Example**: ```ts // This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference // to the data fetched, but Nuxt can't know that because it's not reflected in the key. const route = useRoute() const { data } = await useAsyncData(async () => { return await $fetch(`/api/my-page/${route.params.slug}`) }) // Instead, you should use a key that uniquely identifies the data fetched. const { data } = await useAsyncData(route.params.slug, async () => { return await $fetch(`/api/my-page/${route.params.slug}`) }) ``` ### `spaLoadingTemplateLocation` Keep showing the spa-loading-template until suspense:resolve - **Type**: `string` - **Default:** `"within"` **See**: [Nuxt Issues #21721](https://github.com/nuxt/nuxt/issues/21721) ### `templateImportResolution` Disable resolving imports into Nuxt templates from the path of the module that added the template. By default, Nuxt attempts to resolve imports in templates relative to the module that added them. Setting this to `false` disables this behavior, which may be useful if you're experiencing resolution conflicts in certain environments. - **Type**: `boolean` - **Default:** `true` **Example**: ```ts // nuxt.config.ts export default defineNuxtConfig({ experimental: { // Disable template import resolution from module path templateImportResolution: false } }) ``` **See**: [PR #31175](https://github.com/nuxt/nuxt/pull/31175) ### `templateRouteInjection` By default the route object returned by the auto-imported `useRoute()` composable is kept in sync with the current page in view in `<NuxtPage>`. This is not true for `vue-router`'s exported `useRoute` or for the default `$route` object available in your Vue templates. By enabling this option a mixin will be injected to keep the `$route` template object in sync with Nuxt's managed `useRoute()`. - **Type**: `boolean` - **Default:** `true` ### `templateUtils` Whether to provide a legacy `templateUtils` object (with `serialize`, `importName` and `importSources`) when compiling Nuxt templates. This flag will be removed with the release of v4 and exists only for advance testing within Nuxt v3.12+ or in [the nightly release channel](https://nuxt.com/docs/3.x/guide/going-further/nightly-release-channel). - **Type**: `boolean` - **Default:** `true` ### `treeshakeClientOnly` Tree shakes contents of client-only components from server bundle. - **Type**: `boolean` - **Default:** `true` **See**: [Nuxt PR #5750](https://github.com/nuxt/framework/pull/5750) ### `typedPages` Enable the new experimental typed router using [unplugin-vue-router](https://github.com/posva/unplugin-vue-router). - **Type**: `boolean` - **Default:** `false` ### `viewTransition` Enable View Transition API integration with client-side router. - **Type**: `boolean` - **Default:** `false` **See**: [View Transitions API](https://developer.chrome.com/docs/web-platform/view-transitions) ### `viteEnvironmentApi` - **Type**: `boolean` - **Default:** `false` ### `watcher` Set an alternative watcher that will be used as the watching service for Nuxt. Nuxt uses 'chokidar-granular' if your source directory is the same as your root directory . This will ignore top-level directories (like `node_modules` and `.git`) that are excluded from watching. You can set this instead to `parcel` to use `@parcel/watcher`, which may improve performance in large projects or on Windows platforms. You can also set this to `chokidar` to watch all files in your source directory. - **Type**: `string` - **Default:** `"chokidar"` **See**: [chokidar](https://github.com/paulmillr/chokidar) **See**: [@parcel/watcher](https://github.com/parcel-bundler/watcher) ### `writeEarlyHints` Write early hints when using node server. - **Type**: `boolean` - **Default:** `false` ::callout **Note**: nginx does not support 103 Early hints in the current version. :: ## extends Extend project from multiple local or remote sources. Value should be either a string or array of strings pointing to source directories or config path relative to current config. You can use `github:`, `gh:` `gitlab:` or `bitbucket:` **See**: [`c12` docs on extending config layers](https://github.com/unjs/c12#extending-config-layer-from-remote-sources) **See**: [`giget` documentation](https://github.com/unjs/giget) ## extensions The extensions that should be resolved by the Nuxt resolver. - **Type**: `array` - **Default** ```json [ ".js", ".jsx", ".mjs", ".ts", ".tsx", ".vue" ] ``` ## features Some features of Nuxt are available on an opt-in basis, or can be disabled based on your needs. ### `devLogs` Stream server logs to the client as you are developing. These logs can be handled in the `dev:ssr-logs` hook. If set to `silent`, the logs will not be printed to the browser console. - **Type**: `boolean` - **Default:** `false` ### `inlineStyles` Inline styles when rendering HTML (currently vite only). You can also pass a function that receives the path of a Vue component and returns a boolean indicating whether to inline the styles for that component. - **Type**: `boolean` - **Default:** `true` ### `noScripts` Turn off rendering of Nuxt scripts and JS resource hints. You can also disable scripts more granularly within `routeRules`. If set to 'production' or `true`, JS will be disabled in production mode only. - **Type**: `boolean` - **Default:** `false` ## future `future` is for early opting-in to new features that will become default in a future (possibly major) version of the framework. ### `compatibilityVersion` Enable early access to Nuxt v4 features or flags. Setting `compatibilityVersion` to `4` changes defaults throughout your Nuxt configuration, but you can granularly re-enable Nuxt v3 behaviour when testing (see example). Please file issues if so, so that we can address in Nuxt or in the ecosystem. - **Type**: `number` - **Default:** `3` **Example**: ```ts export default defineNuxtConfig({ future: { compatibilityVersion: 4, }, // To re-enable _all_ Nuxt v3 behaviour, set the following options: srcDir: '.', dir: { app: 'app' }, experimental: { compileTemplate: true, templateUtils: true, relativeWatchPaths: true, resetAsyncDataToUndefined: true, defaults: { useAsyncData: { deep: true } } }, unhead: { renderSSRHeadOptions: { omitLineBreaks: false } } }) ``` ### `multiApp` This enables early access to the experimental multi-app support. - **Type**: `boolean` - **Default:** `false` **See**: [Nuxt Issue #21635](https://github.com/nuxt/nuxt/issues/21635) ### `typescriptBundlerResolution` This enables 'Bundler' module resolution mode for TypeScript, which is the recommended setting for frameworks like Nuxt and Vite. It improves type support when using modern libraries with `exports`. You can set it to false to use the legacy 'Node' mode, which is the default for TypeScript. - **Type**: `boolean` - **Default:** `true` **See**: [TypeScript PR implementing `bundler` module resolution](https://github.com/microsoft/TypeScript/pull/51669) ## generate ### `exclude` This option is no longer used. Instead, use `nitro.prerender.ignore`. - **Type**: `array` <!-- @deprecated --> ### `routes` The routes to generate. If you are using the crawler, this will be only the starting point for route generation. This is often necessary when using dynamic routes. It is preferred to use `nitro.prerender.routes`. - **Type**: `array` **Example**: ```js routes: ['/users/1', '/users/2', '/users/3'] ``` ## hooks Hooks are listeners to Nuxt events that are typically used in modules, but are also available in `nuxt.config`. Internally, hooks follow a naming pattern using colons (e.g., build:done). For ease of configuration, you can also structure them as an hierarchical object in `nuxt.config` (as below). **Example**: ```js import fs from 'node:fs' import path from 'node:path' export default { hooks: { build: { done(builder) { const extraFilePath = path.join( builder.nuxt.options.buildDir, 'extra-file' ) fs.writeFileSync(extraFilePath, 'Something extra') } } } } ``` ## ignore More customizable than `ignorePrefix`: all files matching glob patterns specified inside the `ignore` array will be ignored in building. - **Type**: `array` - **Default** ```json [ "**/*.stories.{js,cts,mts,ts,jsx,tsx}", "**/*.{spec,test}.{js,cts,mts,ts,jsx,tsx}", "**/*.d.{cts,mts,ts}", "**/*.d.vue.{cts,mts,ts}", "**/.{pnpm-store,vercel,netlify,output,git,cache,data,direnv}", "/vendor", "**/node-compile-cache", "**/test-results", "**/*.sock", ".nuxt/analyze", ".nuxt", "**/-*.*" ] ``` ## ignoreOptions Pass options directly to `node-ignore` (which is used by Nuxt to ignore files). **See**: [node-ignore](https://github.com/kaelzhang/node-ignore) **Example**: ```js ignoreOptions: { ignorecase: false } ``` ## ignorePrefix Any file in `pages/`, `layouts/`, `middleware/`, and `public/` directories will be ignored during the build process if its filename starts with the prefix specified by `ignorePrefix`. This is intended to prevent certain files from being processed or served in the built application. By default, the `ignorePrefix` is set to '-', ignoring any files starting with '-'. - **Type**: `string` - **Default:** `"-"` ## imports Configure how Nuxt auto-imports composables into your application. **See**: [Nuxt documentation](https://nuxt.com/docs/guide/directory-structure/composables) ### `dirs` An array of custom directories that will be auto-imported. Note that this option will not override the default directories (\~/composables, \~/utils). - **Type**: `array` **Example**: ```js imports: { // Auto-import pinia stores defined in `~/stores` dirs: ['stores'] } ``` ### `global` - **Type**: `boolean` - **Default:** `false` ### `scan` Whether to scan your `composables/` and `utils/` directories for composables to auto-import. Auto-imports registered by Nuxt or other modules, such as imports from `vue` or `nuxt`, will still be enabled. - **Type**: `boolean` - **Default:** `true` ## logLevel Log level when building logs. Defaults to 'silent' when running in CI or when a TTY is not available. This option is then used as 'silent' in Vite and 'none' in Webpack - **Type**: `string` - **Default:** `"info"` ## modules Modules are Nuxt extensions which can extend its core functionality and add endless integrations. Each module is either a string (which can refer to a package, or be a path to a file), a tuple with the module as first string and the options as a second object, or an inline module function. Nuxt tries to resolve each item in the modules array using node require path (in `node_modules`) and then will be resolved from project `srcDir` if `~` alias is used. - **Type**: `array` ::callout **Note**: Modules are executed sequentially so the order is important. First, the modules defined in `nuxt.config.ts` are loaded. Then, modules found in the `modules/` directory are executed, and they load in alphabetical order. :: **Example**: ```js modules: [ // Using package name '@nuxtjs/axios', // Relative to your project srcDir '~/modules/awesome.js', // Providing options ['@nuxtjs/google-analytics', { ua: 'X1234567' }], // Inline definition function () {} ] ``` ## modulesDir Used to set the modules directories for path resolving (for example, webpack's `resolveLoading`, `nodeExternals` and `postcss`). The configuration path is relative to `options.rootDir` (default is current working directory). Setting this field may be necessary if your project is organized as a yarn workspace-styled mono-repository. - **Type**: `array` - **Default** ```json [ "/<rootDir>/node_modules" ] ``` **Example**: ```js export default { modulesDir: ['../../node_modules'] } ``` ## nitro Configuration for Nitro. **See**: [Nitro configuration docs](https://nitro.build/config/) ### `routeRules` - **Type**: `object` ### `runtimeConfig` - **Type**: `object` - **Default** ```json { "public": {}, "app": { "buildId": "cf819bfb-dece-40e8-ad97-a4091ee7fe95", "baseURL": "/", "buildAssetsDir": "/_nuxt/", "cdnURL": "" }, "nitro": { "envPrefix": "NUXT_" } } ``` ## optimization Build time optimization configuration. ### `asyncTransforms` Options passed directly to the transformer from `unctx` that preserves async context after `await`. #### `asyncFunctions` - **Type**: `array` - **Default** ```json [ "defineNuxtPlugin", "defineNuxtRouteMiddleware" ] ``` #### `objectDefinitions` ##### `defineNuxtComponent` - **Type**: `array` - **Default** ```json [ "asyncData", "setup" ] ``` ##### `defineNuxtPlugin` - **Type**: `array` - **Default** ```json [ "setup" ] ``` ##### `definePageMeta` - **Type**: `array` - **Default** ```json [ "middleware", "validate" ] ``` ### `keyedComposables` Functions to inject a key for. As long as the number of arguments passed to the function is less than `argumentLength`, an additional magic string will be injected that can be used to deduplicate requests between server and client. You will need to take steps to handle this additional key. The key will be unique based on the location of the function being invoked within the file. - **Type**: `array` - **Default** ```json [ { "name": "callOnce", "argumentLength": 3, "source": "#app/composables/once" }, { "name": "defineNuxtComponent", "argumentLength": 2, "source": "#app/composables/component" }, { "name": "useState", "argumentLength": 2, "source": "#app/composables/state" }, { "name": "useFetch", "argumentLength": 3, "source": "#app/composables/fetch" }, { "name": "useAsyncData", "argumentLength": 3, "source": "#app/composables/asyncData" }, { "name": "useLazyAsyncData", "argumentLength": 3, "source": "#app/composables/asyncData" }, { "name": "useLazyFetch", "argumentLength": 3, "source": "#app/composables/fetch" } ] ``` ### `treeShake` Tree shake code from specific builds. #### `composables` Tree shake composables from the server or client builds. **Example**: ```js treeShake: { client: { myPackage: ['useServerOnlyComposable'] } } ``` ##### `client` - **Type**: `object` - **Default** ```json { "vue": [ "onRenderTracked", "onRenderTriggered", "onServerPrefetch" ], "#app": [ "definePayloadReducer", "definePageMeta", "onPrehydrate" ] } ``` ##### `server` - **Type**: `object` - **Default** ```json { "vue": [ "onMounted", "onUpdated", "onUnmounted", "onBeforeMount", "onBeforeUpdate", "onBeforeUnmount", "onRenderTracked", "onRenderTriggered", "onActivated", "onDeactivated" ], "#app": [ "definePayloadReviver", "definePageMeta" ] } ``` ## oxc Configure shared oxc options used within Nuxt and passed where necessary. ### `transform` Options for `oxc-transform` **See**: [Oxc transform docs](https://oxc.rs/docs/guide/usage/transformer.html) #### `options` ##### `jsxFactory` - **Type**: `string` - **Default:** `"h"` ##### `jsxFragment` - **Type**: `string` - **Default:** `"Fragment"` ##### `target` - **Type**: `string` - **Default:** `"esnext"` ## pages Whether to use the vue-router integration in Nuxt 3. If you do not provide a value it will be enabled if you have a `pages/` directory in your source folder. Additionally, you can provide a glob pattern or an array of patterns to scan only certain files for pages. **Example**: ```js pages: { pattern: ['**\/*\/*.vue', '!**\/*.spec.*'], } ``` ## plugins An array of nuxt app plugins. Each plugin can be a string (which can be an absolute or relative path to a file). If it ends with `.client` or `.server` then it will be automatically loaded only in the appropriate context. It can also be an object with `src` and `mode` keys. - **Type**: `array` ::callout **Note**: Plugins are also auto-registered from the `~/plugins` directory and these plugins do not need to be listed in `nuxt.config` unless you need to customize their order. All plugins are deduplicated by their src path. :: **See**: [`plugins/` directory documentation](https://nuxt.com/docs/guide/directory-structure/plugins) **Example**: ```js plugins: [ '~/plugins/foo.client.js', // only in client side '~/plugins/bar.server.js', // only in server side '~/plugins/baz.js', // both client & server { src: '~/plugins/both-sides.js' }, { src: '~/plugins/client-only.js', mode: 'client' }, // only on client side { src: '~/plugins/server-only.js', mode: 'server' } // only on server side ] ``` ## postcss ### `order` A strategy for ordering PostCSS plugins. - **Type**: `function` ### `plugins` Options for configuring PostCSS plugins. **See**: [PostCSS docs](https://postcss.org/) #### `autoprefixer` Plugin to parse CSS and add vendor prefixes to CSS rules. **See**: [`autoprefixer`](https://github.com/postcss/autoprefixer) #### `cssnano` - **Type**: `object` **See**: [`cssnano` configuration options](https://cssnano.github.io/cssnano/docs/config-file/#configuration-options) ## rootDir Define the root directory of your application. This property can be overwritten (for example, running `nuxt ./my-app/` will set the `rootDir` to the absolute path of `./my-app/` from the current/working directory. It is normally not needed to configure this option. - **Type**: `string` - **Default:** `"/<rootDir>"` ## routeRules Global route options applied to matching server routes. **Experimental**: This is an experimental feature and API may change in the future. **See**: [Nitro route rules documentation](https://nitro.build/config/#routerules) ## router ### `options` Additional router options passed to `vue-router`. On top of the options for `vue-router`, Nuxt offers additional options to customize the router (see below). ::callout **Note**: Only JSON serializable options should be passed by Nuxt config. For more control, you can use `app/router.options.ts` file. :: **See**: [Vue Router documentation](https://router.vuejs.org/api/interfaces/routeroptions.html). #### `hashMode` You can enable hash history in SPA mode. In this mode, router uses a hash character (#) before the actual URL that is internally passed. When enabled, the **URL is never sent to the server** and **SSR is not supported**. - **Type**: `boolean` - **Default:** `false` **Default**: false #### `scrollBehaviorType` Customize the scroll behavior for hash links. - **Type**: `string` - **Default:** `"auto"` **Default**: 'auto' ## runtimeConfig Runtime config allows passing dynamic config and environment variables to the Nuxt app context. The value of this object is accessible from server only using `useRuntimeConfig`. It mainly should hold *private* configuration which is not exposed on the frontend. This could include a reference to your API secret tokens. Anything under `public` and `app` will be exposed to the frontend as well. Values are automatically replaced by matching env variables at runtime, e.g. setting an environment variable `NUXT_API_KEY=my-api-key NUXT_PUBLIC_BASE_URL=/foo/` would overwrite the two values in the example below. - **Type**: `object` - **Default** ```json { "public": {}, "app": { "buildId": "cf819bfb-dece-40e8-ad97-a4091ee7fe95", "baseURL": "/", "buildAssetsDir": "/_nuxt/", "cdnURL": "" } } ``` **Example**: ```js export default { runtimeConfig: { apiKey: '', // Default to an empty string, automatically set at runtime using process.env.NUXT_API_KEY public: { baseURL: '' // Exposed to the frontend as well. } } } ``` ## server ### `builder` - **Type**: `string` - **Default:** `"@nuxt/nitro-server"` ## serverDir Define the server directory of your Nuxt application, where Nitro routes, middleware and plugins are kept. If a relative path is specified, it will be relative to your `rootDir`. - **Type**: `string` - **Default:** `"/<srcDir>/server"` ## serverHandlers Nitro server handlers. Each handler accepts the following options: - handler: The path to the file defining the handler. - route: The route under which the handler is available. This follows the conventions of [rou3](https://github.com/unjs/rou3). - method: The HTTP method of requests that should be handled. - middleware: Specifies whether it is a middleware handler. - lazy: Specifies whether to use lazy loading to import the handler. - **Type**: `array` **See**: [`server/` directory documentation](https://nuxt.com/docs/guide/directory-structure/server) ::callout **Note**: Files from `server/api`, `server/middleware` and `server/routes` will be automatically registered by Nuxt. :: **Example**: ```js serverHandlers: [ { route: '/path/foo/**:name', handler: '~/server/foohandler.ts' } ] ``` ## sourcemap Configures whether and how sourcemaps are generated for server and/or client bundles. If set to a single boolean, that value applies to both server and client. Additionally, the `'hidden'` option is also available for both server and client. Available options for both client and server: - `true`: Generates sourcemaps and includes source references in the final bundle. - `false`: Does not generate any sourcemaps. - `'hidden'`: Generates sourcemaps but does not include references in the final bundle. - **Type**: `object` - **Default** ```json { "server": true, "client": false } ``` ## spaLoadingTemplate Boolean or a path to an HTML file with the contents of which will be inserted into any HTML page rendered with `ssr: false`. - If it is unset, it will use `~/app/spa-loading-template.html` file in one of your layers, if it exists. - If it is false, no SPA loading indicator will be loaded. - If true, Nuxt will look for `~/app/spa-loading-template.html` file in one of your layers, or a default Nuxt image will be used. Some good sources for spinners are [SpinKit](https://github.com/tobiasahlin/SpinKit) or [SVG Spinners](https://icones.js.org/collection/svg-spinners). - **Default:** `null` **Example**: \~/app/spa-loading-template.html ```html <!-- https://github.com/barelyhuman/snips/blob/dev/pages/css-loader.md --> <div class="loader"></div> <style> .loader { display: block; position: fixed; z-index: 1031; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 18px; height: 18px; box-sizing: border-box; border: solid 2px transparent; border-top-color: #000; border-left-color: #000; border-bottom-color: #efefef; border-right-color: #efefef; border-radius: 50%; -webkit-animation: loader 400ms linear infinite; animation: loader 400ms linear infinite; } @-webkit-keyframes loader { 0% { -webkit-transform: translate(-50%, -50%) rotate(0deg); } 100% { -webkit-transform: translate(-50%, -50%) rotate(360deg); } } @keyframes loader { 0% { transform: translate(-50%, -50%) rotate(0deg); } 100% { transform: translate(-50%, -50%) rotate(360deg); } } </style> ``` ## srcDir Define the source directory of your Nuxt application. If a relative path is specified, it will be relative to the `rootDir`. - **Type**: `string` - **Default:** `"/<srcDir>"` **Example**: ```js export default { srcDir: 'src/' } ``` This would work with the following folder structure: ```bash -| app/ ---| node_modules/ ---| nuxt.config.js ---| package.json ---| src/ ------| assets/ ------| components/ ------| layouts/ ------| middleware/ ------| pages/ ------| plugins/ ------| public/ ------| store/ ------| server/ ------| app.config.ts ------| app.vue ------| error.vue ``` ## ssr Whether to enable rendering of HTML - either dynamically (in server mode) or at generate time. If set to `false` generated pages will have no content. - **Type**: `boolean` - **Default:** `true` ## telemetry Manually disable nuxt telemetry. **See**: [Nuxt Telemetry](https://github.com/nuxt/telemetry) for more information. ## test Whether your app is being unit tested. - **Type**: `boolean` - **Default:** `false` ## theme Extend project from a local or remote source. Value should be a string pointing to source directory or config path relative to current config. You can use `github:`, `gitlab:`, `bitbucket:` or `https://` to extend from a remote git repository. - **Type**: `string` ## typescript Configuration for Nuxt's TypeScript integration. ### `builder` Which builder types to include for your project. By default Nuxt infers this based on your `builder` option (defaulting to 'vite') but you can either turn off builder environment types (with `false`) to handle this fully yourself, or opt for a 'shared' option. The 'shared' option is advised for module authors, who will want to support multiple possible builders. - **Default:** `null` ### `hoist` Modules to generate deep aliases for within `compilerOptions.paths`. This does not yet support subpaths. It may be necessary when using Nuxt within a pnpm monorepo with `shamefully-hoist=false`. - **Type**: `array` - **Default** ```json [ "@unhead/vue", "@nuxt/devtools", "vue", "@vue/runtime-core", "@vue/compiler-sfc", "vue-router", "vue-router/auto-routes", "unplugin-vue-router/client", "@nuxt/schema", "nuxt" ] ``` ### `includeWorkspace` Include parent workspace in the Nuxt project. Mostly useful for themes and module authors. - **Type**: `boolean` - **Default:** `false` ### `shim` Generate a `*.vue` shim. We recommend instead letting the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) generate accurate types for your components. Note that you may wish to set this to `true` if you are using other libraries, such as ESLint, that are unable to understand the type of `.vue` files. - **Type**: `boolean` - **Default:** `false` ### `strict` TypeScript comes with certain checks to give you more safety and analysis of your program. Once you’ve converted your codebase to TypeScript, you can start enabling these checks for greater safety. [Read More](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) - **Type**: `boolean` - **Default:** `true` ### `tsConfig` You can extend generated `.nuxt/tsconfig.json` using this option. ### `typeCheck` Enable build-time type checking. If set to true, this will type check in development. You can restrict this to build-time type checking by setting it to `build`. Requires to install `typescript` and `vue-tsc` as dev dependencies. - **Type**: `boolean` - **Default:** `false` **See**: [Nuxt TypeScript docs](https://nuxt.com/docs/guide/concepts/typescript) ## unhead An object that allows us to configure the `unhead` nuxt module. ### `legacy` Enable the legacy compatibility mode for `unhead` module. This applies the following changes: - Disables Capo.js sorting - Adds the `DeprecationsPlugin`: supports `hid`, `vmid`, `children`, `body` - Adds the `PromisesPlugin`: supports promises as input - **Type**: `boolean` - **Default:** `false` **See**: [`unhead` migration documentation](https://unhead.unjs.io/docs/typescript/head/guides/get-started/migration) **Example**: ```ts export default defineNuxtConfig({ unhead: { legacy: true }) ``` ### `renderSSRHeadOptions` An object that will be passed to `renderSSRHead` to customize the output. - **Type**: `object` - **Default** ```json { "omitLineBreaks": false } ``` **Example**: ```ts export default defineNuxtConfig({ unhead: { renderSSRHeadOptions: { omitLineBreaks: true } }) ``` ## vite Configuration that will be passed directly to Vite. **See**: [Vite configuration docs](https://vite.dev/config) for more information. Please note that not all vite options are supported in Nuxt. ### `build` #### `assetsDir` - **Type**: `string` - **Default:** `"_nuxt/"` #### `emptyOutDir` - **Type**: `boolean` - **Default:** `false` ### `cacheDir` - **Type**: `string` - **Default:** `"/<rootDir>/node_modules/.cache/vite"` ### `clearScreen` - **Type**: `boolean` - **Default:** `true` ### `define` - **Type**: `object` - **Default** ```json { "__VUE_PROD_DEVTOOLS__": false, "__VUE_PROD_HYDRATION_MISMATCH_DETAILS__": false, "process.dev": false, "import.meta.dev": false, "process.test": false, "import.meta.test": false } ``` ### `esbuild` - **Type**: `object` - **Default** ```json { "target": "esnext", "jsxFactory": "h", "jsxFragment": "Fragment", "tsconfigRaw": {} } ``` ### `mode` - **Type**: `string` - **Default:** `"production"` ### `optimizeDeps` #### `esbuildOptions` - **Type**: `object` - **Default** ```json { "target": "esnext", "jsxFactory": "h", "jsxFragment": "Fragment", "tsconfigRaw": {} } ``` #### `exclude` - **Type**: `array` - **Default** ```json [ "vue-demi" ] ``` ### `publicDir` - **Type**: `boolean` - **Default:** `false` ### `resolve` #### `extensions` - **Type**: `array` - **Default** ```json [ ".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue" ] ``` ### `root` - **Type**: `string` - **Default:** `"/<srcDir>"` ### `server` #### `fs` ##### `allow` - **Type**: `array` - **Default** ```json [ "/<rootDir>/.nuxt", "/<srcDir>", "/<rootDir>", "/<workspaceDir>" ] ``` ### `vue` #### `features` ##### `propsDestructure` - **Type**: `boolean` - **Default:** `true` #### `isProduction` - **Type**: `boolean` - **Default:** `true` #### `script` ##### `hoistStatic` #### `template` ##### `compilerOptions` - **Type**: `object` ##### `transformAssetUrls` - **Type**: `object` - **Default** ```json { "video": [ "src", "poster" ], "source": [ "src" ], "img": [ "src" ], "image": [ "xlink:href", "href" ], "use": [ "xlink:href", "href" ] } ``` ### `vueJsx` - **Type**: `object` - **Default** ```json { "isCustomElement": { "$schema": { "title": "", "description": "", "tags": [] } }, "defineComponentName": [ "defineComponent", "defineNuxtComponent" ] } ``` ## vue Vue.js config ### `compilerOptions` Options for the Vue compiler that will be passed at build time. **See**: [Vue documentation](https://vuejs.org/api/application.html#app-config-compileroptions) ### `config` It is possible to pass configure the Vue app globally. Only serializable options may be set in your `nuxt.config`. All other options should be set at runtime in a Nuxt plugin.. **See**: [Vue app config documentation](https://vuejs.org/api/application.html#app-config) ### `propsDestructure` Enable reactive destructure for `defineProps` - **Type**: `boolean` - **Default:** `true` ### `runtimeCompiler` Include Vue compiler in runtime bundle. - **Type**: `boolean` - **Default:** `false` ### `transformAssetUrls` #### `image` - **Type**: `array` - **Default** ```json [ "xlink:href", "href" ] ``` #### `img` - **Type**: `array` - **Default** ```json [ "src" ] ``` #### `source` - **Type**: `array` - **Default** ```json [ "src" ] ``` #### `use` - **Type**: `array` - **Default** ```json [ "xlink:href", "href" ] ``` #### `video` - **Type**: `array` - **Default** ```json [ "src", "poster" ] ``` ## watch The watch property lets you define patterns that will restart the Nuxt dev server when changed. It is an array of strings or regular expressions. Strings should be either absolute paths or relative to the `srcDir` (and the `srcDir` of any layers). Regular expressions will be matched against the path relative to the project `srcDir` (and the `srcDir` of any layers). - **Type**: `array` ## watchers The watchers property lets you overwrite watchers configuration in your `nuxt.config`. ### `chokidar` Options to pass directly to `chokidar`. **See**: [chokidar](https://github.com/paulmillr/chokidar#api) #### `ignoreInitial` - **Type**: `boolean` - **Default:** `true` #### `ignorePermissionErrors` - **Type**: `boolean` - **Default:** `true` ### `rewatchOnRawEvents` An array of event types, which, when received, will cause the watcher to restart. ### `webpack` `watchOptions` to pass directly to webpack. **See**: [webpack@4 watch options](https://v4.webpack.js.org/configuration/watch/#watchoptions). #### `aggregateTimeout` - **Type**: `number` - **Default:** `1000` ## webpack ### `aggressiveCodeRemoval` Hard-replaces `typeof process`, `typeof window` and `typeof document` to tree-shake bundle. - **Type**: `boolean` - **Default:** `false` ### `analyze` Nuxt uses `webpack-bundle-analyzer` to visualize your bundles and how to optimize them. Set to `true` to enable bundle analysis, or pass an object with options: [for webpack](https://github.com/webpack-contrib/webpack-bundle-analyzer#options-for-plugin) or [for vite](https://github.com/btd/rollup-plugin-visualizer#options). - **Type**: `object` - **Default** ```json { "template": "treemap", "projectRoot": "/<rootDir>", "filename": "/<rootDir>/.nuxt/analyze/{name}.html" } ``` **Example**: ```js analyze: { analyzerMode: 'static' } ``` ### `cssSourceMap` Enables CSS source map support (defaults to `true` in development). - **Type**: `boolean` - **Default:** `false` ### `devMiddleware` See [webpack-dev-middleware](https://github.com/webpack/webpack-dev-middleware) for available options. #### `stats` - **Type**: `string` - **Default:** `"none"` ### `experiments` Configure [webpack experiments](https://webpack.js.org/configuration/experiments/) ### `extractCSS` Enables Common CSS Extraction. Using [mini-css-extract-plugin](https://github.com/webpack-contrib/mini-css-extract-plugin) under the hood, your CSS will be extracted into separate files, usually one per component. This allows caching your CSS and JavaScript separately. - **Type**: `boolean` - **Default:** `true` **Example**: ```js export default { webpack: { extractCSS: true, // or extractCSS: { ignoreOrder: true } } } ``` <!-- If you want to extract all your CSS to a single file, there is a workaround for this. However, note that it is not recommended to extract everything into a single file. Extracting into multiple CSS files is better for caching and preload isolation. It can also improve page performance by downloading and resolving only those resources that are needed. --> **Example**: ```js export default { webpack: { extractCSS: true, optimization: { splitChunks: { cacheGroups: { styles: { name: 'styles', test: /\.(css|vue)$/, chunks: 'all', enforce: true } } } } } } ``` ### `filenames` Customize bundle filenames. To understand a bit more about the use of manifests, take a look at [webpack documentation](https://webpack.js.org/guides/code-splitting/). ::callout **Note**: Be careful when using non-hashed based filenames in production as most browsers will cache the asset and not detect the changes on first load. :: <!-- This example changes fancy chunk names to numerical ids: --> **Example**: ```js filenames: { chunk: ({ isDev }) => (isDev ? '[name].js' : '[id].[contenthash].js') } ``` #### `app` - **Type**: `function` #### `chunk` - **Type**: `function` #### `css` - **Type**: `function` #### `font` - **Type**: `function` #### `img` - **Type**: `function` #### `video` - **Type**: `function` ### `friendlyErrors` Set to `false` to disable the overlay provided by [FriendlyErrorsWebpackPlugin](https://github.com/nuxt/friendly-errors-webpack-plugin). - **Type**: `boolean` - **Default:** `true` ### `hotMiddleware` See [webpack-hot-middleware](https://github.com/webpack-contrib/webpack-hot-middleware) for available options. ### `loaders` Customize the options of Nuxt's integrated webpack loaders. #### `css` See [css-loader](https://github.com/webpack-contrib/css-loader) for available options. ##### `esModule` - **Type**: `boolean` - **Default:** `false` ##### `importLoaders` - **Type**: `number` - **Default:** `0` ##### `url` ###### `filter` - **Type**: `function` #### `cssModules` See [css-loader](https://github.com/webpack-contrib/css-loader) for available options. ##### `esModule` - **Type**: `boolean` - **Default:** `false` ##### `importLoaders` - **Type**: `number` - **Default:** `0` ##### `modules` ###### `localIdentName` - **Type**: `string` - **Default:** `"[local]_[hash:base64:5]"` ##### `url` ###### `filter` - **Type**: `function` #### `esbuild` - **Type**: `object` - **Default** ```json { "target": "esnext", "jsxFactory": "h", "jsxFragment": "Fragment", "tsconfigRaw": {} } ``` **See**: [esbuild loader](https://github.com/esbuild-kit/esbuild-loader) #### `file` **See**: [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options) **Default**: ```ts { esModule: false } ``` ##### `esModule` - **Type**: `boolean` - **Default:** `false` ##### `limit` - **Type**: `number` - **Default:** `1000` #### `fontUrl` **See**: [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options) **Default**: ```ts { esModule: false } ``` ##### `esModule` - **Type**: `boolean` - **Default:** `false` ##### `limit` - **Type**: `number` - **Default:** `1000` #### `imgUrl` **See**: [`file-loader` Options](https://github.com/webpack-contrib/file-loader#options) **Default**: ```ts { esModule: false } ``` ##### `esModule` - **Type**: `boolean` - **Default:** `false` ##### `limit` - **Type**: `number` - **Default:** `1000` #### `less` - **Default** ```json { "sourceMap": false } ``` **See**: [`less-loader` Options](https://github.com/webpack-contrib/less-loader#options) #### `pugPlain` **See**: [`pug` options](https://pugjs.org/api/reference.html#options) #### `sass` **See**: [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options) **Default**: ```ts { sassOptions: { indentedSyntax: true } } ``` ##### `sassOptions` ###### `indentedSyntax` - **Type**: `boolean` - **Default:** `true` #### `scss` - **Default** ```json { "sourceMap": false } ``` **See**: [`sass-loader` Options](https://github.com/webpack-contrib/sass-loader#options) #### `stylus` - **Default** ```json { "sourceMap": false } ``` **See**: [`stylus-loader` Options](https://github.com/webpack-contrib/stylus-loader#options) #### `vue` See [vue-loader](https://github.com/vuejs/vue-loader) for available options. ##### `compilerOptions` - **Type**: `object` ##### `propsDestructure` - **Type**: `boolean` - **Default:** `true` ##### `transformAssetUrls` - **Type**: `object` - **Default** ```json { "video": [ "src", "poster" ], "source": [ "src" ], "img": [ "src" ], "image": [ "xlink:href", "href" ], "use": [ "xlink:href", "href" ] } ``` #### `vueStyle` - **Default** ```json { "sourceMap": false } ``` ### `optimization` Configure [webpack optimization](https://webpack.js.org/configuration/optimization/). #### `minimize` Set minimize to `false` to disable all minimizers. (It is disabled in development by default). - **Type**: `boolean` - **Default:** `true` #### `minimizer` You can set minimizer to a customized array of plugins. #### `runtimeChunk` - **Type**: `string` - **Default:** `"single"` #### `splitChunks` ##### `automaticNameDelimiter` - **Type**: `string` - **Default:** `"/"` ##### `cacheGroups` ##### `chunks` - **Type**: `string` - **Default:** `"all"` ### `optimizeCSS` OptimizeCSSAssets plugin options. Defaults to true when `extractCSS` is enabled. - **Type**: `boolean` - **Default:** `false` **See**: [css-minimizer-webpack-plugin documentation](https://github.com/webpack-contrib/css-minimizer-webpack-plugin). ### `plugins` Add webpack plugins. - **Type**: `array` **Example**: ```js import webpack from 'webpack' import { version } from './package.json' // ... plugins: [ new webpack.DefinePlugin({ 'process.VERSION': version }) ] ``` ### `postcss` Customize PostCSS Loader. same options as [`postcss-loader` options](https://github.com/webpack-contrib/postcss-loader#options) #### `postcssOptions` ##### `plugins` - **Type**: `object` - **Default** ```json { "autoprefixer": {}, "cssnano": {} } ``` ### `profile` Enable the profiler in webpackbar. It is normally enabled by CLI argument `--profile`. - **Type**: `boolean` - **Default:** `false` **See**: [webpackbar](https://github.com/unjs/webpackbar#profile). ### `serverURLPolyfill` The polyfill library to load to provide URL and URLSearchParams. Defaults to `'url'` ([see package](https://www.npmjs.com/package/url)). - **Type**: `string` - **Default:** `"url"` ### `warningIgnoreFilters` Filters to hide build warnings. - **Type**: `array` ## workspaceDir Define the workspace directory of your application. Often this is used when in a monorepo setup. Nuxt will attempt to detect your workspace directory automatically, but you can override it here. It is normally not needed to configure this option. - **Type**: `string` - **Default:** `"/<workspaceDir>"` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "abortNavigation" description: "abortNavigation is a helper function that prevents navigation from taking place and throws an error if one is set as a parameter." canonical_url: "https://nuxt.com/docs/3.x/api/utils/abort-navigation" --- # abortNavigation > abortNavigation is a helper function that prevents navigation from taking place and throws an error if one is set as a parameter. ::warning `abortNavigation` is only usable inside a [route middleware handler](https://nuxt.com/docs/3.x/directory-structure/middleware). :: ## Type ```ts [Signature] export function abortNavigation (err?: Error | string): false ``` ## Parameters ### `err` - **Type**: [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) | `string` Optional error to be thrown by `abortNavigation`. ## Examples The example below shows how you can use `abortNavigation` in a route middleware to prevent unauthorized route access: ```ts [middleware/auth.ts] export default defineNuxtRouteMiddleware((to, from) => { const user = useState('user') if (!user.value.isAuthorized) { return abortNavigation() } if (to.path !== '/edit-post') { return navigateTo('/edit-post') } }) ``` ### `err` as a String You can pass the error as a string: ```ts [middleware/auth.ts] export default defineNuxtRouteMiddleware((to, from) => { const user = useState('user') if (!user.value.isAuthorized) { return abortNavigation('Insufficient permissions.') } }) ``` ### `err` as an Error Object You can pass the error as an [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error) object, e.g. caught by the `catch`-block: ```ts [middleware/auth.ts] export default defineNuxtRouteMiddleware((to, from) => { try { /* code that might throw an error */ } catch (err) { return abortNavigation(err) } }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "addRouteMiddleware" description: "addRouteMiddleware() is a helper function to dynamically add middleware in your application." canonical_url: "https://nuxt.com/docs/3.x/api/utils/add-route-middleware" --- # addRouteMiddleware > addRouteMiddleware() is a helper function to dynamically add middleware in your application. ::note Route middleware are navigation guards stored in the [`middleware/`](https://nuxt.com/docs/3.x/directory-structure/middleware) directory of your Nuxt application (unless [set otherwise](https://nuxt.com/docs/3.x/api/nuxt-config#middleware)). :: ## Type ```ts function addRouteMiddleware (name: string, middleware: RouteMiddleware, options?: AddRouteMiddlewareOptions): void function addRouteMiddleware (middleware: RouteMiddleware): void interface AddRouteMiddlewareOptions { global?: boolean } ``` ## Parameters ### `name` - **Type:** `string` | `RouteMiddleware` Can be either a string or a function of type `RouteMiddleware`. Function takes the next route `to` as the first argument and the current route `from` as the second argument, both of which are Vue route objects. Learn more about available properties of [route objects](https://nuxt.com/docs/3.x/api/composables/use-route). ### `middleware` - **Type:** `RouteMiddleware` The second argument is a function of type `RouteMiddleware`. Same as above, it provides `to` and `from` route objects. It becomes optional if the first argument in `addRouteMiddleware()` is already passed as a function. ### `options` - **Type:** `AddRouteMiddlewareOptions` An optional `options` argument lets you set the value of `global` to `true` to indicate whether the router middleware is global or not (set to `false` by default). ## Examples ### Named Route Middleware Named route middleware is defined by providing a string as the first argument and a function as the second: ```ts [plugins/my-plugin.ts] export default defineNuxtPlugin(() => { addRouteMiddleware('named-middleware', () => { console.log('named middleware added in Nuxt plugin') }) }) ``` When defined in a plugin, it overrides any existing middleware of the same name located in the `middleware/` directory. ### Global Route Middleware Global route middleware can be defined in two ways: - Pass a function directly as the first argument without a name. It will automatically be treated as global middleware and applied on every route change. ```ts [plugins/my-plugin.ts] export default defineNuxtPlugin(() => { addRouteMiddleware((to, from) => { console.log('anonymous global middleware that runs on every route change') }) }) ``` - Set an optional, third argument `{ global: true }` to indicate whether the route middleware is global. ```ts [plugins/my-plugin.ts] export default defineNuxtPlugin(() => { addRouteMiddleware('global-middleware', (to, from) => { console.log('global middleware that runs on every route change') }, { global: true }, ) }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "callOnce" description: "Run a given function or block of code once during SSR or CSR." canonical_url: "https://nuxt.com/docs/3.x/api/utils/call-once" --- # callOnce > Run a given function or block of code once during SSR or CSR. ::important This utility is available since [Nuxt v3.9](https://nuxt.com/blog/v3-9). :: ## Purpose The `callOnce` function is designed to execute a given function or block of code only once during: - server-side rendering but not hydration - client-side navigation This is useful for code that should be executed only once, such as logging an event or setting up a global state. ## Usage The default mode of `callOnce` is to run code only once. For example, if the code runs on the server it won't run again on the client. It also won't run again if you `callOnce` more than once on the client, for example by navigating back to this page. ```vue [app.vue] <script setup lang="ts"> const websiteConfig = useState('config') await callOnce(async () => { console.log('This will only be logged once') websiteConfig.value = await $fetch('https://my-cms.com/api/website-config') }) </script> ``` It is also possible to run on every navigation while still avoiding the initial server/client double load. For this, it is possible to use the `navigation` mode: ```vue [app.vue] <script setup lang="ts"> const websiteConfig = useState('config') await callOnce(async () => { console.log('This will only be logged once and then on every client side navigation') websiteConfig.value = await $fetch('https://my-cms.com/api/website-config') }, { mode: 'navigation' }) </script> ``` ::important `navigation` mode is available since [Nuxt v3.15](https://nuxt.com/blog/v3-15). :: ::tip{to="https://nuxt.com/docs/3.x/getting-started/state-management#usage-with-pinia"} `callOnce` is useful in combination with the [Pinia module](https://nuxt.com/modules/pinia) to call store actions. :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/state-management"} :: ::warning Note that `callOnce` doesn't return anything. You should use [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) or [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) if you want to do data fetching during SSR. :: ::note `callOnce` is a composable meant to be called directly in a setup function, plugin, or route middleware, because it needs to add data to the Nuxt payload to avoid re-calling the function on the client when the page hydrates. :: ## Type ```ts [Signature] export function callOnce (key?: string, fn?: (() => any | Promise<any>), options?: CallOnceOptions): Promise<void> export function callOnce (fn?: (() => any | Promise<any>), options?: CallOnceOptions): Promise<void> type CallOnceOptions = { /** * Execution mode for the callOnce function * @default 'render' */ mode?: 'navigation' | 'render' } ``` ## Parameters - `key`: A unique key ensuring that the code is run once. If you do not provide a key, then a key that is unique to the file and line number of the instance of `callOnce` will be generated for you. - `fn`: The function to run once. It can be asynchronous. - `options`: Setup the mode, either to re-execute on navigation (`navigation`) or just once for the lifetime of the app (`render`). Defaults to `render`. - `render`: Executes once during initial render (either SSR or CSR) - Default mode - `navigation`: Executes once during initial render and once per subsequent client-side navigation --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/once.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "clearError" description: "The clearError composable clears all handled errors." canonical_url: "https://nuxt.com/docs/3.x/api/utils/clear-error" --- # clearError > The clearError composable clears all handled errors. Within your pages, components, and plugins, you can use `clearError` to clear all errors and redirect the user. **Parameters:** - `options?: { redirect?: string }` You can provide an optional path to redirect to (for example, if you want to navigate to a 'safe' page). ```ts // Without redirect clearError() // With redirect clearError({ redirect: '/homepage' }) ``` Errors are set in state using [`useError()`](https://nuxt.com/docs/3.x/api/composables/use-error). The `clearError` composable will reset this state and calls the `app:error:cleared` hook with the provided options. ::read-more{to="https://nuxt.com/docs/3.x/getting-started/error-handling"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/error.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "clearNuxtData" description: "Delete cached data, error status and pending promises of useAsyncData and useFetch." canonical_url: "https://nuxt.com/docs/3.x/api/utils/clear-nuxt-data" --- # clearNuxtData > Delete cached data, error status and pending promises of useAsyncData and useFetch. ::note This method is useful if you want to invalidate the data fetching for another page. :: ## Type ```ts [Signature] export function clearNuxtData (keys?: string | string[] | ((key: string) => boolean)): void ``` ## Parameters - `keys`: One or an array of keys that are used in [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) to delete their cached data. If no keys are provided, **all data** will be invalidated. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "clearNuxtState" description: "Delete the cached state of useState." canonical_url: "https://nuxt.com/docs/3.x/api/utils/clear-nuxt-state" --- # clearNuxtState > Delete the cached state of useState. ::note This method is useful if you want to invalidate the state of `useState`. :: ## Type ```ts [Signature] export function clearNuxtState (keys?: string | string[] | ((key: string) => boolean)): void ``` ## Parameters - `keys`: One or an array of keys that are used in [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) to delete their cached state. If no keys are provided, **all state** will be invalidated. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/state.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "createError" description: "Create an error object with additional metadata." canonical_url: "https://nuxt.com/docs/3.x/api/utils/create-error" --- # createError > Create an error object with additional metadata. You can use this function to create an error object with additional metadata. It is usable in both the Vue and Nitro portions of your app, and is meant to be thrown. ## Parameters - `err`: `string | { cause, data, message, name, stack, status, statusText, fatal }` You can pass either a string or an object to the `createError` function. If you pass a string, it will be used as the error `message`, and the `status` will default to `500`. If you pass an object, you can set multiple properties of the error, such as `status`, `message`, and other error properties. ## In Vue App If you throw an error created with `createError`: - on server-side, it will trigger a full-screen error page which you can clear with `clearError`. - on client-side, it will throw a non-fatal error for you to handle. If you need to trigger a full-screen error page, then you can do this by setting `fatal: true`. ### Example ```vue [pages/movies/[slug\\].vue] <script setup lang="ts"> const route = useRoute() const { data } = await useFetch(`/api/movies/${route.params.slug}`) if (!data.value) { throw createError({ status: 404, statusText: 'Page Not Found' }) } </script> ``` ## In API Routes Use `createError` to trigger error handling in server API routes. ### Example ```ts [server/api/error.ts] export default eventHandler(() => { throw createError({ status: 404, statusText: 'Page Not Found', }) }) ``` In API routes, using `createError` by passing an object with a short `statusText` is recommended because it can be accessed on the client side. Otherwise, a `message` passed to `createError` on an API route will not propagate to the client. Alternatively, you can use the `data` property to pass data back to the client. When handling the error with `useFetch`, the custom data is available at `error.value.data.data`. In any case, always consider avoiding to put dynamic user input to the message to avoid potential security issues. ::read-more{to="https://nuxt.com/docs/3.x/getting-started/error-handling"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/error.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "defineLazyHydrationComponent" description: "Define a lazy hydration component with a specific strategy." canonical_url: "https://nuxt.com/docs/3.x/api/utils/define-lazy-hydration-component" --- # defineLazyHydrationComponent > Define a lazy hydration component with a specific strategy. `defineLazyHydrationComponent` is a compiler macro that helps you create a component with a specific lazy hydration strategy. Lazy hydration defers hydration until components become visible or until the browser has completed more critical tasks. This can significantly reduce the initial performance cost, especially for non-essential components. ## Usage ### Visibility Strategy Hydrates the component when it becomes visible in the viewport. ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'visible', () => import('./components/MyComponent.vue'), ) </script> <template> <div> <!-- Hydration will be triggered when the element(s) is 100px away from entering the viewport. --> <LazyHydrationMyComponent :hydrate-on-visible="{ rootMargin: '100px' }" /> </div> </template> ``` The `hydrateOnVisible` prop is optional. You can pass an object to customize the behavior of the `IntersectionObserver` under the hood. ::read-more{to="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver" title="IntersectionObserver options"} Read more about the options for `hydrate-on-visible`. :: ::note Under the hood, this uses Vue's built-in [`hydrateOnVisible` strategy](https://vuejs.org/guide/components/async#hydrate-on-visible). :: ### Idle Strategy Hydrates the component when the browser is idle. This is suitable if you need the component to load as soon as possible, but not block the critical rendering path. ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'idle', () => import('./components/MyComponent.vue'), ) </script> <template> <div> <!-- Hydration will be triggered when the browser is idle or after 2000ms. --> <LazyHydrationMyComponent :hydrate-on-idle="2000" /> </div> </template> ``` The `hydrateOnIdle` prop is optional. You can pass a positive number to specify the maximum timeout. Idle strategy is for components that can be hydrated when the browser is idle. ::note Under the hood, this uses Vue's built-in [`hydrateOnIdle` strategy](https://vuejs.org/guide/components/async#hydrate-on-idle). :: ### Interaction Strategy Hydrates the component after a specified interaction (e.g., click, mouseover). ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'interaction', () => import('./components/MyComponent.vue'), ) </script> <template> <div> <!-- Hydration will be triggered when the element(s) is hovered over by the pointer. --> <LazyHydrationMyComponent hydrate-on-interaction="mouseover" /> </div> </template> ``` The `hydrateOnInteraction` prop is optional. If you do not pass an event or a list of events, it defaults to hydrating on `pointerenter`, `click`, and `focus`. ::note Under the hood, this uses Vue's built-in [`hydrateOnInteraction` strategy](https://vuejs.org/guide/components/async#hydrate-on-interaction). :: ### Media Query Strategy Hydrates the component when the window matches a media query. ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'mediaQuery', () => import('./components/MyComponent.vue'), ) </script> <template> <div> <!-- Hydration will be triggered when the window width is greater than or equal to 768px. --> <LazyHydrationMyComponent hydrate-on-media-query="(min-width: 768px)" /> </div> </template> ``` ::note Under the hood, this uses Vue's built-in [`hydrateOnMediaQuery` strategy](https://vuejs.org/guide/components/async#hydrate-on-media-query). :: ### Time Strategy Hydrates the component after a specified delay (in milliseconds). ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'time', () => import('./components/MyComponent.vue'), ) </script> <template> <div> <!-- Hydration is triggered after 1000ms. --> <LazyHydrationMyComponent :hydrate-after="1000" /> </div> </template> ``` Time strategy is for components that can wait a specific amount of time. ### If Strategy Hydrates the component based on a boolean condition. ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'if', () => import('./components/MyComponent.vue'), ) const isReady = ref(false) function myFunction () { // Trigger custom hydration strategy... isReady.value = true } </script> <template> <div> <!-- Hydration is triggered when isReady becomes true. --> <LazyHydrationMyComponent :hydrate-when="isReady" /> </div> </template> ``` If strategy is best for components that might not always need to be hydrated. ### Never Hydrate Never hydrates the component. ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'never', () => import('./components/MyComponent.vue'), ) </script> <template> <div> <!-- This component will never be hydrated by Vue. --> <LazyHydrationMyComponent /> </div> </template> ``` ### Listening to Hydration Events All delayed hydration components emit a `@hydrated` event when they are hydrated. ```vue <script setup lang="ts"> const LazyHydrationMyComponent = defineLazyHydrationComponent( 'visible', () => import('./components/MyComponent.vue'), ) function onHydrate () { console.log('Component has been hydrated!') } </script> <template> <div> <LazyHydrationMyComponent :hydrate-on-visible="{ rootMargin: '100px' }" @hydrated="onHydrated" /> </div> </template> ``` ## Parameters ::warning To ensure that the compiler correctly recognizes this macro, avoid using external variables. The following approach will prevent the macro from being properly recognized: ```vue <script setup lang="ts"> const strategy = 'visible' const source = () => import('./components/MyComponent.vue') const LazyHydrationMyComponent = defineLazyHydrationComponent(strategy, source) </script> ``` :: ### `strategy` - **Type**: `'visible' | 'idle' | 'interaction' | 'mediaQuery' | 'if' | 'time' | 'never'` - **Required**: `true` | Strategy | Description | | ------------- | ------------------------------------------------------------ | | `visible` | Hydrates when the component becomes visible in the viewport. | | `idle` | Hydrates when the browser is idle or after a delay. | | `interaction` | Hydrates upon user interaction (e.g., click, hover). | | `mediaQuery` | Hydrates when the specified media query condition is met. | | `if` | Hydrates when a specified boolean condition is met. | | `time` | Hydrates after a specified time delay. | | `never` | Prevents Vue from hydrating the component. | ### `source` - **Type**: `() => Promise<Component>` - **Required**: `true` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/plugins/lazy-hydration-macro-transform.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "defineNuxtComponent" description: "defineNuxtComponent() is a helper function for defining type safe components with Options API." canonical_url: "https://nuxt.com/docs/3.x/api/utils/define-nuxt-component" --- # defineNuxtComponent > defineNuxtComponent() is a helper function for defining type safe components with Options API. ::note `defineNuxtComponent()` is a helper function for defining type safe Vue components using options API similar to [`defineComponent()`](https://vuejs.org/api/general#definecomponent). `defineNuxtComponent()` wrapper also adds support for `asyncData` and `head` component options. :: ::note Using `<script setup lang="ts">` is the recommended way of declaring Vue components in Nuxt. :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: ## `asyncData()` If you choose not to use `setup()` in your app, you can use the `asyncData()` method within your component definition: ```vue [pages/index.vue] <script lang="ts"> export default defineNuxtComponent({ asyncData () { return { data: { greetings: 'hello world!', }, } }, }) </script> ``` ## `head()` If you choose not to use `setup()` in your app, you can use the `head()` method within your component definition: ```vue [pages/index.vue] <script lang="ts"> export default defineNuxtComponent({ head (nuxtApp) { return { title: 'My site', } }, }) </script> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/component.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "defineNuxtPlugin" description: "defineNuxtPlugin() is a helper function for creating Nuxt plugins." canonical_url: "https://nuxt.com/docs/3.x/api/utils/define-nuxt-plugin" --- # defineNuxtPlugin > defineNuxtPlugin() is a helper function for creating Nuxt plugins. `defineNuxtPlugin` is a helper function for creating Nuxt plugins with enhanced functionality and type safety. This utility normalizes different plugin formats into a consistent structure that works seamlessly within Nuxt's plugin system. ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin((nuxtApp) => { // Doing something with nuxtApp }) ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/plugins#creating-plugins"} :: ## Type ```ts [Signature] export function defineNuxtPlugin<T extends Record<string, unknown>> (plugin: Plugin<T> | ObjectPlugin<T>): Plugin<T> & ObjectPlugin<T> type Plugin<T> = (nuxt: NuxtApp) => Promise<void> | Promise<{ provide?: T }> | void | { provide?: T } interface ObjectPlugin<T> { name?: string enforce?: 'pre' | 'default' | 'post' dependsOn?: string[] order?: number parallel?: boolean setup?: Plugin<T> hooks?: Partial<RuntimeNuxtHooks> env?: { islands?: boolean } } ``` ## Parameters **plugin**: A plugin can be defined in two ways: 1. **Function Plugin**: A function that receives the [`NuxtApp`](https://nuxt.com/docs/3.x/guide/going-further/internals#the-nuxtapp-interface) instance and can return a promise with a potential object with a [`provide`](https://nuxt.com/docs/3.x/directory-structure/plugins#providing-helpers) property if you want to provide a helper on [`NuxtApp`](https://nuxt.com/docs/3.x/guide/going-further/internals#the-nuxtapp-interface) instance. 2. **Object Plugin**: An object that can include various properties to configure the plugin's behavior, such as `name`, `enforce`, `dependsOn`, `order`, `parallel`, `setup`, `hooks`, and `env`. | Property | Type | Required | Description | | ----------- | -------------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | `string` | `false` | Optional name for the plugin, useful for debugging and dependency management. | | `enforce` | `'pre'` \| `'default'` \| `'post'` | `false` | Controls when the plugin runs relative to other plugins. | | `dependsOn` | `string[]` | `false` | Array of plugin names this plugin depends on. Ensures proper execution order. | | `order` | `number` | `false` | This allows more granular control over plugin order and should only be used by advanced users. **It overrides the value of `enforce` and is used to sort plugins.** | | `parallel` | `boolean` | `false` | Whether to execute the plugin in parallel with other parallel plugins. | | `setup` | `Plugin<T>`{lang="ts"} | `false` | The main plugin function, equivalent to a function plugin. | | `hooks` | `Partial<RuntimeNuxtHooks>`{lang="ts"} | `false` | Nuxt app runtime hooks to register directly. | | `env` | `{ islands?: boolean }`{lang="ts"} | `false` | Set this value to `false` if you don't want the plugin to run when rendering server-only or island components. | ::video-accordion{title="Watch a video from Alexander Lichter about the Object Syntax for Nuxt plugins" videoId="2aXZyXB1QGQ"} :: ## Examples ### Basic Usage The example below demonstrates a simple plugin that adds global functionality: ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin((nuxtApp) => { // Add a global method return { provide: { hello: (name: string) => `Hello ${name}!`, }, } }) ``` ### Object Syntax Plugin The example below shows the object syntax with advanced configuration: ```ts twoslash [plugins/advanced.ts] export default defineNuxtPlugin({ name: 'my-plugin', enforce: 'pre', async setup (nuxtApp) { // Plugin setup logic const data = await $fetch('/api/config') return { provide: { config: data, }, } }, hooks: { 'app:created' () { console.log('App created!') }, }, }) ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/nuxt.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "defineNuxtRouteMiddleware" description: "Create named route middleware using defineNuxtRouteMiddleware helper function." canonical_url: "https://nuxt.com/docs/3.x/api/utils/define-nuxt-route-middleware" --- # defineNuxtRouteMiddleware > Create named route middleware using defineNuxtRouteMiddleware helper function. Route middleware are stored in the [`middleware/`](https://nuxt.com/docs/3.x/directory-structure/middleware) of your Nuxt application (unless [set otherwise](https://nuxt.com/docs/3.x/api/nuxt-config#middleware)). ## Type ```ts [Signature] export function defineNuxtRouteMiddleware (middleware: RouteMiddleware): RouteMiddleware interface RouteMiddleware { (to: RouteLocationNormalized, from: RouteLocationNormalized): ReturnType<NavigationGuard> } ``` ## Parameters ### `middleware` - **Type**: `RouteMiddleware` A function that takes two Vue Router's route location objects as parameters: the next route `to` as the first, and the current route `from` as the second. Learn more about available properties of `RouteLocationNormalized` in the **[Vue Router docs](https://router.vuejs.org/api/type-aliases/routelocationnormalized)**. ## Examples ### Showing Error Page You can use route middleware to throw errors and show helpful error messages: ```ts [middleware/error.ts] export default defineNuxtRouteMiddleware((to) => { if (to.params.id === '1') { throw createError({ status: 404, statusText: 'Page Not Found' }) } }) ``` The above route middleware will redirect a user to the custom error page defined in the `~/error.vue` file, and expose the error message and code passed from the middleware. ### Redirection Use [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) in combination with `navigateTo` helper function inside the route middleware to redirect users to different routes based on their authentication status: ```ts [middleware/auth.ts] export default defineNuxtRouteMiddleware((to, from) => { const auth = useState('auth') if (!auth.value.isAuthenticated) { return navigateTo('/login') } if (to.path !== '/dashboard') { return navigateTo('/dashboard') } }) ``` Both [navigateTo](https://nuxt.com/docs/3.x/api/utils/navigate-to) and [abortNavigation](https://nuxt.com/docs/3.x/api/utils/abort-navigation) are globally available helper functions that you can use inside `defineNuxtRouteMiddleware`. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "definePageMeta" description: "Define metadata for your page components." canonical_url: "https://nuxt.com/docs/3.x/api/utils/define-page-meta" --- # definePageMeta > Define metadata for your page components. `definePageMeta` is a compiler macro that you can use to set metadata for your **page** components located in the [`pages/`](https://nuxt.com/docs/3.x/directory-structure/pages) directory (unless [set otherwise](https://nuxt.com/docs/3.x/api/nuxt-config#pages)). This way you can set custom metadata for each static or dynamic route of your Nuxt application. ```vue [pages/some-page.vue] <script setup lang="ts"> definePageMeta({ layout: 'default', }) </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/pages#page-metadata"} :: ## Type ```ts [Signature] export function definePageMeta (meta: PageMeta): void interface PageMeta { validate?: ((route: RouteLocationNormalized) => boolean | Promise<boolean> | Partial<NuxtError> | Promise<Partial<NuxtError>>) redirect?: RouteRecordRedirectOption name?: string path?: string props?: RouteRecordRaw['props'] alias?: string | string[] groups?: string[] pageTransition?: boolean | TransitionProps layoutTransition?: boolean | TransitionProps viewTransition?: boolean | 'always' key?: false | string | ((route: RouteLocationNormalizedLoaded) => string) keepalive?: boolean | KeepAliveProps layout?: false | LayoutKey | Ref<LayoutKey> | ComputedRef<LayoutKey> middleware?: MiddlewareKey | NavigationGuard | Array<MiddlewareKey | NavigationGuard> scrollToTop?: boolean | ((to: RouteLocationNormalizedLoaded, from: RouteLocationNormalizedLoaded) => boolean) [key: string]: unknown } ``` ## Parameters ### `meta` - **Type**: `PageMeta` An object accepting the following page metadata: **`name`** - **Type**: `string` You may define a name for this page's route. By default, name is generated based on path inside the [`pages/` directory](https://nuxt.com/docs/3.x/directory-structure/pages). **`path`** - **Type**: `string` You may define a [custom regular expression](https://nuxt.com/docs/3.x/api/utils/define-page-meta#using-a-custom-regular-expression) if you have a more complex pattern than can be expressed with the file name. **`props`** - **Type**: [`RouteRecordRaw['props']`](https://router.vuejs.org/guide/essentials/passing-props) Allows accessing the route `params` as props passed to the page component. **`alias`** - **Type**: `string | string[]` Aliases for the record. Allows defining extra paths that will behave like a copy of the record. Allows having paths shorthands like `/users/:id` and `/u/:id`. All `alias` and `path` values must share the same params. **`groups`** - **Type**: `string[]` Route groups the page belongs to, based on the folder structure. Automatically populated for pages within [route groups](https://nuxt.com/docs/3.x/guide/directory-structure/app/pages#route-groups). **`keepalive`** - **Type**: `boolean` | [`KeepAliveProps`](https://vuejs.org/api/built-in-components#keepalive) Set to `true` when you want to preserve page state across route changes or use the [`KeepAliveProps`](https://vuejs.org/api/built-in-components#keepalive) for a fine-grained control. **`key`** - **Type**: `false` | `string` | `((route: RouteLocationNormalizedLoaded) => string)` Set `key` value when you need more control over when the `<NuxtPage>` component is re-rendered. **`layout`** - **Type**: `false` | `LayoutKey` | `Ref<LayoutKey>` | `ComputedRef<LayoutKey>` Set a static or dynamic name of the layout for each route. This can be set to `false` in case the default layout needs to be disabled. **`layoutTransition`** - **Type**: `boolean` | [`TransitionProps`](https://vuejs.org/api/built-in-components#transition) Set name of the transition to apply for current layout. You can also set this value to `false` to disable the layout transition. **`middleware`** - **Type**: `MiddlewareKey` | [`NavigationGuard`](https://router.vuejs.org/api/interfaces/navigationguard) | `Array<MiddlewareKey | NavigationGuard>` Define anonymous or named middleware directly within `definePageMeta`. Learn more about [route middleware](https://nuxt.com/docs/3.x/directory-structure/middleware). **`pageTransition`** - **Type**: `boolean` | [`TransitionProps`](https://vuejs.org/api/built-in-components#transition) Set name of the transition to apply for current page. You can also set this value to `false` to disable the page transition. **`viewTransition`** - **Type**: `boolean | 'always'` **Experimental feature, only available when [enabled in your nuxt.config file](https://nuxt.com/docs/3.x/getting-started/transitions#view-transitions-api-experimental)**Enable/disable View Transitions for the current page. If set to true, Nuxt will not apply the transition if the users browser matches `prefers-reduced-motion: reduce` (recommended). If set to `always`, Nuxt will always apply the transition. **`redirect`** - **Type**: [`RouteRecordRedirectOption`](https://router.vuejs.org/guide/essentials/redirect-and-alias) Where to redirect if the route is directly matched. The redirection happens before any navigation guard and triggers a new navigation with the new target location. **`validate`** - **Type**: `(route: RouteLocationNormalized) => boolean | Promise<boolean> | Partial<NuxtError> | Promise<Partial<NuxtError>>` Validate whether a given route can validly be rendered with this page. Return true if it is valid, or false if not. If another match can't be found, this will mean a 404. You can also directly return an object with `status`/`statusText` to respond immediately with an error (other matches will not be checked). **`scrollToTop`** - **Type**: `boolean | (to: RouteLocationNormalized, from: RouteLocationNormalized) => boolean` Tell Nuxt to scroll to the top before rendering the page or not. Navigation is independent from rendering, so scroll behavior is always triggered even when the page doesn't re-render (e.g. when using a fixed [`key`](https://nuxt.com/docs/3.x/api/utils/define-page-meta#key)). Set `scrollToTop: false` to disable scrolling in such cases. If you want to overwrite the default scroll behavior of Nuxt, you can do so in `~/app/router.options.ts` (see [custom routing](https://nuxt.com/docs/3.x/guide/recipes/custom-routing#using-approuteroptions)) for more info. **`[key: string]`** - **Type**: `any` Apart from the above properties, you can also set **custom** metadata. You may wish to do so in a type-safe way by [augmenting the type of the `meta` object](https://nuxt.com/docs/3.x/directory-structure/pages/#typing-custom-metadata). ## Examples ### Basic Usage The example below demonstrates: - how `key` can be a function that returns a value; - how `keepalive` property makes sure that the `<modal>` component is not cached when switching between multiple components; - adding `pageType` as a custom property: ```vue [pages/some-page.vue] <script setup lang="ts"> definePageMeta({ key: route => route.fullPath, keepalive: { exclude: ['modal'], }, pageType: 'Checkout', }) </script> ``` ### Defining Middleware The example below shows how the middleware can be defined using a `function` directly within the `definePageMeta` or set as a `string` that matches the middleware file name located in the `middleware/` directory: ```vue [pages/some-page.vue] <script setup lang="ts"> definePageMeta({ // define middleware as a function middleware: [ function (to, from) { const auth = useState('auth') if (!auth.value.authenticated) { return navigateTo('/login') } if (to.path !== '/checkout') { return navigateTo('/checkout') } }, ], // ... or a string middleware: 'auth', // ... or multiple strings middleware: ['auth', 'another-named-middleware'], }) </script> ``` ### Using a Custom Regular Expression A custom regular expression is a good way to resolve conflicts between overlapping routes, for instance: The two routes "/test-category" and "/1234-post" match both `[postId]-[postSlug].vue` and `[categorySlug].vue` page routes. To make sure that we are only matching digits (`\d+`) for `postId` in the `[postId]-[postSlug]` route, we can add the following to the `[postId]-[postSlug].vue` page template: ```vue [pages/[postId\\]-[postSlug\\].vue] <script setup lang="ts"> definePageMeta({ path: '/:postId(\\d+)-:postSlug', }) </script> ``` For more examples see [Vue Router's Matching Syntax](https://router.vuejs.org/guide/essentials/route-matching-syntax). ### Defining Layout You can define the layout that matches the layout's file name located (by default) in the [`layouts/` directory](https://nuxt.com/docs/3.x/directory-structure/layouts). You can also disable the layout by setting the `layout` to `false`: ```vue [pages/some-page.vue] <script setup lang="ts"> definePageMeta({ // set custom layout layout: 'admin', // ... or disable a default layout layout: false, }) </script> ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/pages/runtime/composables.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "defineRouteRules" description: "Define route rules for hybrid rendering at the page level." canonical_url: "https://nuxt.com/docs/3.x/api/utils/define-route-rules" --- # defineRouteRules > Define route rules for hybrid rendering at the page level. ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/experimental-features#inlinerouterules" icon="i-lucide-star"} This feature is experimental and in order to use it you must enable the `experimental.inlineRouteRules` option in your `nuxt.config`. :: ## Usage ```vue [pages/index.vue] <script setup lang="ts"> defineRouteRules({ prerender: true, }) </script> <template> <h1>Hello world!</h1> </template> ``` Will be translated to: ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/': { prerender: true }, }, }) ``` ::note When running [`nuxt build`](https://nuxt.com/docs/3.x/api/commands/build), the home page will be pre-rendered in `.output/public/index.html` and statically served. :: ## Notes - A rule defined in `~/pages/foo/bar.vue` will be applied to `/foo/bar` requests. - A rule in `~/pages/foo/[id].vue` will be applied to `/foo/**` requests. For more control, such as if you are using a custom `path` or `alias` set in the page's [`definePageMeta`](https://nuxt.com/docs/3.x/api/utils/define-page-meta), you should set `routeRules` directly within your `nuxt.config`. ::read-more{to="https://nuxt.com/docs/3.x/guide/concepts/rendering#hybrid-rendering" icon="i-lucide-medal"} Read more about the `routeRules`. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/pages/runtime/composables.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "$fetch" description: "Nuxt uses ofetch to expose globally the $fetch helper for making HTTP requests." canonical_url: "https://nuxt.com/docs/3.x/api/utils/dollarfetch" --- # $fetch > Nuxt uses ofetch to expose globally the $fetch helper for making HTTP requests. Nuxt uses [ofetch](https://github.com/unjs/ofetch) to expose globally the `$fetch` helper for making HTTP requests within your Vue app or API routes. ::tip{icon="i-lucide-rocket"} During server-side rendering, calling `$fetch` to fetch your internal [API routes](https://nuxt.com/docs/3.x/directory-structure/server) will directly call the relevant function (emulating the request), **saving an additional API call**. :: ::note{color="blue" icon="i-lucide-info"} Using `$fetch` in components without wrapping it with [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) causes fetching the data twice: initially on the server, then again on the client-side during hydration, because `$fetch` does not transfer state from the server to the client. Thus, the fetch will be executed on both sides because the client has to get the data again. :: ## Usage We recommend using [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) or [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) + `$fetch` to prevent double data fetching when fetching the component data. ```vue [app.vue] <script setup lang="ts"> // During SSR data is fetched twice, once on the server and once on the client. const dataTwice = await $fetch('/api/item') // During SSR data is fetched only on the server side and transferred to the client. const { data } = await useAsyncData('item', () => $fetch('/api/item')) // You can also useFetch as shortcut of useAsyncData + $fetch const { data } = await useFetch('/api/item') </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: You can use `$fetch` in any methods that are executed only on client-side. ```vue [pages/contact.vue] <script setup lang="ts"> async function contactForm () { await $fetch('/api/contact', { method: 'POST', body: { hello: 'world' }, }) } </script> <template> <button @click="contactForm"> Contact </button> </template> ``` ::tip `$fetch` is the preferred way to make HTTP calls in Nuxt instead of [@nuxt/http](https://github.com/nuxt/http) and [@nuxtjs/axios](https://github.com/nuxt-community/axios-module) that are made for Nuxt 2. :: ::note If you use `$fetch` to call an (external) HTTPS URL with a self-signed certificate in development, you will need to set `NODE_TLS_REJECT_UNAUTHORIZED=0` in your environment. :: ### Passing Headers and Cookies When we call `$fetch` in the browser, user headers like `cookie` will be directly sent to the API. However, during Server-Side Rendering, due to security risks such as **Server-Side Request Forgery (SSRF)** or **Authentication Misuse**, the `$fetch` wouldn't include the user's browser cookies, nor pass on cookies from the fetch response. ::code-group ```vue [pages/index.vue] <script setup lang="ts"> // This will NOT forward headers or cookies during SSR const { data } = await useAsyncData(() => $fetch('/api/cookies')) </script> ``` ```ts [server/api/cookies.ts] export default defineEventHandler((event) => { const foo = getCookie(event, 'foo') // ... Do something with the cookie }) ``` :: If you need to forward headers and cookies on the server, you must manually pass them: ```vue [pages/index.vue] <script setup lang="ts"> // This will forward the user's headers and cookies to `/api/cookies` const requestFetch = useRequestFetch() const { data } = await useAsyncData(() => requestFetch('/api/cookies')) </script> ``` However, when calling `useFetch` with a relative URL on the server, Nuxt will use [`useRequestFetch`](https://nuxt.com/docs/3.x/api/composables/use-request-fetch) to proxy headers and cookies (with the exception of headers not meant to be forwarded, like `host`). --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/entry.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "navigateTo" description: "navigateTo is a helper function that programmatically navigates users." canonical_url: "https://nuxt.com/docs/3.x/api/utils/navigate-to" --- # navigateTo > navigateTo is a helper function that programmatically navigates users. ## Usage `navigateTo` is available on both server side and client side. It can be used within the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context), or directly, to perform page navigation. ::warning Make sure to always use `await` or `return` on result of `navigateTo` when calling it. :: ::note `navigateTo` cannot be used within Nitro routes. To perform a server-side redirect in Nitro routes, use [`sendRedirect`](https://h3.dev/utils/response#redirectlocation-status-statustext) instead. :: ### Within a Vue Component ```vue <script setup lang="ts"> // passing 'to' as a string await navigateTo('/search') // ... or as a route object await navigateTo({ path: '/search' }) // ... or as a route object with query parameters await navigateTo({ path: '/search', query: { page: 1, sort: 'asc', }, }) </script> ``` ### Within Route Middleware ```ts export default defineNuxtRouteMiddleware((to, from) => { if (to.path !== '/search') { // setting the redirect code to '301 Moved Permanently' return navigateTo('/search', { redirectCode: 301 }) } }) ``` When using `navigateTo` within route middleware, you must **return its result** to ensure the middleware execution flow works correctly. For example, the following implementation **will not work as expected**: ```ts export default defineNuxtRouteMiddleware((to, from) => { if (to.path !== '/search') { // ❌ This will not work as expected navigateTo('/search', { redirectCode: 301 }) return } }) ``` In this case, `navigateTo` will be executed but not returned, which may lead to unexpected behavior. ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/middleware"} :: ### Navigating to an External URL The `external` parameter in `navigateTo` influences how navigating to URLs is handled: - **Without `external: true`**: - Internal URLs navigate as expected. - External URLs throw an error. - **With `external: true`**: - Internal URLs navigate with a full-page reload. - External URLs navigate as expected. #### Example ```vue <script setup lang="ts"> // will throw an error; // navigating to an external URL is not allowed by default await navigateTo('https://nuxt.com') // will redirect successfully with the 'external' parameter set to 'true' await navigateTo('https://nuxt.com', { external: true, }) </script> ``` ### Opening a Page in a New Tab ```vue <script setup lang="ts"> // will open 'https://nuxt.com' in a new tab await navigateTo('https://nuxt.com', { open: { target: '_blank', windowFeatures: { width: 500, height: 500, }, }, }) </script> ``` ## Type ```ts [Signature] export function navigateTo ( to: RouteLocationRaw | undefined | null, options?: NavigateToOptions, ): Promise<void | NavigationFailure | false> | false | void | RouteLocationRaw interface NavigateToOptions { replace?: boolean redirectCode?: number external?: boolean open?: OpenOptions } type OpenOptions = { target: string windowFeatures?: OpenWindowFeatures } type OpenWindowFeatures = { popup?: boolean noopener?: boolean noreferrer?: boolean } & XOR<{ width?: number }, { innerWidth?: number }> & XOR<{ height?: number }, { innerHeight?: number }> & XOR<{ left?: number }, { screenX?: number }> & XOR<{ top?: number }, { screenY?: number }> ``` ## Parameters ### `to` **Type**: [`RouteLocationRaw`](https://router.vuejs.org/api/interfaces/routelocationoptions) | `undefined` | `null` **Default**: `'/'` `to` can be a plain string or a route object to redirect to. When passed as `undefined` or `null`, it will default to `'/'`. #### Example ```ts // Passing the URL directly will redirect to the '/blog' page await navigateTo('/blog') // Using the route object, will redirect to the route with the name 'blog' await navigateTo({ name: 'blog' }) // Redirects to the 'product' route while passing a parameter (id = 1) using the route object. await navigateTo({ name: 'product', params: { id: 1 } }) ``` ### `options` (optional) **Type**: `NavigateToOptions` An object accepting the following properties: - `replace` - **Type**: `boolean` - **Default**: `false` - By default, `navigateTo` pushes the given route into the Vue Router's instance on the client side. This behavior can be changed by setting `replace` to `true`, to indicate that given route should be replaced. - `redirectCode` - **Type**: `number` - **Default**: `302` - `navigateTo` redirects to the given path and sets the redirect code to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/302) by default when the redirection takes place on the server side. This default behavior can be modified by providing different `redirectCode`. Commonly, [`301 Moved Permanently`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/301) can be used for permanent redirections. - `external` - **Type**: `boolean` - **Default**: `false` - Allows navigating to an external URL when set to `true`. Otherwise, `navigateTo` will throw an error, as external navigation is not allowed by default. - `open` - **Type**: `OpenOptions` - Allows navigating to the URL using the [open()](https://developer.mozilla.org/en-US/docs/Web/API/Window/open) method of the window. This option is only applicable on the client side and will be ignored on the server side. An object accepting the following properties: - `target` - **Type**: `string` - **Default**: `'_blank'` - A string, without whitespace, specifying the name of the browsing context the resource is being loaded into. - `windowFeatures` - **Type**: `OpenWindowFeatures` - An object accepting the following properties: | Property | Type | Description | | ------------------------- | --------- | ---------------------------------------------------------------------------------------------- | | `popup` | `boolean` | Requests a minimal popup window instead of a new tab, with UI features decided by the browser. | | `width` or `innerWidth` | `number` | Specifies the content area's width (minimum 100 pixels), including scrollbars. | | `height` or `innerHeight` | `number` | Specifies the content area's height (minimum 100 pixels), including scrollbars. | | `left` or `screenX` | `number` | Sets the horizontal position of the new window relative to the left edge of the screen. | | `top` or `screenY` | `number` | Sets the vertical position of the new window relative to the top edge of the screen. | | `noopener` | `boolean` | Prevents the new window from accessing the originating window via `window.opener`. | | `noreferrer` | `boolean` | Prevents the Referer header from being sent and implicitly enables `noopener`. | Refer to the [documentation](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#windowfeatures) for more detailed information on the **windowFeatures** properties. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "onBeforeRouteLeave" description: "The onBeforeRouteLeave composable allows registering a route guard within a component." canonical_url: "https://nuxt.com/docs/3.x/api/utils/on-before-route-leave" --- # onBeforeRouteLeave > The onBeforeRouteLeave composable allows registering a route guard within a component. ::read-more ```yaml [props] icon: i-simple-icons-vuedotjs to: https://router.vuejs.org/api/functions/onbeforerouteleave title: Vue Router Docs target: _blank ``` :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "onBeforeRouteUpdate" description: "The onBeforeRouteUpdate composable allows registering a route guard within a component." canonical_url: "https://nuxt.com/docs/3.x/api/utils/on-before-route-update" --- # onBeforeRouteUpdate > The onBeforeRouteUpdate composable allows registering a route guard within a component. ::read-more ```yaml [props] icon: i-simple-icons-vuedotjs to: https://router.vuejs.org/api/functions/onbeforerouteupdate title: Vue Router Docs target: _blank ``` :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "onNuxtReady" description: "The onNuxtReady composable allows running a callback after your app has finished initializing." canonical_url: "https://nuxt.com/docs/3.x/api/utils/on-nuxt-ready" --- # onNuxtReady > The onNuxtReady composable allows running a callback after your app has finished initializing. ::important `onNuxtReady` only runs on the client-side. It is ideal for running code that should not block the initial rendering of your app. :: ```ts [plugins/ready.client.ts] export default defineNuxtPlugin(() => { onNuxtReady(async () => { const myAnalyticsLibrary = await import('my-big-analytics-library') // do something with myAnalyticsLibrary }) }) ``` It is 'safe' to run even after your app has initialized. In this case, then the code will be registered to run in the next idle callback. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ready.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "prefetchComponents" description: "Nuxt provides utilities to give you control over prefetching components." canonical_url: "https://nuxt.com/docs/3.x/api/utils/prefetch-components" --- # prefetchComponents > Nuxt provides utilities to give you control over prefetching components. Prefetching component downloads the code in the background, this is based on the assumption that the component will likely be used for rendering, enabling the component to load instantly if and when the user requests it. The component is downloaded and cached for anticipated future use without the user making an explicit request for it. Use `prefetchComponents` to manually prefetch individual components that have been registered globally in your Nuxt app. By default Nuxt registers these as async components. You must use the Pascal-cased version of the component name. ```ts await prefetchComponents('MyGlobalComponent') await prefetchComponents(['MyGlobalComponent1', 'MyGlobalComponent2']) ``` ::note Current implementation behaves exactly the same as [`preloadComponents`](https://nuxt.com/docs/3.x/api/utils/preload-components) by preloading components instead of just prefetching we are working to improve this behavior. :: ::note On server, `prefetchComponents` will have no effect. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/preload.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "preloadComponents" description: "Nuxt provides utilities to give you control over preloading components." canonical_url: "https://nuxt.com/docs/3.x/api/utils/preload-components" --- # preloadComponents > Nuxt provides utilities to give you control over preloading components. Preloading components loads components that your page will need very soon, which you want to start loading early in rendering lifecycle. This ensures they are available earlier and are less likely to block the page's render, improving performance. Use `preloadComponents` to manually preload individual components that have been registered globally in your Nuxt app. By default Nuxt registers these as async components. You must use the Pascal-cased version of the component name. ```ts await preloadComponents('MyGlobalComponent') await preloadComponents(['MyGlobalComponent1', 'MyGlobalComponent2']) ``` ::note On server, `preloadComponents` will have no effect. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/preload.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "preloadRouteComponents" description: "preloadRouteComponents allows you to manually preload individual pages in your Nuxt app." canonical_url: "https://nuxt.com/docs/3.x/api/utils/preload-route-components" --- # preloadRouteComponents > preloadRouteComponents allows you to manually preload individual pages in your Nuxt app. Preloading routes loads the components of a given route that the user might navigate to in future. This ensures that the components are available earlier and less likely to block the navigation, improving performance. ::tip{icon="i-lucide-rocket"} Nuxt already automatically preloads the necessary routes if you're using the `NuxtLink` component. :: ::read-more{to="https://nuxt.com/docs/3.x/api/components/nuxt-link"} :: ## Example Preload a route when using `navigateTo`. ```ts // we don't await this async function, to avoid blocking rendering // this component's setup function preloadRouteComponents('/dashboard') const submit = async () => { const results = await $fetch('/api/authentication') if (results.token) { await navigateTo('/dashboard') } } ``` ::read-more{to="https://nuxt.com/docs/3.x/api/utils/navigate-to"} :: ::note On server, `preloadRouteComponents` will have no effect. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/preload.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "prerenderRoutes" description: "prerenderRoutes hints to Nitro to prerender an additional route." canonical_url: "https://nuxt.com/docs/3.x/api/utils/prerender-routes" --- # prerenderRoutes > prerenderRoutes hints to Nitro to prerender an additional route. When prerendering, you can hint to Nitro to prerender additional paths, even if their URLs do not show up in the HTML of the generated page. ::important `prerenderRoutes` can only be called within the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context). :: ::note `prerenderRoutes` has to be executed during prerendering. If the `prerenderRoutes` is used in dynamic pages/routes which are not prerendered, then it will not be executed. :: ```ts const route = useRoute() prerenderRoutes('/') prerenderRoutes(['/', '/about']) ``` ::note In the browser, or if called outside prerendering, `prerenderRoutes` will have no effect. :: You can even prerender API routes which is particularly useful for full statically generated sites (SSG) because you can then `$fetch` data as if you have an available server! ```ts prerenderRoutes('/api/content/article/name-of-article') // Somewhere later in App const articleContent = await $fetch('/api/content/article/name-of-article', { responseType: 'json', }) ``` ::warning Prerendered API routes in production may not return the expected response headers, depending on the provider you deploy to. For example, a JSON response might be served with an `application/octet-stream` content type. Always manually set `responseType` when fetching prerendered API routes. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "refreshCookie" description: "Refresh useCookie values manually when a cookie has changed" canonical_url: "https://nuxt.com/docs/3.x/api/utils/refresh-cookie" --- # refreshCookie > Refresh useCookie values manually when a cookie has changed ::important This utility is available since [Nuxt v3.10](https://nuxt.com/blog/v3-10). :: ## Purpose The `refreshCookie` function is designed to refresh cookie value returned by `useCookie`. This is useful for updating the `useCookie` ref when we know the new cookie value has been set in the browser. ## Usage ```vue [app.vue] <script setup lang="ts"> const tokenCookie = useCookie('token') const login = async (username, password) => { const token = await $fetch('/api/token', { /** ... */ }) // Sets `token` cookie on response refreshCookie('token') } const loggedIn = computed(() => !!tokenCookie.value) </script> ``` ::note{to="https://nuxt.com/docs/3.x/guide/going-further/experimental-features#cookiestore"} Since [Nuxt v3.12.0](https://github.com/nuxt/nuxt/releases/tag/v3.12.0), the experimental `cookieStore` option is enabled by default. It automatically refreshes the `useCookie` value when cookies change in the browser. :: ## Type ```ts [Signature] export function refreshCookie (name: string): void ``` --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/cookie.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "refreshNuxtData" description: "Refresh all or specific asyncData instances in Nuxt" canonical_url: "https://nuxt.com/docs/3.x/api/utils/refresh-nuxt-data" --- # refreshNuxtData > Refresh all or specific asyncData instances in Nuxt `refreshNuxtData` is used to refetch all or specific `asyncData` instances, including those from [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data), [`useLazyAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-lazy-async-data), [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch), and [`useLazyFetch`](https://nuxt.com/docs/3.x/api/composables/use-lazy-fetch). ::note If your component is cached by `<KeepAlive>` and enters a deactivated state, the `asyncData` inside the component will still be refetched until the component is unmounted. :: ## Type ```ts [Signature] export function refreshNuxtData (keys?: string | string[]) ``` ## Parameters - `keys`: A single string or an array of strings as `keys` that are used to fetch the data. This parameter is **optional**. All [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) keys are re-fetched when no `keys` are explicitly specified. ## Return Values `refreshNuxtData` returns a promise, resolving when all or specific `asyncData` instances have been refreshed. ## Examples ### Refresh All Data This example below refreshes all data being fetched using `useAsyncData` and `useFetch` in Nuxt application. ```vue [pages/some-page.vue] <script setup lang="ts"> const refreshing = ref(false) async function refreshAll () { refreshing.value = true try { await refreshNuxtData() } finally { refreshing.value = false } } </script> <template> <div> <button :disabled="refreshing" @click="refreshAll" > Refetch All Data </button> </div> </template> ``` ### Refresh Specific Data This example below refreshes only data where the key matches to `count` and `user`. ```vue [pages/some-page.vue] <script setup lang="ts"> const refreshing = ref(false) async function refresh () { refreshing.value = true try { // you could also pass an array of keys to refresh multiple data await refreshNuxtData(['count', 'user']) } finally { refreshing.value = false } } </script> <template> <div v-if="refreshing"> Loading </div> <button @click="refresh"> Refresh </button> </template> ``` ::note If you have access to the `asyncData` instance, it is recommended to use its `refresh` or `execute` method as the preferred way to refetch the data. :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/asyncData.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "reloadNuxtApp" description: "reloadNuxtApp will perform a hard reload of the page." canonical_url: "https://nuxt.com/docs/3.x/api/utils/reload-nuxt-app" --- # reloadNuxtApp > reloadNuxtApp will perform a hard reload of the page. ::note `reloadNuxtApp` will perform a hard reload of your app, re-requesting a page and its dependencies from the server. :: By default, it will also save the current `state` of your app (that is, any state you could access with `useState`). ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/experimental-features#restorestate" icon="i-lucide-star"} You can enable experimental restoration of this state by enabling the `experimental.restoreState` option in your `nuxt.config` file. :: ## Type ```ts [Signature] export function reloadNuxtApp (options?: ReloadNuxtAppOptions) interface ReloadNuxtAppOptions { ttl?: number force?: boolean path?: string persistState?: boolean } ``` ### `options` (optional) **Type**: `ReloadNuxtAppOptions` An object accepting the following properties: - `path` (optional) **Type**: `string` **Default**: `window.location.pathname` The path to reload (defaulting to the current path). If this is different from the current window location it will trigger a navigation and add an entry in the browser history. - `ttl` (optional) **Type**: `number` **Default**: `10000` The number of milliseconds in which to ignore future reload requests. If called again within this time period, `reloadNuxtApp` will not reload your app to avoid reload loops. - `force` (optional) **Type**: `boolean` **Default**: `false` This option allows bypassing reload loop protection entirely, forcing a reload even if one has occurred within the previously specified TTL. - `persistState` (optional) **Type**: `boolean` **Default**: `false` Whether to dump the current Nuxt state to sessionStorage (as `nuxt:reload:state`). By default this will have no effect on reload unless `experimental.restoreState` is also set, or unless you handle restoring the state yourself. --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/chunk.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "setPageLayout" description: "setPageLayout allows you to dynamically change the layout of a page." canonical_url: "https://nuxt.com/docs/3.x/api/utils/set-page-layout" --- # setPageLayout > setPageLayout allows you to dynamically change the layout of a page. ::important `setPageLayout` allows you to dynamically change the layout of a page. It relies on access to the Nuxt context and therefore can only be called within the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context). :: ```ts [middleware/custom-layout.ts] export default defineNuxtRouteMiddleware((to) => { // Set the layout on the route you are navigating _to_ setPageLayout('other') }) ``` ## Passing Props to Layouts You can pass props to the layout by providing an object as the second argument: ```ts [middleware/admin-layout.ts] export default defineNuxtRouteMiddleware((to) => { setPageLayout('admin', { sidebar: true, title: 'Dashboard', }) }) ``` The layout can then receive these props: ```vue [layouts/admin.vue] <script setup lang="ts"> const props = defineProps<{ sidebar?: boolean title?: string }>() </script> <template> <div> <aside v-if="sidebar"> Sidebar </aside> <main> <h1>{{ title }}</h1> <slot /> </main> </div> </template> ``` ::note If you choose to set the layout dynamically on the server side, you *must* do so before the layout is rendered by Vue (that is, within a plugin or route middleware) to avoid a hydration mismatch. :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/router.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "setResponseStatus" description: "setResponseStatus sets the status (and optionally the statusText) of the response." canonical_url: "https://nuxt.com/docs/3.x/api/utils/set-response-status" --- # setResponseStatus > setResponseStatus sets the status (and optionally the statusText) of the response. Nuxt provides composables and utilities for first-class server-side-rendering support. `setResponseStatus` sets the status (and optionally the statusText) of the response. ::important `setResponseStatus` can only be called in the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context). :: ```ts const event = useRequestEvent() // event will be undefined in the browser if (event) { // Set the status code to 404 for a custom 404 page setResponseStatus(event, 404) // Set the status message as well setResponseStatus(event, 404, 'Page Not Found') } ``` ::note In the browser, `setResponseStatus` will have no effect. :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/error-handling"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/ssr.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "showError" description: "Nuxt provides a quick and simple way to show a full screen error page if needed." canonical_url: "https://nuxt.com/docs/3.x/api/utils/show-error" --- # showError > Nuxt provides a quick and simple way to show a full screen error page if needed. Within the [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context) you can use `showError` to show an error. **Parameters:** - `error`: `string | Error | Partial<{ cause, data, message, name, stack, status, statusText }>` ```ts showError('😱 Oh no, an error has been thrown.') showError({ status: 404, statusText: 'Page Not Found', }) ``` The error is set in the state using [`useError()`](https://nuxt.com/docs/3.x/api/composables/use-error) to create a reactive and SSR-friendly shared error state across components. ::tip `showError` calls the `app:error` hook. :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/error-handling"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/composables/error.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "updateAppConfig" description: "Update the App Config at runtime." canonical_url: "https://nuxt.com/docs/3.x/api/utils/update-app-config" --- # updateAppConfig > Update the App Config at runtime. ::note Updates the [`app.config`](https://nuxt.com/docs/3.x/directory-structure/app-config) using deep assignment. Existing (nested) properties will be preserved. :: ## Usage ```js import { updateAppConfig, useAppConfig } from '#imports' const appConfig = useAppConfig() // { foo: 'bar' } const newAppConfig = { foo: 'baz' } updateAppConfig(newAppConfig) console.log(appConfig) // { foo: 'baz' } ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/app-config"} :: --- - [Source](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/config.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Legacy Composition API" description: "Learn how to migrate to Composition API with Nuxt Bridge." canonical_url: "https://nuxt.com/docs/3.x/bridge/bridge-composition-api" --- # Legacy Composition API > Learn how to migrate to Composition API with Nuxt Bridge. Nuxt Bridge provides access to Composition API syntax. It is specifically designed to be aligned with Nuxt 3. Because of this, there are a few extra steps to take when enabling Nuxt Bridge, if you have been using the Composition API previously. ## Remove Modules - Remove `@vue/composition-api` from your dependencies. - Remove `@nuxtjs/composition-api` from your dependencies (and from your modules in `nuxt.config`). ## Using `@vue/composition-api` If you have been using just `@vue/composition-api` and not `@nuxtjs/composition-api`, then things are very straightforward. 1. First, remove the plugin where you are manually registering the Composition API. Nuxt Bridge will handle this for you. ```diff - import Vue from 'vue' - import VueCompositionApi from '@vue/composition-api' - - Vue.use(VueCompositionApi) ``` 2. Otherwise, there is nothing you need to do. However, if you want, you can remove your explicit imports from `@vue/composition-api` and rely on Nuxt Bridge auto-importing them for you. ## Migrating from `@nuxtjs/composition-api` Nuxt Bridge implements the Composition API slightly differently from `@nuxtjs/composition-api` and provides different composables (designed to be aligned with the composables that Nuxt 3 provides). Because some composables have been removed and don't yet have a replacement, this will be a slightly more complicated process. ### Remove `@nuxtjs/composition-api/module` from your buildModules You don't have to immediately update your imports yet - Nuxt Bridge will automatically provide a 'shim' for most imports you currently have, to give you time to migrate to the new, Nuxt 3-compatible composables, with the following exceptions: - `withContext` has been removed. See [below](https://nuxt.com/docs/3.x/bridge/nuxt3-compatible-api#usecontext-and-withcontext). - `useStatic` has been removed. There is no current replacement. Feel free to raise a discussion if you have a use case for this. - `reqRef` and `reqSsrRef`, which were deprecated, have now been removed entirely. Follow the instructions below regarding [ssrRef](https://nuxt.com/docs/3.x/bridge/nuxt3-compatible-api#ssrref-and-shallowssrref) to replace this. ### Set `bridge.capi` ```ts import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { capi: true, nitro: false, // If migration to Nitro is complete, set to true }, }) ``` For each other composable you are using from `@nuxtjs/composition-api`, follow the steps below. ### useFetch `$fetchState` and `$fetch` have been removed. ```diff const { - $fetch, - $fetchState, + fetch, + fetchState, } = useFetch(() => { posts.value = await $fetch('/api/posts') }) ``` ### `defineNuxtMiddleware` This was a type-helper stub function that is now removed. Remove the `defineNuxtMiddleware` wrapper: ```diff - import { defineNuxtMiddleware } from '@nuxtjs/composition-api` - export default defineNuxtMiddleware((ctx) => {}) + export default (ctx) => {} ``` For typescript support, you can use `@nuxt/types`: ```ts import type { Middleware } from '@nuxt/types' export default <Middleware> function (ctx) { } ``` ### `defineNuxtPlugin` This was a type-helper stub function that is now removed. You may also keep using Nuxt 2-style plugins, by removing the function (as with [defineNuxtMiddleware](https://nuxt.com/docs/3.x/bridge/bridge-composition-api#definenuxtmiddleware)). Remove the `defineNuxtPlugin` wrapper: ```diff - import { defineNuxtPlugin } from '@nuxtjs/composition-api' - export default defineNuxtPlugin((ctx, inject) => {}) + export default (ctx, inject) => {} ``` For typescript support, you can use `@nuxt/types`: ```ts import type { Plugin } from '@nuxt/types' export default <Plugin> function (ctx, inject) {} ``` ::warning While this example is valid, Nuxt 3 introduces a new defineNuxtPlugin function that has a slightly different signature. :: ::read-more{link="/docs/3.x/directory-structure/plugins#creating-plugins"} :: ### `useRouter` and `useRoute` Nuxt Bridge provides direct replacements for these composables via [`useRouter`](https://nuxt.com/docs/3.x/api/composables/use-router) and `useRoute`. The only key difference is that [`useRoute`](https://nuxt.com/docs/3.x/api/composables/use-route) no longer returns a computed property. ```diff - import { useRouter, useRoute } from '@nuxtjs/composition-api' const router = useRouter() const route = useRoute() - console.log(route.value.path) + console.log(route.path) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Configuration" description: "Learn how to configure Nuxt Bridge to your own needs." canonical_url: "https://nuxt.com/docs/3.x/bridge/configuration" --- # Configuration > Learn how to configure Nuxt Bridge to your own needs. ## Feature Flags You can optionally disable some features from bridge or opt-in to less stable ones. In normal circumstances, it is always best to stick with defaults! You can check [bridge/src/module.ts](https://github.com/nuxt/bridge/blob/main/packages/bridge/src/module.ts) for latest defaults. ```ts [nuxt.config.ts] import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { // -- Opt-in features -- // Use Vite as the bundler instead of webpack 4 // vite: true, // Enable Nuxt 3 compatible useHead // meta: true, // Enable definePageMeta macro // macros: { // pageMeta: true // }, // Enable transpiling TypeScript with esbuild // typescript: { // esbuild: true // }, // -- Default features -- // Use legacy server instead of Nitro // nitro: false, // Disable Nuxt 3 compatible `nuxtApp` interface // app: false, // Disable Composition API support // capi: false, // ... or just disable legacy Composition API support // capi: { // legacy: false // }, // Do not transpile modules // transpile: false, // Disable <script setup> support // scriptSetup: false, // Disable composables auto importing // imports: false, // Do not warn about module incompatibilities // constraints: false }, vite: { // Config for Vite }, }) ``` ## Migration of each option ### router.base ```diff export default defineNuxtConfig({ - router: { - base: '/my-app/' - } + app: { + baseURL: '/my-app/' + } }) ``` ### build.publicPath ```diff export default defineNuxtConfig({ - build: { - publicPath: 'https://my-cdn.net' - } + app: { + cdnURL: 'https://my-cdn.net' + } }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Meta Tags" description: "Learn how to migrate from Nuxt 2 to Nuxt Bridge new meta tags." canonical_url: "https://nuxt.com/docs/3.x/bridge/meta" --- # Meta Tags > Learn how to migrate from Nuxt 2 to Nuxt Bridge new meta tags. If you need to access the component state with `head`, you should migrate to using [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) . If you need to use the Options API, there is a `head()` method you can use when you use `defineNuxtComponent`. ## Migration ### Set `bridge.meta` ```js import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { meta: true, nitro: false, // If migration to Nitro is complete, set to true }, }) ``` ### Update head properties In your `nuxt.config`, rename `head` to `app.head`. (Note that objects no longer have a `hid` key for deduplication.) ::code-group ```ts [Nuxt 2] export default { head: { titleTemplate: '%s - Nuxt', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { hid: 'description', name: 'description', content: 'Meta description' }, ], }, } ``` ```ts [Nuxt 3] export default defineNuxtConfig({ app: { head: { titleTemplate: '%s - Nuxt', meta: [ { charset: 'utf-8' }, { name: 'viewport', content: 'width=device-width, initial-scale=1' }, { name: 'description', content: 'Meta description' }, ], }, }, }) ``` :: ## `useHead` Composables Nuxt Bridge provides a new Nuxt 3 meta API that can be accessed with a new [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) composable. ```vue <script setup lang="ts"> useHead({ title: 'My Nuxt App', }) </script> ``` ::tip This [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) composable uses `@unhead/vue` under the hood (rather than `vue-meta`) to manipulate your `<head>`. :: ::warning We recommend not using the native Nuxt 2 `head()` properties in addition to [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) , as they may conflict. :: For more information on how to use this composable, see [the docs](https://nuxt.com/docs/3.x/getting-started/seo-meta). ## Options API ```vue <script> // if using options API `head` method you must use `defineNuxtComponent` export default defineNuxtComponent({ head (nuxtApp) { // `head` receives the nuxt app but cannot access the component instance return { meta: [{ name: 'description', content: 'This is my page description.', }], } }, }) </script> ``` ::warning Possible breaking change: `head` receives the nuxt app but cannot access the component instance. If the code in your `head` tries to access the data object through `this` or `this.$data`, you will need to migrate to the `useHead` composable. :: ## Title Template If you want to use a function (for full control), then this cannot be set in your nuxt.config, and it is recommended instead to set it within your `/layouts` directory. ```vue [layouts/default.vue] <script setup lang="ts"> useHead({ titleTemplate: (titleChunk) => { return titleChunk ? `${titleChunk} - Site Title` : 'Site Title' }, }) </script> ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nitro" description: "Activate Nitro to your Nuxt 2 application with Nuxt Bridge." canonical_url: "https://nuxt.com/docs/3.x/bridge/nitro" --- # Nitro > Activate Nitro to your Nuxt 2 application with Nuxt Bridge. ## Remove Modules - Remove `@nuxt/nitro`: Bridge injects same functionality ## Update Config ```ts [nuxt.config.ts] import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { nitro: true, }, }) ``` ## Update Your Scripts You will also need to update your scripts within your `package.json` to reflect the fact that Nuxt will now produce a Nitro server as build output. ### Install Nuxi Install `nuxi` as a development dependency: ::code-group{sync="pm"} ```bash [npm] npm install -D nuxi ``` ```bash [yarn] yarn add --dev nuxi ``` ```bash [pnpm] pnpm add -D nuxi ``` ```bash [bun] bun add -D nuxi ``` ```bash [deno] deno add -D npm:nuxi ``` :: ### Nuxi Nuxt 3 introduced the new Nuxt CLI command [`nuxi`](https://nuxt.com/docs/3.x/api/commands/add). Update your scripts as follows to leverage the better support from Nuxt Bridge: ```diff { "scripts": { - "dev": "nuxt", + "dev": "nuxi dev", - "build": "nuxt build", + "build": "nuxi build", - "start": "nuxt start", + "start": "nuxi preview" } } ``` ::tip If `nitro: false`, use the `nuxt2` command. :: ### Static Target If you have set `target: 'static'` in your `nuxt.config` then you need to ensure that you update your build script to be `nuxi generate`. ```json [package.json] { "scripts": { "build": "nuxi generate" } } ``` ### Server Target For all other situations, you can use the `nuxi build` command. ```json [package.json] { "scripts": { "build": "nuxi build", "start": "nuxi preview" } } ``` ## Exclude Built Nitro Folder From Git Add the folder `.output` to the `.gitignore` file. ## Ensure Everything Goes Well ✔️ Try with `nuxi dev` and `nuxi build` (or `nuxi generate`) to see if everything goes well. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "New Composition API" description: "Nuxt Bridge implements composables compatible with Nuxt 3." canonical_url: "https://nuxt.com/docs/3.x/bridge/nuxt3-compatible-api" --- # New Composition API > Nuxt Bridge implements composables compatible with Nuxt 3. By migrating from `@nuxtjs/composition-api` to the Nuxt 3 compatible API, there will be less rewriting when migrating to Nuxt 3. ## `ssrRef` and `shallowSsrRef` These two functions have been replaced with a new composable that works very similarly under the hood: `useState`. The key differences are that you must provide a *key* for this state (which Nuxt generated automatically for `ssrRef` and `shallowSsrRef`), and that it can only be called within a Nuxt 3 plugin (which is defined by `defineNuxtPlugin`) or a component instance. (In other words, you cannot use [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) with a global/ambient context, because of the danger of shared state across requests.) ```diff - import { ssrRef } from '@nuxtjs/composition-api' - const ref1 = ssrRef('initialData') - const ref2 = ssrRef(() => 'factory function') + const ref1 = useState('ref1-key', () => 'initialData') + const ref2 = useState('ref2-key', () => 'factory function') // accessing the state console.log(ref1.value) ``` Because the state is keyed, you can access the same state from multiple locations, as long as you are using the same key. You can read more about how to use this composable in [the Nuxt 3 docs](https://nuxt.com/docs/3.x/api/composables/use-state). ## `ssrPromise` This function has been removed, and you will need to find an alternative implementation if you were using it. If you have a use case for `ssrPromise`, please let us know via a discussion. ## `onGlobalSetup` This function has been removed, but its use cases can be met by using [`useNuxtApp`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app) or [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) within `defineNuxtPlugin`. You can also run any custom code within the `setup()` function of a layout. ```diff - import { onGlobalSetup } from '@nuxtjs/composition-api' - export default () => { - onGlobalSetup(() => { + export default defineNuxtPlugin((nuxtApp) => { + nuxtApp.hook('vue:setup', () => { // ... }) - } + }) ``` ## `useStore` In order to access Vuex store instance, you can use `useNuxtApp().$store`. ```diff - import { useStore } from '@nuxtjs/composition-api` + const { $store } = useNuxtApp() ``` ## `useContext` and `withContext` You can access injected helpers using `useNuxtApp`. ```diff - import { useContext } from '@nuxtjs/composition-api` + const { $axios } = useNuxtApp() ``` ::note `useNuxtApp()` also provides a key called `nuxt2Context` which contains all the same properties you would normally access from Nuxt 2 context, but it's advised *not* to use this directly, as it won't exist in Nuxt 3. Instead, see if there is another way to access what you need. (If not, please raise a feature request or discussion.) :: ## `wrapProperty` This helper function is not provided any more but you can replace it with the following code: ```ts import { computed, getCurrentInstance } from 'vue' const wrapProperty = (property: string, makeComputed = true) => () => { const vm = getCurrentInstance().proxy return makeComputed ? computed(() => vm[property]) : vm[property] } ``` ## `useAsync` and `useFetch` These two composables can be replaced with `useLazyAsyncData` and `useLazyFetch`, which are documented [in the Nuxt 3 docs](https://nuxt.com/docs/3.x/getting-started/data-fetching). Just like the previous `@nuxtjs/composition-api` composables, these composables do not block route navigation on the client-side (hence the 'lazy' part of the name). ::important Note that the API is entirely different, despite similar sounding names. Importantly, you should not attempt to change the value of other variables outside the composable (as you may have been doing with the previous `useFetch`). :: ::warning The `useLazyFetch` must have been configured for [Nitro](https://nuxt.com/docs/3.x/bridge/nitro). :: Migrating to the new composables from `useAsync`: ```diff <script setup> - import { useAsync } from '@nuxtjs/composition-api' - const posts = useAsync(() => $fetch('/api/posts')) + const { data: posts } = useLazyAsyncData('posts', () => $fetch('/api/posts')) + // or, more simply! + const { data: posts } = useLazyFetch('/api/posts') </script> ``` Migrating to the new composables from `useFetch`: ```diff <script setup> - import { useFetch } from '@nuxtjs/composition-api' - const posts = ref([]) - const { fetch } = useFetch(() => { posts.value = await $fetch('/api/posts') }) + const { data: posts, refresh } = useLazyAsyncData('posts', () => $fetch('/api/posts')) + // or, more simply! + const { data: posts, refresh } = useLazyFetch('/api/posts') function updatePosts() { - return fetch() + return refresh() } </script> ``` ### `useMeta` In order to interact with `vue-meta`, you may use `useNuxt2Meta`, which will work in Nuxt Bridge (but not Nuxt 3) and will allow you to manipulate your meta tags in a `vue-meta`-compatible way. ```diff <script setup> - import { useMeta } from '@nuxtjs/composition-api' useNuxt2Meta({ title: 'My Nuxt App', }) </script> ``` You can also pass in computed values or refs, and the meta values will be updated reactively: ```vue <script setup> const title = ref('my title') useNuxt2Meta({ title, }) title.value = 'new title' </script> ``` ::note Be careful not to use both `useNuxt2Meta()` and the Options API `head()` within the same component, as behavior may be unpredictable. :: Nuxt Bridge also provides a Nuxt 3-compatible meta implementation that can be accessed with the [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) composable. ```diff <script setup> - import { useMeta } from '@nuxtjs/composition-api' useHead({ title: 'My Nuxt App', }) </script> ``` You will also need to enable it explicitly in your `nuxt.config`: ```js import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { meta: true, }, }) ``` This [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) composable uses `@unhead/vue` under the hood (rather than `vue-meta`) to manipulate your `<head>`. Accordingly, it is recommended not to use both the native Nuxt 2 `head()` properties as well as [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) , as they may conflict. For more information on how to use this composable, see [the Nuxt 3 docs](https://nuxt.com/docs/3.x/getting-started/seo-meta). ### Explicit Imports Nuxt exposes every auto-import with the `#imports` alias that can be used to make the import explicit if needed: ```vue <script setup lang="ts"> import { computed, ref } from '#imports' const count = ref(1) const double = computed(() => count.value * 2) </script> ``` ### Disabling Auto-imports If you want to disable auto-importing composables and utilities, you can set `imports.autoImport` to `false` in the `nuxt.config` file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ imports: { autoImport: false, }, }) ``` This will disable auto-imports completely but it's still possible to use [explicit imports](https://nuxt.com/docs/3.x/bridge/nuxt3-compatible-api#explicit-imports) from `#imports`. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Overview" description: "Reduce the differences with Nuxt 3 and reduce the burden of migration to Nuxt 3." canonical_url: "https://nuxt.com/docs/3.x/bridge/overview" --- # Overview > Reduce the differences with Nuxt 3 and reduce the burden of migration to Nuxt 3. ::note If you're starting a fresh Nuxt 3 project, please skip this section and go to [Nuxt 3 Installation](https://nuxt.com/docs/3.x/getting-started/introduction). :: ::warning Nuxt Bridge provides identical features to Nuxt 3 ([docs](https://nuxt.com/docs/3.x/guide/concepts/auto-imports)) but there are some limitations, notably that [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) composables are not available. Please read the rest of this page for details. :: Bridge is a forward-compatibility layer that allows you to experience many of the new Nuxt 3 features by simply installing and enabling a Nuxt module. Using Nuxt Bridge, you can make sure your project is (almost) ready for Nuxt 3 and you can gradually proceed with the transition to Nuxt 3. ## First Step ### Upgrade Nuxt 2 Make sure your dev server (`nuxt dev`) isn't running, remove any package lock files (`package-lock.json` and `yarn.lock`), and install the latest Nuxt 2 version: ```diff [package.json] - "nuxt": "^2.16.3" + "nuxt": "^2.17.3" ``` Then, reinstall your dependencies: ::code-group{sync="pm"} ```bash [npm] npm install ``` ```bash [yarn] yarn install ``` ```bash [pnpm] pnpm install ``` ```bash [bun] bun install ``` ```bash [deno] deno install ``` :: ::note Once the installation is complete, make sure both development and production builds are working as expected before proceeding. :: ### Install Nuxt Bridge Install `@nuxt/bridge` and `nuxi` as development dependencies: ::code-group{sync="pm"} ```bash [npm] npm install -D @nuxt/bridge nuxi ``` ```bash [yarn] yarn add --dev @nuxt/bridge nuxi ``` ```bash [pnpm] pnpm add -D @nuxt/bridge nuxi ``` ```bash [bun] bun add -D @nuxt/bridge nuxi ``` ```bash [deno] deno add -D npm:@nuxt/bridge npm:nuxi ``` :: ### Update `nuxt.config` Please make sure to avoid any CommonJS syntax such as `module.exports`, `require` or `require.resolve` in your config file. It will soon be deprecated and unsupported. You can use static `import`, dynamic `import()` and `export default` instead. Using TypeScript by renaming to [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) is also possible and recommended. ```ts [nuxt.config.ts] import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: false, }) ``` ### Update Commands The `nuxt` command should now be changed to the `nuxt2` command. ```diff { "scripts": { - "dev": "nuxt", + "dev": "nuxt2", - "build": "nuxt build", + "build": "nuxt2 build", - "start": "nuxt start", + "start": "nuxt2 start" } } ``` Try running `nuxt2` once here. You will see that the application works as before. (If 'bridge' is set to false, your application will operate without any changes as before.) ## Upgrade Steps With Nuxt Bridge, the migration to Nuxt 3 can proceed in steps. The below `Upgrade Steps` does not need to be done all at once. - [TypeScript](https://nuxt.com/docs/3.x/bridge/typescript) - [Migrate Legacy Composition API](https://nuxt.com/docs/3.x/bridge/bridge-composition-api) - [Plugins and Middleware](https://nuxt.com/docs/3.x/bridge/plugins-and-middleware) - [Migrate New Composition API](https://nuxt.com/docs/3.x/bridge/nuxt3-compatible-api) - [Meta Tags](https://nuxt.com/docs/3.x/bridge/meta) - [Runtime Config](https://nuxt.com/docs/3.x/bridge/runtime-config) - [Nitro](https://nuxt.com/docs/3.x/bridge/nitro) - [Vite](https://nuxt.com/docs/3.x/bridge/vite) ## Migrate from CommonJS to ESM Nuxt 3 natively supports TypeScript and ECMAScript Modules. Please check [Native ES Modules](https://nuxt.com/docs/3.x/guide/concepts/esm) for more info and upgrading. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Plugins and Middleware" description: "Learn how to migrate from Nuxt 2 to Nuxt Bridge new plugins and middleware." canonical_url: "https://nuxt.com/docs/3.x/bridge/plugins-and-middleware" --- # Plugins and Middleware > Learn how to migrate from Nuxt 2 to Nuxt Bridge new plugins and middleware. ## New Plugins Format You can now migrate to the Nuxt 3 plugins API, which is slightly different in format from Nuxt 2. Plugins now take only one argument (`nuxtApp`). You can find out more in [the docs](https://nuxt.com/docs/3.x/directory-structure/plugins). ```ts [plugins/hello.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.provide('injected', () => 'my injected function') // now available on `nuxtApp.$injected` }) ``` ::note If you want to use the new Nuxt composables (such as [`useNuxtApp`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app) or `useRuntimeConfig`) within your plugins, you will need to use the `defineNuxtPlugin` helper for those plugins. :: ::warning Although a compatibility interface is provided via `nuxtApp.vueApp` you should avoid registering plugins, directives, mixins or components this way without adding your own logic to ensure they are not installed more than once, or this may cause a memory leak. :: ## New Middleware Format You can now migrate to the Nuxt 3 middleware API, which is slightly different in format from Nuxt 2. Middleware now take only two argument (`to`, `from`). You can find out more in [the docs](https://nuxt.com/docs/3.x/directory-structure/middleware). ```ts twoslash export default defineNuxtRouteMiddleware((to) => { if (to.path !== '/') { return navigateTo('/') } }) ``` ::important Use of `defineNuxtRouteMiddleware` is not supported outside of the `middleware` directory. :: ## definePageMeta You can also use [`definePageMeta`](https://nuxt.com/docs/3.x/api/utils/define-page-meta) in Nuxt Bridge. It can be enabled with the `macros.pageMeta` option in your configuration file ```ts [nuxt.config.ts] import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { macros: { pageMeta: true, }, }, }) ``` ::note But only for `middleware` and `layout`. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Runtime Config" description: "Nuxt provides a runtime config API to expose configuration and secrets within your application." canonical_url: "https://nuxt.com/docs/3.x/bridge/runtime-config" --- # Runtime Config > Nuxt provides a runtime config API to expose configuration and secrets within your application. ::warning When using `runtimeConfig` option, [nitro](https://nuxt.com/docs/3.x/bridge/nitro) must have been configured. :: ## Update Runtime Config Nuxt 3 approaches runtime config differently than Nuxt 2, using a new combined `runtimeConfig` option. First, you'll need to combine your `publicRuntimeConfig` and `privateRuntimeConfig` properties into a new one called `runtimeConfig`, with the public config within a key called `public`. ```diff // nuxt.config.js - privateRuntimeConfig: { - apiKey: process.env.NUXT_API_KEY || 'super-secret-key' - }, - publicRuntimeConfig: { - websiteURL: 'https://public-data.com' - } + runtimeConfig: { + apiKey: process.env.NUXT_API_KEY || 'super-secret-key', + public: { + websiteURL: 'https://public-data.com' + } + } ``` This also means that when you need to access public runtime config, it's behind a property called `public`. If you use public runtime config, you'll need to update your code. ```diff // MyWidget.vue - <div>Website: {{ $config.websiteURL }}</div> + <div>Website: {{ $config.public.websiteURL }}</div> ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "TypeScript" description: "Learn how to use TypeScript with Nuxt Bridge." canonical_url: "https://nuxt.com/docs/3.x/bridge/typescript" --- # TypeScript > Learn how to use TypeScript with Nuxt Bridge. ## Remove Modules - Remove `@nuxt/typescript-build`: Bridge enables same functionality - Remove `@nuxt/typescript-runtime` and `nuxt-ts`: Nuxt 2 has built-in runtime support ### Set `bridge.typescript` ```ts import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { typescript: true, nitro: false, // If migration to Nitro is complete, set to true }, }) ``` ## Update `tsconfig.json` If you are using TypeScript, you can edit your `tsconfig.json` to benefit from auto-generated Nuxt types: ```diff [tsconfig.json] { + "extends": "./.nuxt/tsconfig.json", "compilerOptions": { ... } } ``` ::note As `.nuxt/tsconfig.json` is generated and not checked into version control, you'll need to generate that file before running your tests. Add `nuxi prepare` as a step before your tests, otherwise you'll see `TS5083: Cannot read file '~/.nuxt/tsconfig.json'` :: ::note Keep in mind that all options extended from `./.nuxt/tsconfig.json` will be overwritten by the options defined in your `tsconfig.json`. Overwriting options such as `"compilerOptions.paths"` with your own configuration will lead TypeScript to not factor in the module resolutions from `./.nuxt/tsconfig.json`. This can lead to module resolutions such as `#imports` not being recognized. In case you need to extend options provided by `./.nuxt/tsconfig.json` further, you can use the `alias` property within your `nuxt.config`. `nuxi` will pick them up and extend `./.nuxt/tsconfig.json` accordingly. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Vite" description: "Activate Vite to your Nuxt 2 application with Nuxt Bridge." canonical_url: "https://nuxt.com/docs/3.x/bridge/vite" --- # Vite > Activate Vite to your Nuxt 2 application with Nuxt Bridge. ::warning When using `vite`, [nitro](https://nuxt.com/docs/3.x/bridge/nitro) must have been configured. :: ## Remove Modules - Remove `nuxt-vite`: Bridge enables same functionality ## Update Config ```ts [nuxt.config.ts] import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ bridge: { vite: true, nitro: true, }, }) ``` ## Configuration ```ts [nuxt.config.ts] import { defineNuxtConfig } from '@nuxt/bridge' export default defineNuxtConfig({ vite: { // Config for Vite }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Releases" description: "Discover the latest releases of Nuxt & Nuxt official modules." canonical_url: "https://nuxt.com/docs/3.x/community/changelog" --- # Releases > Discover the latest releases of Nuxt & Nuxt official modules. ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/nuxt to: https://github.com/nuxt/nuxt/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt framework releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/cli to: https://github.com/nuxt/cli/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt CLI (`@nuxt/cli`) releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/a11y to: https://github.com/nuxt/a11y/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt A11y releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/content to: https://github.com/nuxt/content/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt Content releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/devtools to: https://github.com/nuxt/devtools/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt DevTools releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/fonts to: https://github.com/nuxt/fonts/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt Fonts releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/hints to: https://github.com/nuxt/hints/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt Hints releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/image to: https://github.com/nuxt/image/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt Image releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/scripts to: https://github.com/nuxt/scripts/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt Scripts releases. ::: :::card ```yaml [props] icon: i-simple-icons-github title: nuxt/ui to: https://github.com/nuxt/ui/releases target: _blank ui.icon.base: text-black dark:text-white ``` Nuxt UI releases. ::: :: ::read-more{icon="i-simple-icons-github" to="https://github.com/nuxt" target="_blank"} Discover the `nuxt` organization on GitHub :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Contribution" description: "Nuxt is a community project - and so we love contributions of all kinds! ❤️" canonical_url: "https://nuxt.com/docs/3.x/community/contribution" --- # Contribution > Nuxt is a community project - and so we love contributions of all kinds! ❤️ There is a range of different ways you might be able to contribute to the Nuxt ecosystem. ## Ecosystem The Nuxt ecosystem includes many different projects and organizations: - [nuxt/](https://github.com/nuxt) - core repositories for the Nuxt framework itself. [**nuxt/nuxt**](https://github.com/nuxt/nuxt) contains the Nuxt framework (both versions 2 and 3). - [nuxt-modules/](https://github.com/nuxt-modules) - community-contributed and maintained modules and libraries. There is a [process to migrate a module](https://nuxt.com/docs/3.x/guide/modules/ecosystem) to `nuxt-modules`. While these modules have individual maintainers, they are not dependent on a single person. - [unjs/](https://github.com/unjs) - many of these libraries are used throughout the Nuxt ecosystem. They are designed to be universal libraries that are framework- and environment-agnostic. We welcome contributions and usage by other frameworks and projects. ## How To Contribute ### Triage Issues and Help Out in Discussions Check out the issues and discussions for the project you want to help. For example, here are [the issues board](https://github.com/nuxt/nuxt/issues) and [discussions](https://github.com/nuxt/nuxt/discussions) for Nuxt. Helping other users, sharing workarounds, creating reproductions, or even poking into a bug a little bit and sharing your findings makes a huge difference. ### Creating an Issue Thank you for taking the time to create an issue! ❤️ - **Reporting bugs**: Check out [our guide](https://nuxt.com/docs/3.x/community/reporting-bugs) for some things to do before opening an issue. - **Feature requests**: Check that there is not an existing issue or discussion covering the scope of the feature you have in mind. If the feature is to another part of the Nuxt ecosystem (such as a module), please consider raising a feature request there first. If the feature you have in mind is general or the API is not entirely clear, consider opening a discussion in the **Ideas** section to discuss with the community first. We'll do our best to follow our [internal issue decision making flowchart](https://mermaid.live/view#pako:eNqFlE1v2zAMhv8K4UuToslhx2Bo0TZt12Edhm7YMCAXWqJtorLk6qOpkfS_j7KdfpyWQ-BQr8mHL6nsCuU0FauiMm6rGvQRfq03FuRzvvvTYIQHthpcBT_ugQNwPHuZjheLxf4i1VDx8x4udrf5EBCOQvSsYg4ffS79KS9pmX9QALTgyid2KYB7Ih-4bmKWbDk2YB0E1gRUVaRi-FDmmjAmT3u4nB3DmoNKIUA1BsGSohA49jnVMQhHbDh_EZQUImyxh-gAtfaiG-KWSJ-N8nt6YtpCdgEeE5rXPOdav5YwWJIJU7zrvNADV9C7JBIyIC07Wxupkx3LFQ5vCkguRno5f9fP2qnUko0Y2dk9rGdvHAa9IIhVGlCp5FFNPN-ce4DKeXBd53xMliOLp9IZtyORQVsnrGm-WJzejtUu5fFqdr5FGQ3bLslYvGthjZbJTLpReZG5_lLYw7XQ_CbPVT92ws9gnEJj-v84dk-PiaXnmF1XGAaPsOsMKywNvYmG80ZohV8k4wDR9_N3KN_dHm5mh1lnkM5FsYzRfNiTvJoT5gnQsl6uxjqXLhkNQ9syHJ0UZZ8ERUIlNShr6N8gZDEliR-ow7QZa0fhY4LoHLRo-8N7ZxPwjRj5ZZYXpvOSNs9v3Jjs8NXB4ets92xan3zydXZHvj64lKMayh4-gZC1bjASW2ipLeWuzIuToiXfImu5rbucclMIc0ubYiWPGv3DptjYF9Fhiu5nb1Wxij7RSZE6jZHWjLXHtlhVaIJESXN0_m68_sO_wMs_oO9gyg) when responding to issues. ### Send a Pull Request We always welcome pull requests! ❤️ #### Before You Start Before you fix a bug, we recommend that you check whether **there's an issue that describes it**, as it's possible it's a documentation issue or that there is some context that would be helpful to know. If you're working on a feature, then we ask that you **open a feature request issue first** to discuss with the maintainers whether the feature is desired - and the design of those features. This helps save time for both the maintainers and the contributors and means that features can be shipped faster. The issue **should be confirmed** by a framework team member before building out a feature in a pull request. For typo fixes, it's recommended to batch multiple typo fixes into one pull request to maintain a cleaner commit history. For bigger changes to Nuxt itself, we recommend that you first [create a Nuxt module](https://nuxt.com/docs/3.x/community/contribution#create-a-module) and implement the feature there. This allows for quick proof-of-concept. You can then [create an RFC](https://nuxt.com/docs/3.x/community/contribution#make-an-rfc) in the form of a discussion. As users adopt it and you gather feedback, it can then be refined and either added to Nuxt core or continue as a standalone module. #### Commit Conventions We use [Conventional Commits](https://www.conventionalcommits.org) for commit messages, which [allows a changelog to be auto-generated](https://github.com/unjs/changelogen) based on the commits. Please read the guide through if you aren't familiar with it already. Note that `fix:` and `feat:` are for **actual code changes** (that might affect logic). For typo or document changes, use `docs:` or `chore:` instead: - ~~`fix: typo`~~ -> `docs: fix typo` If you are working in a project with a monorepo, like `nuxt/nuxt`, ensure that you specify the main scope of your commit in brackets. For example: `feat(kit): add 'addMagicStuff' utility`. #### Making the Pull Request If you don't know how to send a pull request, we recommend reading [the guide](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request). When sending a pull request, make sure your PR's title also follows the [Commit Convention](https://nuxt.com/docs/3.x/community/contribution#commit-conventions). If your PR fixes or resolves existing issues, please make sure you mention them in the PR description. It's ok to have multiple commits in a single PR; you don't need to rebase or force push for your changes as we will use `Squash and Merge` to squash the commits into one commit when merging. We do not add any commit hooks to allow for quick commits. But before you make a pull request, you should ensure that any lint/test scripts are passing. In general, please also make sure that there are no *unrelated* changes in a PR. For example, if your editor has made any changes to whitespace or formatting elsewhere in a file that you edited, please revert these so it is more obvious what your PR changes. And please avoid including multiple unrelated features or fixes in a single PR. If it is possible to separate them, it is better to have multiple PRs to review and merge separately. In general, a PR should do *one thing only*. #### Once You've Made a Pull Request Once you've made a pull request, we'll do our best to review it promptly. If we assign it to a maintainer, then that means that person will take special care to review it and implement any changes that may be required. If we request changes on a PR, please ignore the red text! It doesn't mean we think it's a bad PR - it's just a way of easily telling the status of a list of pull requests at a glance. If we mark a PR as 'pending', that means we likely have another task to do in reviewing the PR - it's an internal note-to-self, and not necessarily a reflection on whether the PR is a good idea or not. We will do our best to explain via a comment the reason for the pending status. We'll do our best to follow [our PR decision making flowchart](https://mermaid.live/view#pako:eNp9VE1v2kAQ_SsjXzBSEqlALlaUisSh0ACK2l4qcVm8Y9hi7672Iwly-O-ZtYPt5FAOCHbee_PmzdpVlCmOURLlhXrJ9sw4-JNuJNBnWs1UQafIQVjrERyWumAOv58-AJeXt29_0b7BXbWwwL0uRPa1vlZvcB_fF8oiMMmB2QM4BXkt3UoON7Lh3LWaDz2SVkK6QGt7DHvw0CKt5sxCKaQoWQEGtVHcZ04oGdw04LTVngW_LHOeFcURGGz97mw6PSv-iJdsi0UCA4nI7SfNwc3W3JZit3eQ1SZFDlKB15yswQ2MgbOjbYeatY3n8bcr-IWlekYYaJRcyB04I9gOB1CEfkF5dAVTzmFAtnqn4-bUYAiMMmHZgWhNPRhgus5mW2BATxq0NkIZ4Y4NbNjzE2ZchBzcHmGLe_ZMSKCcyRXyLrVFa_5n_PBK2xKy3kk9eOjULUdltk6C8kI-7NFDr8f4EVGDoqlp-wa4sJm3ltIMIuZ_mTQXJyTSkQZtunPqsKxShV9GKdkBYe1fHXjpbcjlvONlO9Kqx_M7YHmOmav_luxfE5zKwVs09hM5DLSupgYDlr5flDkwo7ykixKG-xDsUly1LZ-uY32dgDc7lG7YqwbNp0msJwmIUivjWFtfd-xRrEcJ7Omydz37qFplHOtxEp4GskI2qB5dRCWakglOz3oV8JuITJa4iRL6yZk5bKKNPBGOead-H2UWJc54vIiaW53SPgwrz4fIhVNm1bw76lfI6R2_MW21) when responding and reviewing to pull requests. ### AI-Assisted Contributions We welcome the thoughtful use of AI tools when contributing to Nuxt, yet ask all contributors to follow [two core principles](https://roe.dev/blog/using-ai-in-open-source). #### Never let an LLM speak for you - All comments, issues, and pull request descriptions should be written in your own voice - We value clear, human communication over perfect grammar or spelling - Avoid copy-pasting AI-generated summaries that don't reflect your own understanding #### Never let an LLM think for you - Feel free to use AI tools to generate code or explore ideas - Only submit contributions you fully understand and can explain - Contributions should reflect your own reasoning and problem-solving Our aim is ensuring quality and maintaining the joy of collaborating and communicating with real people. If you have ideas for improving our policy on AI in the Nuxt community, we'd love to hear them! ❤️ ### Create a Module If you've built something with Nuxt that's cool, why not [extract it into a module](https://nuxt.com/docs/3.x/guide/modules), so it can be shared with others? We have [many excellent modules already](https://nuxt.com/modules), but there's always room for more. If you need help while building it, feel free to [check in with us](https://nuxt.com/docs/3.x/community/getting-help). ### Make an RFC We highly recommend [creating a module](https://nuxt.com/docs/3.x/community/contribution#create-a-module) first to test out big new features and gain community adoption. If you have done this already, or it's not appropriate to create a new module, then please start by creating a new discussion. Make sure it explains your thinking as clearly as possible. Include code examples or function signatures for new APIs. Reference existing issues or pain points with examples. If we think this should be an RFC, we'll change the category to RFC and broadcast it more widely for feedback. An RFC will then move through the following stages: - `rfc: active` - currently open for comment - `rfc: approved` - approved by the Nuxt team - `rfc: ready to implement` - an issue has been created and assigned to implement - `rfc: shipped` - implemented - `rfc: archived` - not approved, but archived for future reference ### Conventions Across Ecosystem The following conventions are *required* within the `nuxt/` organization and recommended for other maintainers in the ecosystem. #### Module Conventions Modules should follow the [Nuxt module template](https://github.com/nuxt/starter/tree/module). See [module guide](https://nuxt.com/docs/3.x/guide/modules) for more information. #### Use Core `unjs/` Libraries We recommend the following libraries which are used throughout the ecosystem: - [pathe](https://github.com/unjs/pathe) - universal path utilities (replacement for node `path`) - [ufo](https://github.com/unjs/ufo) - URL parsing and joining utilities - [obuild](https://github.com/unjs/obuild) - rolldown-powered build system - ... check out the rest of the [unjs/](https://github.com/unjs) organization for many more! #### Use ESM Syntax and Default to `type: module` Most of the Nuxt ecosystem can consume ESM directly. In general we advocate that you avoid using CJS-specific code, such as `__dirname` and `require` statements. You can [read more about ESM](https://nuxt.com/docs/3.x/guide/concepts/esm). #### What's Corepack [Corepack](https://github.com/nodejs/corepack) makes sure you are using the correct version for package manager when you run corresponding commands. Projects might have `packageManager` field in their `package.json`. Under projects with configuration as shown below, Corepack will install `v7.5.0` of `pnpm` (if you don't have it already) and use it to run your commands. ```jsonc [package.json] { "packageManager": "pnpm@7.5.0" } ``` #### Use ESLint We use [ESLint](https://eslint.org) for both linting and formatting with [`@nuxt/eslint`](https://github.com/nuxt/eslint). ##### IDE Setup We recommend using [VS Code](https://code.visualstudio.com) along with the [ESLint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint). If you would like, you can enable auto-fix and formatting when you save the code you are editing: ```json [settings.json] { "editor.codeActionsOnSave": { "source.fixAll": "never", "source.fixAll.eslint": "explicit" } } ``` #### No Prettier Since ESLint is already configured to format the code, there is no need to duplicate the functionality with Prettier. To format the code, you can run `yarn lint --fix`, `pnpm lint --fix`, `bun run lint --fix`, or `deno run lint --fix` or referring the [ESLint section](https://nuxt.com/docs/3.x/community/contribution#use-eslint) for IDE Setup. If you have Prettier installed in your editor, we recommend you disable it when working on the project to avoid conflict. #### Package Manager We recommend `pnpm` as a package manager for modules, libraries and apps. It is important to enable Corepack to ensure you are on the same version of the package manager as the project. Corepack is built-in to new node versions for seamless package manager integration. To enable it, run ```bash [Terminal] corepack enable ``` You only need to do this one time, after Node.js is installed on your computer. ## Documentation Style Guide Documentation is an essential part of Nuxt. We aim to be an intuitive framework - and a big part of that is making sure that both the developer experience and the docs are perfect across the ecosystem. 👌 Here are some tips that may help improve your documentation: - Avoid subjective words like *simply*, *just*, *obviously...* when possible. Keep in mind your readers can have different backgrounds and experiences. Therefore, these words don't convey meaning and can be harmful. ::caution{icon="i-lucide-circle-x"} Simply make sure the function returns a promise. :: ::tip{icon="i-lucide-circle-check"} Make sure the function returns a [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). :: - Prefer [active voice](https://developers.google.com/tech-writing/one/active-voice). ::caution{icon="i-lucide-circle-x"} An error will be thrown by Nuxt. :: ::tip{icon="i-lucide-circle-check"} Nuxt will throw an error. :: ::read-more{to="https://nuxt.com/docs/3.x/community/framework-contribution#documentation-guide"} Learn how to contribute to the documentation. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Framework" description: "Some specific points about contributions to the framework repository." canonical_url: "https://nuxt.com/docs/3.x/community/framework-contribution" --- # Framework > Some specific points about contributions to the framework repository. Once you've read the [general contribution guide](https://nuxt.com/docs/3.x/community/contribution), here are some specific points to make about contributions to the [`nuxt/nuxt`](https://github.com/nuxt/nuxt) repository. ## Monorepo Guide - `packages/kit`: Toolkit for authoring Nuxt modules, published as [`@nuxt/kit`](https://www.npmjs.com/package/@nuxt/kit). - `packages/nuxt`: The core of Nuxt, published as [`nuxt`](https://www.npmjs.com/package/nuxt). - `packages/schema`: Cross-version Nuxt typedefs and defaults, published as [`@nuxt/schema`](https://www.npmjs.com/package/@nuxt/schema). - `packages/rspack`: The [Rspack](https://rspack.rs) bundler for Nuxt, published as [`@nuxt/rspack-builder`](https://www.npmjs.com/package/@nuxt/rspack-builder). - `packages/vite`: The [Vite](https://vite.dev) bundler for Nuxt, published as [`@nuxt/vite-builder`](https://www.npmjs.com/package/@nuxt/vite-builder). - `packages/webpack`: The [webpack](https://webpack.js.org) bundler for Nuxt, published as [`@nuxt/webpack-builder`](https://www.npmjs.com/package/@nuxt/webpack-builder). ## Setup To contribute to Nuxt, you need to set up a local environment. 1. [Fork](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/fork-a-repo) the [`nuxt/nuxt`](https://github.com/nuxt/nuxt) repository to your own GitHub account and then [clone](https://docs.github.com/en/repositories/creating-and-managing-repositories/cloning-a-repository) it to your local device. 2. Ensure using the latest [Node.js](https://nodejs.org/en) 3. Enable [Corepack](https://github.com/nodejs/corepack) to have `pnpm` and `yarn` ```bash [Terminal] corepack enable ``` 4. Run `pnpm install --frozen-lockfile` to Install the dependencies with pnpm: ```bash [Terminal] pnpm install --frozen-lockfile ``` ::note If you are adding a dependency, please use `pnpm add`. The `pnpm-lock.yaml` file is the source of truth for all Nuxt dependencies. :: 5. Activate the passive development system ```bash [Terminal] pnpm dev:prepare ``` 6. Check out a branch where you can work and commit your changes: ```bash [Terminal] git checkout -b my-new-branch ``` Then, test your changes against the [playground](https://nuxt.com/docs/3.x/community/framework-contribution#playground) and [test](https://nuxt.com/docs/3.x/community/framework-contribution#testing) your changes before submitting a pull request. ### Playground While working on a pull request, you will likely want to check if your changes are working correctly. You can modify the example app in `playground/`, and run: ```bash [Terminal] pnpm dev ``` ::important Please make sure not to commit it to your branch, but it could be helpful to add some example code to your PR description. This can help reviewers and other Nuxt users understand the feature you've built in-depth. :: ### Testing Every new feature should have a corresponding unit test (if possible). The `test/` directory in this repository is currently a work in progress, but do your best to create a new test following the example of what's already there. Before creating a PR or marking it as ready-to-review, ensure that all tests pass by running: ```bash [Terminal] pnpm test ``` ### Linting You might have noticed already that we use ESLint to enforce a coding standard. Before committing your changes, to verify that the code style is correct, run: ```bash [Terminal] pnpm lint ``` ::note You can use `pnpm lint --fix` to fix most of the style changes. If there are still errors left, you must correct them manually. :: ### Documentation If you are adding a new feature or refactoring or changing the behavior of Nuxt in any other manner, you'll likely want to document the changes. Please include any changes to the docs in the same PR. You don't have to write documentation up on the first commit (but please do so as soon as your pull request is mature enough). ::important Make sure to make changes according to the [Documentation Style Guide](https://nuxt.com/docs/3.x/community/contribution#documentation-style-guide). :: ### Final Checklist When submitting your PR, there is a simple template that you have to fill out. Please tick all appropriate "answers" in the checklists. ## Documentation Guide If you spot an area where we can improve documentation or error messages, please do open a PR - even if it's just to fix a typo! ::important Make sure to make changes according to the [Documentation Style Guide](https://nuxt.com/docs/3.x/community/contribution#documentation-style-guide). :: ### Quick Edits If you spot a typo or want to rephrase a sentence, you can click on the **Edit this page** link located on the right aside in the **Community** section. Make the change directly in the GitHub interface and open a Pull Request. ### Longer Edits The documentation content is inside the `docs/` directory of the [nuxt/nuxt](https://github.com/nuxt/nuxt) repository and written in markdown. ::note To preview the docs locally, follow the steps on [nuxt/nuxt.com](https://github.com/nuxt/nuxt.com) repository. :: ::note We recommend that you install the [MDC extension](https://marketplace.visualstudio.com/items?itemName=Nuxt.mdc) for VS Code. :: When linking to other documentation pages, use relative paths without the domain or a version segment: `/docs/getting-started/installation` rather than `/docs/3.x/getting-started/installation`. [nuxt.com](http://nuxt.com) inserts the version segment of the branch the page was built from, so the same link resolves correctly on every version and docs changes can be cherry-picked between branches unchanged. Only write a version segment when you deliberately mean *another* version's docs, and in that case use the full URL, `https://nuxt.com/docs/4.x/getting-started/introduction`, so the intent is obvious in review. ### Linting Docs Documentation is linted using [MarkdownLint](https://github.com/DavidAnson/markdownlint) and [case police](https://github.com/antfu/case-police) to keep the documentation cohesive. ```bash [Terminal] pnpm lint:docs ``` ::note You can also run `pnpm lint:docs:fix` to highlight and resolve any lint issues. :: ### Open a PR Please make sure your PR title adheres to the [conventional commits](https://www.conventionalcommits.org) guidelines. ```bash [Example of PR title] docs: update the section about the nuxt.config.ts file ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Getting Help" description: "We're a friendly community of developers and we'd love to help." canonical_url: "https://nuxt.com/docs/3.x/community/getting-help" --- # Getting Help > We're a friendly community of developers and we'd love to help. At some point, you may find that there's an issue you need some help with. But don't worry! We're a friendly community of developers and we'd love to help. ::card-group :::card ```yaml [props] icon: i-simple-icons-discord title: Discord to: https://go.nuxt.com/discord target: _blank ``` Get real-time help, exchange with the core team and the community, and stay updated on the latest Nuxt news. ::: :::card ```yaml [props] icon: i-simple-icons-nuxt title: Nuxters to: https://nuxters.nuxt.com target: _blank ``` Connect with other Nuxt enthusiasts. ::: :: ## "I can't figure out how to (...)." You've read through these docs and you think it should be possible, but it's not clear how. The best thing is to [open a GitHub Discussion](https://github.com/nuxt/nuxt/discussions). Please don't feel embarrassed about asking a question that you think is easy - we've all been there! ❤️ Everyone you'll encounter is helping out because they care, not because they are paid to do so. The kindest thing to do is make it easy for them to help you. Here are some ideas: - *Explain what your objective is, not just the problem you're facing.* "I need to ensure my form inputs are accessible, so I'm trying to get the ids to match between server and client." - *Make sure you've first read the docs and used your favorite search engine*. Let people know by saying something like "I've Googled for 'nuxt script setup' but I couldn't find code examples anywhere." - *Explain what you've tried.* Tell people the kind of solutions you've experimented with, and why. Often this can make people's advice more relevant to your situation. - *Share your code.* People probably won't be able to help if they just see an error message or a screenshot - but that all changes if you share your code in a copy/pasteable format - preferably in the form of a minimal reproduction like a CodeSandbox. And finally, just ask the question! There's no need to [ask permission to ask a question](https://dontasktoask.com) or [wait for someone to reply to your 'hello'](https://www.nohello.com). If you do, you might not get a response because people are waiting for the whole question before engaging. ## "Could there be a bug?" Something isn't working the way that the docs say that it should. You're not sure if it's a bug. You've searched through the [open issues](https://github.com/nuxt/nuxt/issues) and [discussions](https://github.com/nuxt/nuxt/discussions) but you can't find anything. (if there is a closed issue, please create a new one) We recommend taking a look at [how to report bugs](https://nuxt.com/docs/3.x/community/reporting-bugs). Nuxt is still in active development, and every issue helps make it better. ## "I need professional help" If the community couldn't provide the help you need in the time-frame you have, NuxtLabs offers professional support with the [Nuxt Experts](https://nuxt.com/enterprise/agencies). The objective of the Nuxt Expert is to provide support to the Vue ecosystem, while also creating freelance opportunities for those contributing to open-source solutions, thus helping to maintain the sustainability of the ecosystem. The Nuxt experts are Vue, Nuxt and Vite chosen contributors providing professional support and consulting services. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Reporting Bugs" description: "One of the most valuable roles in open source is taking the time to report bugs helpfully." canonical_url: "https://nuxt.com/docs/3.x/community/reporting-bugs" --- # Reporting Bugs > One of the most valuable roles in open source is taking the time to report bugs helpfully. Try as we might, we will never completely eliminate bugs. Even if you can't fix the underlying code, reporting a bug well can enable someone else with a bit more familiarity with the codebase to spot a pattern or make a quick fix. Here are a few key steps. ## Is It Really a Bug? Consider if you're looking to get help with something, or whether you think there's a bug with Nuxt itself. If it's the former, we'd love to help you - but the best way to do that is through [asking for help](https://nuxt.com/docs/3.x/community/getting-help) rather than reporting a bug. ## Search the Issues Search through the [open issues](https://github.com/nuxt/nuxt/issues) and [discussions](https://github.com/nuxt/nuxt/discussions) first. If you find anything that seems like the same bug, it's much better to comment on an existing thread than create a duplicate. ## Create a Minimal Reproduction It's important to be able to reproduce the bug reliably - in a minimal way and apart from the rest of your project. This narrows down what could be causing the issue and makes it possible for someone not only to find the cause, but also to test a potential solution. Start with the Nuxt sandbox and add the **minimum** amount of code necessary to reproduce the bug you're experiencing. ::note If your issue concerns Vue or Vite, please try to reproduce it first with the Vue SSR starter. :: **Nuxt**: ::card-group :::card ```yaml [props] title: Nuxt on StackBlitz icon: i-simple-icons-stackblitz to: https://nuxt.new/s/v3 target: _blank ``` ::: :::card ```yaml [props] title: Nuxt on CodeSandbox icon: i-simple-icons-codesandbox to: https://nuxt.new/c/v3 target: _blank ``` ::: :: **Vue**: ::card-group :::card ```yaml [props] title: Vue SSR on StackBlitz icon: i-simple-icons-stackblitz to: https://stackblitz.com/github/nuxt-contrib/vue3-ssr-starter/tree/main?terminal=dev target: _blank ``` ::: :::card ```yaml [props] title: Vue SSR on CodeSandbox icon: i-simple-icons-codesandbox to: https://codesandbox.io/p/sandbox/github/nuxt-contrib/vue3-ssr-starter/main target: _blank ``` ::: :::card ```yaml [props] title: Vue SSR Template on GitHub icon: i-simple-icons-github to: https://github.com/nuxt-contrib/vue3-ssr-starter/generate target: _blank ``` ::: :: Once you've reproduced the issue, remove as much code from your reproduction as you can (while still recreating the bug). The time spent making the reproduction as minimal as possible will make a huge difference to whoever sets out to fix the issue. ## Figure Out What the Cause Might Be With a Nuxt project, there are lots of moving pieces - from [Nuxt modules](https://nuxt.com/modules) to [other JavaScript libraries](https://www.npmjs.com). Try to report the bug at the most relevant and specific place. That will likely be the Nuxt module causing an issue, or the upstream library that Nuxt is depending on. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Roadmap" description: "Nuxt is constantly evolving, with new features and modules being added all the time." canonical_url: "https://nuxt.com/docs/3.x/community/roadmap" --- # Roadmap > Nuxt is constantly evolving, with new features and modules being added all the time. ::read-more{to="https://nuxt.com/blog"} See our blog for the latest framework and ecosystem announcements. :: ## Status Reports ::read-more{icon="i-simple-icons-github" to="https://github.com/nuxt/nuxt/issues/13653" target="_blank"} Documentation Progress :: ::read-more{icon="i-simple-icons-github" to="https://github.com/nuxt/nuxt/discussions/16119" target="_blank"} Rendering Optimizations: Today and Tomorrow :: ::read-more{icon="i-simple-icons-github" to="https://github.com/nuxt/image/discussions/563" target="_blank"} Nuxt Image: Performance and Status :: ## Roadmap {#roadmap} In roadmap below are some features we are planning or working on at the moment. ::tip Check [Discussions](https://github.com/nuxt/nuxt/discussions) and [RFCs](https://github.com/nuxt/nuxt/discussions/categories/rfcs) for more upcoming features and ideas. :: | Milestone | Expected date | Notes | Description | | ------------ | ------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | | SEO & PWA | 2025 | [nuxt/nuxt#18395](https://github.com/nuxt/nuxt/discussions/18395) | Migrating from [nuxt-community/pwa-module](https://github.com/nuxt-community/pwa-module) for built-in SEO utils and service worker support | | Assets | 2025 | [nuxt/nuxt#22012](https://github.com/nuxt/nuxt/discussions/22012) | Allow developers and modules to handle loading third-party assets. | | Translations | - | [nuxt/nuxt.com#1711](https://github.com/nuxt/nuxt.com/issues/1711) | A collaborative project for a stable translation process for Nuxt docs. Currently pending for ideas and documentation tooling support. | ## Core Modules Roadmap In addition to the Nuxt framework, there are modules that are vital for the ecosystem. Their status will be updated below. | Module | Status | Nuxt Support | Repository | Description | | ------------------------------------ | ------------ | ------------ | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Auth Utils | Planned | 4.x, 5.x | `nuxt/auth-utils` to be announced | The temporary repository [atinux/nuxt-auth-utils](https://github.com/atinux/nuxt-auth-utils) is available while awaiting its official integration into Nuxt via RFC. | | [a11y](https://github.com/nuxt/a11y) | Public Alpha | 3.x, 4.x | [nuxt/a11y](https://github.com/nuxt/a11y). | Real-time accessibility feedback and automated testing in your browser during development (see [nuxt/nuxt#23255](https://github.com/nuxt/nuxt/issues/23255)). | ## Release Cycle Since January 2023, we've adopted a consistent release cycle for Nuxt, following [semver](https://semver.org). We aim for major framework releases every year, with an expectation of patch releases every week or so and minor releases every month or so. They should never contain breaking changes except within options clearly marked as `experimental`. We are planning a slight variation from this plan for Nuxt 4 and Nuxt 5. Nuxt 4 will be a stability-focused release containing all `compatibilityVersion: 4` features, and will be followed shortly by Nuxt 5 which will include an upgrade to Nitro v3 and additional changes. This approach separates breaking changes into manageable phases, allowing for better ecosystem testing and smoother migrations. ### Ongoing Support for Nuxt We commit to support each major version of Nuxt for a minimum of six months after the release of the next major version, and to providing an upgrade path for current users at that point. ### Current Packages The current active version of [Nuxt](https://nuxt.com) is **v4** which is available as `nuxt` on npm with the `latest` tag. Nuxt 3 will continue to receive maintenance updates (bug fixes and security patches) until the end of July 2026. Each active version has its own nightly releases which are generated automatically. For more about enabling the Nuxt nightly release channel, see [the nightly release channel docs](https://nuxt.com/docs/3.x/guide/going-further/nightly-release-channel). | Release | | Initial release | End Of Life | Docs | | --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | -------------------------- | ------------------------------------------------------------------ | | **5.x** (scheduled) | | Q1 2026 (estimated) | TBA | | | **4.x** (stable) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt latest version" src="https://img.shields.io/npm/v/nuxt.svg?logo=nuxt&label=&style=flat&colorA=18181B&colorB=28CF8D" class="not-prose h-5 w-auto" zoom="false"></a> | 2025-07-16 | 6 months after 5.x release | [nuxt.com](https://nuxt.com/docs/4.x/getting-started/introduction) | | **3.x** (maintenance) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 3.x version" src="https://img.shields.io/npm/v/nuxt/3x.svg?logo=nuxt&label=&style=flat&colorA=18181B&colorB=28CF8D" class="not-prose h-5 w-auto" zoom="false"></a> | 2022-11-16 | 2026-07-31 | [nuxt.com](https://nuxt.com/docs/3.x/getting-started/introduction) | | **2.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 2.x version" src="https://img.shields.io/npm/v/nuxt/2x.svg?logo=nuxt&label=&style=flat&colorA=18181B&colorB=28CF8D" class="not-prose h-5 w-auto" zoom="false"></a> | 2018-09-21 | 2024-06-30 | [v2.nuxt.com](https://v2.nuxt.com/docs/get-started/installation/) | | **1.x** (unsupported) | <a href="https://www.npmjs.com/package/nuxt?activeTab=versions"><img alt="Nuxt 1.x version" src="https://img.shields.io/npm/v/nuxt/1x.svg?logo=nuxt&label=&style=flat&colorA=18181B&colorB=28CF8D" class="not-prose h-5 w-auto" zoom="false"></a> | 2018-01-08 | 2019-09-21 | | ### Support Status | Status | Description | | ----------- | ----------------------------------------------------------------------------- | | Unsupported | This version is not maintained any more and will not receive security patches | | Maintenance | This version will only receive security patches | | Stable | This version is being developed for and will receive security patches | | Development | This version could be unstable | | Scheduled | This version does not exist yet but is planned | ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt Directory Structure" description: "Learn about the directory structure of a Nuxt application and how to use it." canonical_url: "https://nuxt.com/docs/3.x/directory-structure" --- # Nuxt Directory Structure > Learn about the directory structure of a Nuxt application and how to use it. Nuxt applications have a specific directory structure that is used to organize the code. This structure is designed to be easy to understand and to be used in a consistent way. ## Root Directory The root directory of a Nuxt application is the directory that contains the `nuxt.config.ts` file. This file is used to configure the Nuxt application. ## App Directory The following directories are related to the universal Nuxt application: - [`assets/`](https://nuxt.com/docs/3.x/directory-structure/assets): website's assets that the build tool (Vite or webpack) will process - [`components/`](https://nuxt.com/docs/3.x/directory-structure/components): Vue components of the application - [`composables/`](https://nuxt.com/docs/3.x/directory-structure/composables): add your Vue composables - [`layouts/`](https://nuxt.com/docs/3.x/directory-structure/layouts): Vue components that wrap around your pages and avoid re-rendering between pages - [`middleware/`](https://nuxt.com/docs/3.x/directory-structure/middleware): run code before navigating to a particular route - [`pages/`](https://nuxt.com/docs/3.x/directory-structure/pages): file-based routing to create routes within your web application - [`plugins/`](https://nuxt.com/docs/3.x/directory-structure/plugins): use Vue plugins and more at the creation of your Nuxt application - [`utils/`](https://nuxt.com/docs/3.x/directory-structure/utils): add functions throughout your application that can be used in your components, composables, and pages. This directory also includes specific files: - [`app.config.ts`](https://nuxt.com/docs/3.x/directory-structure/app-config): a reactive configuration within your application - [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app): the root component of your Nuxt application - [`error.vue`](https://nuxt.com/docs/3.x/directory-structure/error): the error page of your Nuxt application ## Public Directory The [`public/`](https://nuxt.com/docs/3.x/directory-structure/public) directory is the directory that contains the public files of the Nuxt application. Files contained within this directory are served at the root and are not modified by the build process. This is suitable for files that have to keep their names (e.g. `robots.txt`) *or* likely won't change (e.g. `favicon.ico`). ## Server Directory The [`server/`](https://nuxt.com/docs/3.x/directory-structure/server) directory is the directory that contains the server-side code of the Nuxt application. It contains the following subdirectories: - [`api/`](https://nuxt.com/docs/3.x/directory-structure/server#server-routes): contains the API routes of the application. - [`routes/`](https://nuxt.com/docs/3.x/directory-structure/server#server-routes): contains the server routes of the application (e.g. dynamic `/sitemap.xml`). - [`middleware/`](https://nuxt.com/docs/3.x/directory-structure/server#server-middleware): run code before a server route is processed - [`plugins/`](https://nuxt.com/docs/3.x/directory-structure/server#server-plugins): use plugins and more at the creation of the Nuxt server - [`utils/`](https://nuxt.com/docs/3.x/directory-structure/server#server-utilities): add functions throughout your application that can be used in your server code. ## Shared Directory The [`shared/`](https://nuxt.com/docs/3.x/directory-structure/shared) directory is the directory that contains the shared code of the Nuxt application and Nuxt server. This code can be used in both the Vue app and the Nitro server. ## Content Directory The [`content/`](https://nuxt.com/docs/3.x/directory-structure/content) directory is enabled by the [Nuxt Content](https://content.nuxt.com) module. It is used to create a file-based CMS for your application using Markdown files. ## Modules Directory The [`modules/`](https://nuxt.com/docs/3.x/directory-structure/modules) directory is the directory that contains the local modules of the Nuxt application. Modules are used to extend the functionality of the Nuxt application. ## Layers Directory The [`layers/`](https://nuxt.com/docs/3.x/directory-structure/layers) directory allows you to organize and share reusable code, components, composables, and configurations. Layers within this directory are automatically registered in your project. ## Nuxt Files - [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) file is the main configuration file for the Nuxt application. - [`.nuxtrc`](https://nuxt.com/docs/3.x/directory-structure/nuxtrc) file is another syntax for configuring the Nuxt application (useful for global configurations). - [`.nuxtignore`](https://nuxt.com/docs/3.x/directory-structure/nuxtignore) file is used to ignore files in the root directory during the build phase. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "app.vue" description: "The app.vue file is the main component of your Nuxt application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/app" --- # app.vue > The app.vue file is the main component of your Nuxt application. ::tip If you have a `pages/` directory, the `app.vue` file is optional. Nuxt will automatically include a default `app.vue`, but you can still add your own to customize the structure and content as needed. :: ## Usage ### Minimal Usage With Nuxt, the [`pages/`](https://nuxt.com/docs/3.x/directory-structure/pages) directory is optional. If it is not present, Nuxt will not include the [vue-router](https://router.vuejs.org) dependency. This is useful when building a landing page or an application that does not require routing. ```vue [app.vue] <template> <h1>Hello World!</h1> </template> ``` ::link-example{to="https://nuxt.com/docs/examples/hello-world"} :: ### Usage with Pages When you have a [`pages/`](https://nuxt.com/docs/3.x/directory-structure/pages) directory, you need to use the [`<NuxtPage>`](https://nuxt.com/docs/3.x/api/components/nuxt-page) component to display the current page: ```vue [app.vue] <template> <NuxtPage /> </template> ``` You can also define the common structure of your application directly in `app.vue`. This is useful when you want to include global elements such as a header or footer: ```vue [app.vue] <template> <header> Header content </header> <NuxtPage /> <footer> Footer content </footer> </template> ``` ::note Remember that `app.vue` acts as the main component of your Nuxt application. Anything you add to it (JS and CSS) will be global and included in every page. :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/pages"} Learn more about how to structure your pages using the `pages/` directory. :: ### Usage with Layouts When your application requires different layouts for different pages, you can use the `layouts/` directory with the [`<NuxtLayout>`](https://nuxt.com/docs/3.x/api/components/nuxt-layout) component. This allows you to define multiple layouts and apply them per page. ```vue [app.vue] <template> <NuxtLayout> <NuxtPage /> </NuxtLayout> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/layouts"} Learn more about how to structure your layouts using the `layouts/` directory. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "app.config.ts" description: "Expose reactive configuration within your application with the App Config file." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/app-config" --- # app.config.ts > Expose reactive configuration within your application with the App Config file. Nuxt provides an `app.config.ts` config file to expose reactive configuration within your application with the ability to update it at runtime within lifecycle or using a nuxt plugin and editing it with HMR (hot-module-replacement). You can easily provide runtime app configuration using `app.config.ts` file. It can have either of `.ts`, `.js`, or `.mjs` extensions. ```ts twoslash [app.config.ts] export default defineAppConfig({ foo: 'bar', }) ``` ::caution Do not put any secret values inside `app.config` file. It is exposed to the user client bundle. :: ::note When configuring a custom [`srcDir`](https://nuxt.com/docs/3.x/api/nuxt-config#srcdir), make sure to place the `app.config` file at the root of the new `srcDir` path. :: ## Usage To expose config and environment variables to the rest of your app, you will need to define configuration in `app.config` file. ```ts twoslash [app.config.ts] export default defineAppConfig({ theme: { primaryColor: '#ababab', }, }) ``` We can now universally access `theme` both when server-rendering the page and in the browser using [`useAppConfig`](https://nuxt.com/docs/3.x/api/composables/use-app-config) composable. ```vue [pages/index.vue] <script setup lang="ts"> const appConfig = useAppConfig() console.log(appConfig.theme) </script> ``` The [`updateAppConfig`](https://nuxt.com/docs/3.x/api/utils/update-app-config) utility can be used to update the `app.config` at runtime. ```vue [pages/index.vue] <script setup> const appConfig = useAppConfig() // { foo: 'bar' } const newAppConfig = { foo: 'baz' } updateAppConfig(newAppConfig) console.log(appConfig) // { foo: 'baz' } </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/api/utils/update-app-config"} Read more about the `updateAppConfig` utility. :: ## Typing App Config Nuxt tries to automatically generate a TypeScript interface from provided app config so you won't have to type it yourself. However, there are some cases where you might want to type it yourself. There are two possible things you might want to type. ### App Config Input `AppConfigInput` might be used by module authors who are declaring what valid *input* options are when setting app config. This will not affect the type of `useAppConfig()`. ```ts [index.d.ts] declare module 'nuxt/schema' { interface AppConfigInput { /** Theme configuration */ theme?: { /** Primary app color */ primaryColor?: string } } } // It is always important to ensure you import/export something when augmenting a type export {} ``` ### App Config Output If you want to type the result of calling [`useAppConfig()`](https://nuxt.com/docs/3.x/api/composables/use-app-config), then you will want to extend `AppConfig`. ::warning Be careful when typing `AppConfig` as you will overwrite the types Nuxt infers from your actually defined app config. :: ```ts [index.d.ts] declare module 'nuxt/schema' { interface AppConfig { // This will entirely replace the existing inferred `theme` property theme: { // You might want to type this value to add more specific types than Nuxt can infer, // such as string literal types primaryColor?: 'red' | 'blue' } } } // It is always important to ensure you import/export something when augmenting a type export {} ``` ## Merging Strategy Nuxt uses a custom merging strategy for the `AppConfig` within [the layers](https://nuxt.com/docs/3.x/getting-started/layers) of your application. This strategy is implemented using a [Function Merger](https://github.com/unjs/defu#function-merger), which allows defining a custom merging strategy for every key in `app.config` that has an array as value. ::note The function merger can only be used in the extended layers and not the main `app.config` in project. :: Here's an example of how you can use: ::code-group ```ts twoslash [layer/app.config.ts] export default defineAppConfig({ // Default array value array: ['hello'], }) ``` ```ts twoslash [app.config.ts] export default defineAppConfig({ // Overwrite default array value by using a merger function array: () => ['bonjour'], }) ``` :: ## Known Limitations As of Nuxt v3.3, the `app.config.ts` file is shared with Nitro, which results in the following limitations: 1. You cannot import Vue components directly in `app.config.ts`. 2. Some auto-imports are not available in the Nitro context. These limitations occur because Nitro processes the app config without full Vue component support. While it's possible to use Vite plugins in the Nitro config as a workaround, this approach is not recommended: ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { vite: { plugins: [vue()], }, }, }) ``` ::warning Using this workaround may lead to unexpected behavior and bugs. The Vue plugin is one of many that are not available in the Nitro context. :: Related issues: - [Issue #19858](https://github.com/nuxt/nuxt/issues/19858) - [Issue #19854](https://github.com/nuxt/nuxt/issues/19854) ::note Nitro v3 will resolve these limitations by removing support for the app config. You can track the progress in [this pull request](https://github.com/nitrojs/nitro/pull/2521). :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "assets" description: "The assets/ directory is used to add all the website's assets that the build tool will process." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/assets" --- # assets > The assets/ directory is used to add all the website's assets that the build tool will process. The directory usually contains the following types of files: - Stylesheets (CSS, SASS, etc.) - Fonts - Images that won't be served from the [`public/`](https://nuxt.com/docs/3.x/directory-structure/public) directory. If you want to serve assets from the server, we recommend taking a look at the [`public/`](https://nuxt.com/docs/3.x/directory-structure/public) directory. ::read-more{to="https://nuxt.com/docs/3.x/getting-started/assets"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "components" description: "The components/ directory is where you put all your Vue components." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/components" --- # components > The components/ directory is where you put all your Vue components. Nuxt automatically imports any components in this directory (along with components that are registered by any modules you may be using). ```bash [Directory Structure] -| components/ ---| AppHeader.vue ---| AppFooter.vue ``` ```html [app.vue] <template> <div> <AppHeader /> <NuxtPage /> <AppFooter /> </div> </template> ``` ## Component Names If you have a component in nested directories such as: ```bash [Directory Structure] -| components/ ---| base/ -----| foo/ -------| Button.vue ``` ... then the component's name will be based on its own path directory and filename, with duplicate segments being removed. Therefore, the component's name will be: ```html <BaseFooButton /> ``` ::note For clarity, we recommend that the component's filename matches its name. So, in the example above, you could rename `Button.vue` to be `BaseFooButton.vue`. :: If you want to auto-import components based only on its name, not path, then you need to set `pathPrefix` option to `false` using extended form of the configuration object: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: [ { path: '~/components', pathPrefix: false, // [!code ++] }, ], }) ``` This registers the components using the same strategy as used in Nuxt 2. For example, `~/components/Some/MyComponent.vue` will be usable as `<MyComponent>` and not `<SomeMyComponent>`. ## Dynamic Components If you want to use the Vue `<component :is="someComputedComponent">`{lang="vue"} syntax, you need to use the `resolveComponent` helper provided by Vue or import the component directly from `#components` and pass it into `is` prop. For example: ```vue [pages/index.vue] <script setup lang="ts"> import { SomeComponent } from '#components' const MyButton = resolveComponent('MyButton') </script> <template> <component :is="clickable ? MyButton : 'div'" /> <component :is="SomeComponent" /> </template> ``` ::important If you are using `resolveComponent` to handle dynamic components, make sure not to insert anything but the name of the component, which must be a literal string and not be or contain a variable. The string is statically analyzed at the compilation step. :: ::video-accordion{title="Watch Daniel Roe's short video about resolveComponent()" videoId="4kq8E5IUM2U"} :: Alternatively, though not recommended, you can register all your components globally, which will create async chunks for all your components and make them available throughout your application. ```diff export default defineNuxtConfig({ components: { + global: true, + dirs: ['~/components'] }, }) ``` You can also selectively register some components globally by placing them in a `~/components/global` directory, or by using a `.global.vue` suffix in the filename. As noted above, each global component is rendered in a separate chunk, so be careful not to overuse this feature. ::note The `global` option can also be set per component directory. :: ## Dynamic Imports To dynamically import a component (also known as lazy-loading a component) all you need to do is add the `Lazy` prefix to the component's name. This is particularly useful if the component is not always needed. By using the `Lazy` prefix you can delay loading the component code until the right moment, which can be helpful for optimizing your JavaScript bundle size. ```vue [pages/index.vue] <script setup lang="ts"> const show = ref(false) </script> <template> <div> <h1>Mountains</h1> <LazyMountainsList v-if="show" /> <button v-if="!show" @click="show = true" > Show List </button> </div> </template> ``` ## Delayed (or Lazy) Hydration Lazy components are great for controlling the chunk sizes in your app, but they don't always enhance runtime performance, as they still load eagerly unless conditionally rendered. In real-world applications, some pages may include a lot of content and a lot of components, and most of the time not all of them need to be interactive as soon as the page is loaded. Having them all load eagerly can negatively impact performance. In order to optimize your app, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks. Nuxt supports this using lazy (or delayed) hydration, allowing you to control when components become interactive. ### Hydration Strategies Nuxt provides a range of built-in hydration strategies. Only one strategy can be used per lazy component. ::note Any prop change on a lazily hydrated component will trigger hydration immediately. (e.g., changing a prop on a component with `hydrate-never` will cause it to hydrate) :: ::warning Currently Nuxt's built-in lazy hydration only works in single-file components (SFCs), and requires you to define the prop in the template (rather than spreading an object of props via `v-bind`). It also does not work with direct imports from `#components`. :: #### `hydrate-on-visible` Hydrates the component when it becomes visible in the viewport. ```vue [pages/index.vue] <template> <div> <LazyMyComponent hydrate-on-visible /> </div> </template> ``` ::read-more{to="https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver" title="IntersectionObserver options"} Read more about the options for `hydrate-on-visible`. :: ::note Under the hood, this uses Vue's built-in [`hydrateOnVisible` strategy](https://vuejs.org/guide/components/async#hydrate-on-visible). :: #### `hydrate-on-idle` Hydrates the component when the browser is idle. This is suitable if you need the component to load as soon as possible, but not block the critical rendering path. You can also pass a number which serves as a max timeout. ```vue [pages/index.vue] <template> <div> <LazyMyComponent hydrate-on-idle /> </div> </template> ``` ::note Under the hood, this uses Vue's built-in [`hydrateOnIdle` strategy](https://vuejs.org/guide/components/async#hydrate-on-idle). :: #### `hydrate-on-interaction` Hydrates the component after a specified interaction (e.g., click, mouseover). ```vue [pages/index.vue] <template> <div> <LazyMyComponent hydrate-on-interaction="mouseover" /> </div> </template> ``` If you do not pass an event or list of events, it defaults to hydrating on `pointerenter`, `click` and `focus`. ::note Under the hood, this uses Vue's built-in [`hydrateOnInteraction` strategy](https://vuejs.org/guide/components/async#hydrate-on-interaction). :: #### `hydrate-on-media-query` Hydrates the component when the window matches a media query. ```vue [pages/index.vue] <template> <div> <LazyMyComponent hydrate-on-media-query="(max-width: 768px)" /> </div> </template> ``` ::note Under the hood, this uses Vue's built-in [`hydrateOnMediaQuery` strategy](https://vuejs.org/guide/components/async#hydrate-on-media-query). :: #### `hydrate-after` Hydrates the component after a specified delay (in milliseconds). ```vue [pages/index.vue] <template> <div> <LazyMyComponent :hydrate-after="2000" /> </div> </template> ``` #### `hydrate-when` Hydrates the component based on a boolean condition. ```vue [pages/index.vue] <template> <div> <LazyMyComponent :hydrate-when="isReady" /> </div> </template> <script setup lang="ts"> const isReady = ref(false) function myFunction () { // trigger custom hydration strategy... isReady.value = true } </script> ``` #### `hydrate-never` Never hydrates the component. ```vue [pages/index.vue] <template> <div> <LazyMyComponent hydrate-never /> </div> </template> ``` ### Listening to Hydration Events All delayed hydration components emit a `@hydrated` event when they are hydrated. ```vue [pages/index.vue] <template> <div> <LazyMyComponent hydrate-on-visible @hydrated="onHydrate" /> </div> </template> <script setup lang="ts"> function onHydrate () { console.log('Component has been hydrated!') } </script> ``` ### Caveats and Best Practices Delayed hydration can offer performance benefits, but it's essential to use it correctly: 1. **Prioritize In-Viewport Content:** Avoid delayed hydration for critical, above-the-fold content. It's best suited for content that isn't immediately needed. 2. **Conditional Rendering:** When using `v-if="false"` on a lazy component, you might not need delayed hydration. You can just use a normal lazy component. 3. **Shared State:** Be mindful of shared state (`v-model`) across multiple components. Updating the model in one component can trigger hydration in all components bound to that model. 4. **Use Each Strategy's Intended Use Case:** Each strategy is optimized for a specific purpose. - `hydrate-when` is best for components that might not always need to be hydrated. - `hydrate-after` is for components that can wait a specific amount of time. - `hydrate-on-idle` is for components that can be hydrated when the browser is idle. 5. **Avoid `hydrate-never` on interactive components:** If a component requires user interaction, it should not be set to never hydrate. ## Direct Imports You can also explicitly import components from `#components` if you want or need to bypass Nuxt's auto-importing functionality. ```vue [pages/index.vue] <script setup lang="ts"> import { LazyMountainsList, NuxtLink } from '#components' const show = ref(false) </script> <template> <div> <h1>Mountains</h1> <LazyMountainsList v-if="show" /> <button v-if="!show" @click="show = true" > Show List </button> <NuxtLink to="/">Home</NuxtLink> </div> </template> ``` ## Custom Directories By default, only the `~/components` directory is scanned. If you want to add other directories, or change how the components are scanned within a subfolder of this directory, you can add additional directories to the configuration: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: [ // ~/calendar-module/components/event/Update.vue => <EventUpdate /> { path: '~/calendar-module/components' }, // ~/user-module/components/account/UserDeleteDialog.vue => <UserDeleteDialog /> { path: '~/user-module/components', pathPrefix: false }, // ~/components/special-components/Btn.vue => <SpecialBtn /> { path: '~/components/special-components', prefix: 'Special' }, // It's important that this comes last if you have overrides you wish to apply // to sub-directories of `~/components`. // // ~/components/Btn.vue => <Btn /> // ~/components/base/Btn.vue => <BaseBtn /> '~/components', ], }) ``` ::note Any nested directories need to be added first as they are scanned in order. :: ## npm Packages If you want to auto-import components from an npm package, you can use [`addComponent`](https://nuxt.com/docs/3.x/api/kit/components#addcomponent) in a [local module](https://nuxt.com/docs/3.x/directory-structure/modules) to register them. ::code-group ```ts twoslash [~/modules/register-component.ts] import { addComponent, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { // import { MyComponent as MyAutoImportedComponent } from 'my-npm-package' addComponent({ name: 'MyAutoImportedComponent', export: 'MyComponent', filePath: 'my-npm-package', }) }, }) ``` ```vue [app.vue] <template> <div> <!-- the component uses the name we specified and is auto-imported --> <MyAutoImportedComponent /> </div> </template> ``` :: ## Component Extensions By default, any file with an extension specified in the [extensions key of `nuxt.config.ts`](https://nuxt.com/docs/3.x/api/nuxt-config#extensions) is treated as a component. If you need to restrict the file extensions that should be registered as components, you can use the extended form of the components directory declaration and its `extensions` key: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: [ { path: '~/components', extensions: ['.vue'], // [!code ++] }, ], }) ``` ## Client Components If a component is meant to be rendered only client-side, you can add the `.client` suffix to your component. ```bash [Directory Structure] | components/ --| Comments.client.vue ``` ```vue [pages/example.vue] <template> <div> <!-- this component will only be rendered on client side --> <Comments /> </div> </template> ``` ::note This feature only works with Nuxt auto-imports and `#components` imports. Explicitly importing these components from their real paths does not convert them into client-only components. :: ::important `.client` components are rendered only after being mounted. To access the rendered template using `onMounted()`, add `await nextTick()` in the callback of the `onMounted()` hook. :: ::read-more{to="https://nuxt.com/docs/3.x/api/components/client-only"} You can also achieve a similar result with the `<ClientOnly>` component. :: ## Server Components Server components allow server-rendering individual components within your client-side apps. It's possible to use server components within Nuxt, even if you are generating a static site. That makes it possible to build complex sites that mix dynamic components, server-rendered HTML and even static chunks of markup. Server components can either be used on their own or paired with a [client component](https://nuxt.com/docs/3.x/directory-structure/components#paired-with-a-client-component). ::video-accordion{title="Watch Learn Vue video about Nuxt Server Components" videoId="u1yyXe86xJM"} :: ::tip{icon="i-lucide-newspaper" to="https://roe.dev/blog/nuxt-server-components" target="_blank"} Read Daniel Roe's guide to Nuxt Server Components. :: ### Standalone server components Standalone server components will always be rendered on the server, also known as Islands components. When their props update, this will result in a network request that will update the rendered HTML in-place. Server components are currently experimental and in order to use them, you need to enable the 'component islands' feature in your nuxt.config: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { componentIslands: true, }, }) ``` Now you can register server-only components with the `.server` suffix and use them anywhere in your application automatically. ```bash [Directory Structure] -| components/ ---| HighlightedMarkdown.server.vue ``` ```vue [pages/example.vue] <template> <div> <!-- this will automatically be rendered on the server, meaning your markdown parsing + highlighting libraries are not included in your client bundle. --> <HighlightedMarkdown markdown="# Headline" /> </div> </template> ``` Server-only components use [`<NuxtIsland>`](https://nuxt.com/docs/3.x/api/components/nuxt-island) under the hood, meaning that `lazy` prop and `#fallback` slot are both passed down to it. ::warning Server components (and islands) must have a single root element. (HTML comments are considered elements as well.) :: ::warning Props are passed to server components via URL query parameters, and are therefore limited by the possible length of a URL, so be careful not to pass enormous amounts of data to server components via props. :: ::note Server component props come from the request (URL query or body), so treat them as untrusted input. Nuxt rejects the props most likely to leak through unintentionally: a top-level `as` that the island does not declare (an undeclared prop falls through as an attribute onto the island's root), and, with `vue.runtimeCompiler` enabled, a `template` anywhere in the props. Beyond that, avoid feeding props you have not validated into dynamic component resolution (`<component :is>`{lang="vue"}, `h()`, `resolveDynamicComponent()`, or a polymorphic `as` / `asChild` prop), since a string can resolve to any registered component or HTML element. Props a component does not declare fall through as attributes onto its single root element, so an island whose root is a polymorphic component (e.g. from `reka-ui` / `@nuxt/ui`) can receive attributes you did not bind. Set `defineOptions({ inheritAttrs: false })`{lang="ts"} on such islands, or declare the props you accept. To switch components based on caller input, map a discriminator through an allowlist of imported components rather than passing the raw prop: ```vue <script setup lang="ts"> import type { Component } from 'vue' import CardA from './CardA.vue' import CardB from './CardB.vue' const props = defineProps<{ variant: string }>() const allowed: Record<string, Component> = { a: CardA, b: CardB } const component = allowed[props.variant] ?? CardA </script> <template> <component :is="component" /> </template> ``` :: ::warning Be careful when nesting islands within other islands as each island adds some extra overhead. :: ::warning Most features for server-only components and island components, such as slots and client components, are only available for single file components. :: #### Client components within server components ::note This feature needs `experimental.componentIslands.selectiveClient` within your configuration to be true. :: You can partially hydrate a component by setting a `nuxt-client` attribute on the component you wish to be loaded client-side. ```vue [components/ServerWithClient.vue] <template> <div> <HighlightedMarkdown markdown="# Headline" /> <!-- Counter will be loaded and hydrated client-side --> <Counter nuxt-client :count="5" /> </div> </template> ``` ::note This only works within a server component. Slots for client components are working only with `experimental.componentIsland.selectiveClient` set to `'deep'` and since they are rendered server-side, they are not interactive once client-side. :: #### Server Component Context When rendering a server-only or island component, `<NuxtIsland>` makes a fetch request which comes back with a `NuxtIslandResponse`. (This is an internal request if rendered on the server, or a request that you can see in the network tab if it's rendering on client-side navigation.) This means: - A new Vue app will be created server-side to create the `NuxtIslandResponse`. - A new 'island context' will be created while rendering the component. - You can't access the 'island context' from the rest of your app and you can't access the context of the rest of your app from the island component. In other words, the server component or island is *isolated* from the rest of your app. - Your plugins will run again when rendering the island, unless they have `env: { islands: false }` set (which you can do in an object-syntax plugin). ::important Route middleware does not run when rendering island components. Middleware is a routing concept that applies to pages, not components, and is not designed to control component rendering. :: ::important `useRoute()` and other `vue-router` composables do not track the current page route inside a server (island) component. The island is rendered in its own isolated Vue app keyed only on its props (and any explicit context), which is what keeps islands cacheable independently of the page they are rendered on. Inside an island, `useRoute()` will reflect the island's own request, not the page the user is on. If an island needs information about the current route, pass it in explicitly — either as props from the parent component, or via the `context` prop on `<NuxtIsland>` (read inside the island from `nuxtApp.ssrContext.islandContext`). :: Within an island component, you can access its island context through `nuxtApp.ssrContext.islandContext`. Note that while island components are still marked as experimental, the format of this context may change. ::note Slots can be interactive and are wrapped within a `<div>` with `display: contents;` :: ### Paired with a Client component In this case, the `.server` + `.client` components are two 'halves' of a component and can be used in advanced use cases for separate implementations of a component on server and client side. ```bash [Directory Structure] -| components/ ---| Comments.client.vue ---| Comments.server.vue ``` ```vue [pages/example.vue] <template> <div> <!-- this component will render Comments.server on the server then Comments.client once mounted in the browser --> <Comments /> </div> </template> ``` ## Built-In Nuxt Components There are a number of components that Nuxt provides, including `<ClientOnly>` and `<DevOnly>`. You can read more about them in the API documentation. ::read-more{to="https://nuxt.com/docs/3.x/api"} :: ## Library Authors Making Vue component libraries with automatic tree-shaking and component registration is super easy. ✨ You can use the [`addComponentsDir`](https://nuxt.com/docs/3.x/api/kit/components#addcomponentsdir) method provided from the `@nuxt/kit` to register your components directory in your Nuxt module. Imagine a directory structure like this: ```bash [Directory Structure] -| node_modules/ ---| awesome-ui/ -----| components/ -------| Alert.vue -------| Button.vue -----| nuxt.ts -| pages/ ---| index.vue -| nuxt.config.ts ``` Then in `awesome-ui/nuxt.ts` you can use the `addComponentsDir` hook: ```ts twoslash import { addComponentsDir, createResolver, defineNuxtModule } from '@nuxt/kit' export default defineNuxtModule({ setup () { const resolver = createResolver(import.meta.url) // Add ./components dir to the list addComponentsDir({ path: resolver.resolve('./components'), prefix: 'awesome', }) }, }) ``` That's it! Now in your project, you can import your UI library as a Nuxt module in your `nuxt.config` file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ modules: ['awesome-ui/nuxt'], }) ``` ... and directly use the module components (prefixed with `awesome-`) in our `pages/index.vue`: ```vue <template> <div> My <AwesomeButton>UI button</AwesomeButton>! <awesome-alert>Here's an alert!</awesome-alert> </div> </template> ``` It will automatically import the components only if used and also support HMR when updating your components in `node_modules/awesome-ui/components/`. ::link-example{to="https://nuxt.com/docs/examples/features/auto-imports"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "composables" description: "Use the composables/ directory to auto-import your Vue composables into your application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/composables" --- # composables > Use the composables/ directory to auto-import your Vue composables into your application. ## Usage **Method 1:** Using named export ```ts [composables/useFoo.ts] export const useFoo = () => { return useState('foo', () => 'bar') } ``` **Method 2:** Using default export ```ts [composables/use-foo.ts or composables/useFoo.ts] // It will be available as useFoo() (camelCase of file name without extension) export default function () { return useState('foo', () => 'bar') } ``` **Usage:** You can now use auto imported composable in `.js`, `.ts` and `.vue` files ```vue [app.vue] <script setup lang="ts"> const foo = useFoo() </script> <template> <div> {{ foo }} </div> </template> ``` ::note The `composables/` directory in Nuxt does not provide any additional reactivity capabilities to your code. Instead, any reactivity within composables is achieved using Vue's Composition API mechanisms, such as ref and reactive. Note that reactive code is also not limited to the boundaries of the `composables/` directory. You are free to employ reactivity features wherever they're needed in your application. :: ::read-more{to="https://nuxt.com/docs/3.x/guide/concepts/auto-imports"} :: ::link-example{to="https://nuxt.com/docs/examples/features/auto-imports"} :: ## Types Under the hood, Nuxt auto generates the file `.nuxt/imports.d.ts` to declare the types. Be aware that you have to run [`nuxt prepare`](https://nuxt.com/docs/3.x/api/commands/prepare), [`nuxt dev`](https://nuxt.com/docs/3.x/api/commands/dev) or [`nuxt build`](https://nuxt.com/docs/3.x/api/commands/build) in order to let Nuxt generate the types. ::note If you create a composable without having the dev server running, TypeScript will throw an error, such as `Cannot find name 'useBar'.` :: ## Examples ### Nested Composables You can use a composable within another composable using auto imports: ```ts [composables/test.ts] export const useFoo = () => { const nuxtApp = useNuxtApp() const bar = useBar() } ``` ### Access plugin injections You can access [plugin injections](https://nuxt.com/docs/3.x/directory-structure/plugins#providing-helpers) from composables: ```ts [composables/test.ts] export const useHello = () => { const nuxtApp = useNuxtApp() return nuxtApp.$hello } ``` ## How Files Are Scanned Nuxt only scans files at the top level of the [`composables/` directory](https://nuxt.com/docs/3.x/directory-structure/composables), e.g.: ```bash [Directory Structure] -| composables/ ---| index.ts // scanned ---| useFoo.ts // scanned ---| nested/ -----| utils.ts // not scanned ``` Only `composables/index.ts` and `composables/useFoo.ts` would be searched for imports. To get auto imports working for nested modules, you could either re-export them (recommended) or configure the scanner to include nested directories: **Example:** Re-export the composables you need from the `composables/index.ts` file: ```ts [composables/index.ts] // Enables auto import for this export export { utils } from './nested/utils.ts' ``` **Example:** Scan nested directories inside the `composables/` folder: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ imports: { dirs: [ // Scan top-level composables '~/composables', // ... or scan composables nested one level deep with a specific name and file extension '~/composables/*/index.{ts,js,mjs,mts}', // ... or scan all composables within given directory '~/composables/**', ], }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "content" description: "Use the content/ directory to create a file-based CMS for your application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/content" --- # content > Use the content/ directory to create a file-based CMS for your application. [Nuxt Content](https://content.nuxt.com) reads the `content/` directory in your project and parses `.md`, `.yml`, `.csv` and `.json` files to create a file-based CMS for your application. - Render your content with built-in components. - Query your content with a MongoDB-like API. - Use your Vue components in Markdown files with the MDC syntax. - Automatically generate your navigation. ::read-more{to="https://content.nuxt.com" target="_blank"} Learn more in **Nuxt Content** documentation. :: ## Enable Nuxt Content Install the `@nuxt/content` module in your project as well as adding it to your `nuxt.config.ts` with one command: ```bash [Terminal] npx nuxt module add content ``` ## Create Content Place your markdown files inside the `content/` directory: ```md [content/index.md] # Hello Content ``` The module automatically loads and parses them. ## Render Content To render content pages, add a [catch-all route](https://nuxt.com/docs/3.x/directory-structure/pages/#catch-all-route) using the [`<ContentRenderer>`](https://content.nuxt.com/docs/components/content-renderer) component: ```vue [pages/[...slug\\].vue] <script lang="ts" setup> const route = useRoute() const { data: page } = await useAsyncData(route.path, () => { return queryCollection('content').path(route.path).first() }) </script> <template> <div> <header><!-- ... --></header> <ContentRenderer v-if="page" :value="page" /> <footer><!-- ... --></footer> </div> </template> ``` ## Documentation ::tip{icon="i-lucide-book"} Head over to <https://content.nuxt.com> to learn more about the Content module features, such as how to build queries and use Vue components in your Markdown files with the MDC syntax. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: ".env" description: "A .env file specifies your build/dev-time environment variables." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/env" --- # .env > A .env file specifies your build/dev-time environment variables. ::important This file should be added to your [`.gitignore`](https://nuxt.com/docs/3.x/directory-structure/gitignore) file to avoid pushing secrets to your repository. :: ## Dev, Build and Generate Time Nuxt CLI has built-in [dotenv](https://github.com/motdotla/dotenv) support in development mode and when running [`nuxt build`](https://nuxt.com/docs/3.x/api/commands/build) and [`nuxt generate`](https://nuxt.com/docs/3.x/api/commands/generate). In addition to any process environment variables, if you have a `.env` file in your project root directory, it will be automatically loaded **at dev, build and generate time**. Any environment variables set there will be accessible within your `nuxt.config` file and modules. ```ini [.env] MY_ENV_VARIABLE=hello ``` ::note Note that removing a variable from `.env` or removing the `.env` file entirely will not unset values that have already been set. :: ## Custom File If you want to use a different file - for example, to use `.env.local` or `.env.production` - you can do so by passing the `--dotenv` flag when using the Nuxt CLI. ```bash [Terminal] npx nuxt dev --dotenv .env.local ``` When updating `.env` in development mode, the Nuxt instance is automatically restarted to apply new values to the `process.env`. ::important In your application code, you should use [Runtime Config](https://nuxt.com/docs/3.x/guide/going-further/runtime-config) instead of plain env variables. :: ## Production **After your server is built**, you are responsible for setting environment variables when you run the server. Your `.env` files will not be read at this point. How you do this is different for every environment. This design decision was made to ensure compatibility across various deployment environments, some of which may not have a traditional file system available, such as serverless platforms or edge networks like Cloudflare Workers. Since `.env` files are not used in production, you must explicitly set environment variables using the tools and methods provided by your hosting environment. Here are some common approaches: - You can pass the environment variables as arguments using the terminal: `$ NODE_ENV=production DATABASE_HOST=mydatabaseconnectionstring node .output/server/index.mjs` - You can set environment variables in shell configuration files like `.bashrc` or `.profile`. - Many cloud service providers, such as Vercel, Netlify, and AWS, provide interfaces for setting environment variables via their dashboards, CLI tools or configuration files. ::important `runtimeConfig` [won't pick up environment variables that don't start with `NUXT_` in production] (/docs/guide/going-further/runtime-config#environment-variables). :: ## Production Preview For local production preview purpose, we recommend using [`nuxt preview`](https://nuxt.com/docs/3.x/api/commands/preview) since using this command, the `.env` file will be loaded into `process.env` for convenience. Note that this command requires dependencies to be installed in the package directory. Or you could pass the environment variables as arguments using the terminal. For example, on Linux or macOS: ```bash [Terminal] NODE_ENV=production DATABASE_HOST=mydatabaseconnectionstring node .output/server/index.mjs ``` Note that for a purely static site, it is not possible to set runtime configuration config after your project is prerendered. ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/runtime-config"} :: ::note If you want to use environment variables set at build time but do not care about updating these down the line (or only need to update them reactively *within* your app) then `appConfig` may be a better choice. You can define `appConfig` both within your `nuxt.config` (using environment variables) and also within an `~/app.config.ts` file in your project. :read-more{to="https://nuxt.com/docs/3.x/directory-structure/app-config"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "error.vue" description: "The error.vue file is the error page in your Nuxt application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/error" --- # error.vue > The error.vue file is the error page in your Nuxt application. During the lifespan of your application, some errors may appear unexpectedly at runtime. In such case, we can use the `error.vue` file to override the default error files and display the error nicely. ```vue [error.vue] <script setup lang="ts"> import type { NuxtError } from '#app' const props = defineProps<{ error: NuxtError }>() </script> <template> <div> <h1>{{ error.status }}</h1> <NuxtLink to="/">Go back home</NuxtLink> </div> </template> ``` ::note Although it is called an 'error page' it's not a route and shouldn't be placed in your `~/pages` directory. For the same reason, you shouldn't use `definePageMeta` within this page. That being said, you can still use layouts in the error file, by utilizing the [`NuxtLayout`](https://nuxt.com/docs/3.x/api/components/nuxt-layout) component and specifying the name of the layout. :: The error page has a single prop - `error` which contains an error for you to handle. The `error` object provides the following fields: ```ts interface NuxtError { status: number fatal: boolean unhandled: boolean statusText?: string data?: unknown cause?: unknown // legacy/deprecated equivalent of `status` statusCode: number // legacy/deprecated equivalent of `statusText` statusMessage?: string } ``` If you have an error with custom fields they will be lost; you should assign them to `data` instead: ```ts throw createError({ status: 404, statusText: 'Page Not Found', data: { myCustomField: true, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: ".gitignore" description: "A .gitignore file specifies intentionally untracked files that git should ignore." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/gitignore" --- # .gitignore > A .gitignore file specifies intentionally untracked files that git should ignore. A `.gitignore` file specifies intentionally untracked files that git should ignore. ::read-more ```yaml [props] icon: i-simple-icons-git title: the git documentation to: https://git-scm.com/docs/gitignore target: _blank ``` :: We recommend having a `.gitignore` file that has **at least** the following entries present: ```bash [.gitignore] # Nuxt dev/build outputs .output .data .nuxt .nitro .cache dist # Node dependencies node_modules # Logs logs *.log # Misc .DS_Store # Local env files .env .env.* !.env.example ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "layers" description: "Use the layers/ directory to organize and auto-register local layers within your application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/layers" --- # layers > Use the layers/ directory to organize and auto-register local layers within your application. The `layers/` directory allows you to organize and share reusable code, components, composables, and configurations across your Nuxt application. Any layers within your project in the `layers/` directory will be automatically registered. ::note The `layers/` directory auto-registration is available in Nuxt v3.12.0+. :: ::tip{icon="i-lucide-lightbulb"} Layers are ideal for organizing large codebases with **Domain-Driven Design (DDD)**, creating reusable **UI libraries** or **themes**, sharing **configuration presets** across projects, and separating concerns like **admin panels** or **feature modules**. :: ## Structure Each subdirectory within `layers/` is treated as a separate layer. A layer can contain the same structure as a standard Nuxt application. ::important Every layer **must have** a `nuxt.config.ts` file to be recognized as a valid layer, even if it's empty. :: ```bash [Directory structure] -| layers/ ---| base/ -----| nuxt.config.ts -----| app/ -------| components/ ---------| BaseButton.vue -------| composables/ ---------| useBase.ts -----| server/ -------| api/ ---------| hello.ts ---| admin/ -----| nuxt.config.ts -----| app/ -------| pages/ ---------| admin.vue -------| layouts/ ---------| admin.vue ``` ## Automatic Aliases Named layer aliases to the `srcDir` of each layer are automatically created. You can access a layer using the `#layers/[name]` alias: ```ts // Access the base layer import something from '#layers/base/path/to/file' // Access the admin layer import { useAdmin } from '#layers/admin/composables/useAdmin' ``` ::note Named layer aliases were introduced in Nuxt v3.16.0. :: ## Layer Content Each layer can include: - [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) - Layer-specific configuration that will be merged with the main config - [`app.config.ts`](https://nuxt.com/docs/3.x/directory-structure/app-config) - Reactive application configuration - [`components/`](https://nuxt.com/docs/3.x/directory-structure/components) - Vue components (auto-imported) - [`composables/`](https://nuxt.com/docs/3.x/directory-structure/composables) - Vue composables (auto-imported) - [`utils/`](https://nuxt.com/docs/3.x/directory-structure/utils) - Utility functions (auto-imported) - [`pages/`](https://nuxt.com/docs/3.x/directory-structure/pages) - Application pages - [`layouts/`](https://nuxt.com/docs/3.x/directory-structure/layouts) - Application layouts - [`middleware/`](https://nuxt.com/docs/3.x/directory-structure/middleware) - Route middleware - [`plugins/`](https://nuxt.com/docs/3.x/directory-structure/plugins) - Nuxt plugins - [`server/`](https://nuxt.com/docs/3.x/directory-structure/server) - Server routes, middleware, and utilities - [`shared/`](https://nuxt.com/docs/3.x/directory-structure/shared) - Shared code between app and server ## Priority Order When multiple layers define the same resource (component, composable, page, etc.), the layer with **higher priority wins**. Layers are sorted alphabetically, with later letters having higher priority (Z > A). To control the order, prefix directories with numbers: `1.base/`, `2.features/`, `3.admin/`. ::read-more{to="https://nuxt.com/docs/3.x/getting-started/layers#layer-priority"} :: ::video-accordion{title="Watch a video from Learn Vue about Nuxt Layers" videoId="lnFCM7c9f7I"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "layouts" description: "Nuxt provides a layouts framework to extract common UI patterns into reusable layouts." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/layouts" --- # layouts > Nuxt provides a layouts framework to extract common UI patterns into reusable layouts. ::tip{icon="i-lucide-rocket"} For best performance, components placed in this directory will be automatically loaded via asynchronous import when used. :: ## Enable Layouts Layouts are enabled by adding [`<NuxtLayout>`](https://nuxt.com/docs/3.x/api/components/nuxt-layout) to your [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app): ```vue [app.vue] <template> <NuxtLayout> <NuxtPage /> </NuxtLayout> </template> ``` To use a layout: - Set a `layout` property in your page with [definePageMeta](https://nuxt.com/docs/3.x/api/utils/define-page-meta). - Set the `name` prop of `<NuxtLayout>`. - Set the `appLayout` property in route rules. ::note The layout name is normalized to kebab-case, so `someLayout` becomes `some-layout`. :: ::note If no layout is specified, `layouts/default.vue` will be used. :: ::important If you only have a single layout in your application, we recommend using [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app) instead. :: ::important Unlike other components, your layouts must have a single root element to allow Nuxt to apply transitions between layout changes - and this root element cannot be a `<slot />`. :: ## Default Layout Add a `~/layouts/default.vue`: ```vue [layouts/default.vue] <template> <div> <p>Some default layout content shared across all pages</p> <slot /> </div> </template> ``` In a layout file, the content of the page will be displayed in the `<slot />` component. ## Named Layout ```bash [Directory Structure] -| layouts/ ---| default.vue ---| custom.vue ``` Then you can use the `custom` layout in your page: ```vue twoslash [pages/about.vue] <script setup lang="ts"> declare module 'nuxt/app' { interface NuxtLayouts { 'custom': unknown } } // ---cut--- definePageMeta({ layout: 'custom', }) </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/pages#page-metadata"} Learn more about `definePageMeta`. :: You can directly override the default layout for all pages using the `name` property of [`<NuxtLayout>`](https://nuxt.com/docs/3.x/api/components/nuxt-layout): ```vue [app.vue] <script setup lang="ts"> // You might choose this based on an API call or logged-in status const layout = 'custom' </script> <template> <NuxtLayout :name="layout"> <NuxtPage /> </NuxtLayout> </template> ``` If you have a layout in nested directories, the layout's name will be based on its own path directory and filename, with duplicate segments being removed. | File | Layout Name | | --------------------------------- | ----------------- | | `~/layouts/desktop/default.vue` | `desktop-default` | | `~/layouts/desktop-base/base.vue` | `desktop-base` | | `~/layouts/desktop/index.vue` | `desktop` | For clarity, we recommend that the layout's filename matches its name: | File | Layout Name | | ---------------------------------------- | ----------------- | | `~/layouts/desktop/DesktopDefault.vue` | `desktop-default` | | `~/layouts/desktop-base/DesktopBase.vue` | `desktop-base` | | `~/layouts/desktop/Desktop.vue` | `desktop` | ::link-example{to="https://nuxt.com/docs/examples/features/layouts"} :: ## Changing the Layout Dynamically You can also use the [`setPageLayout`](https://nuxt.com/docs/3.x/api/utils/set-page-layout) helper to change the layout dynamically: ```vue twoslash [app/pages/index.vue] <script setup lang="ts"> declare module 'nuxt/app' { interface NuxtLayouts { 'custom': unknown } } // ---cut--- function enableCustomLayout () { setPageLayout('custom') } definePageMeta({ layout: false, }) </script> <template> <div> <button @click="enableCustomLayout"> Update layout </button> </div> </template> ``` You can also set layouts for specific routes using the `appLayout` property in route rules: ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Set layout for specific route '/admin': { appLayout: 'admin' }, // Set layout for multiple routes '/dashboard/**': { appLayout: 'dashboard' }, // Disable layout for a route '/landing': { appLayout: false }, }, }) ``` ::tip This is useful when you want to manage layouts centrally in your configuration rather than in each page file, or when you need to apply layouts to routes that don't have corresponding page components (such as catchall pages which might match many paths). :: ::link-example{to="https://nuxt.com/docs/examples/features/layouts"} :: ## Overriding a Layout on a Per-page Basis If you are using pages, you can take full control by setting `layout: false` and then using the `<NuxtLayout>` component within the page. ::code-group ```vue [pages/index.vue] <script setup lang="ts"> definePageMeta({ layout: false, }) </script> <template> <div> <NuxtLayout name="custom"> <template #header> Some header template content. </template> The rest of the page </NuxtLayout> </div> </template> ``` ```vue [layouts/custom.vue] <template> <div> <header> <slot name="header"> Default header content </slot> </header> <main> <slot /> </main> </div> </template> ``` :: ::important If you use `<NuxtLayout>` within your pages, make sure it is not the root element (or [disable layout/page transitions](https://nuxt.com/docs/3.x/getting-started/transitions#disable-transitions)). :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "middleware" description: "Nuxt provides middleware to run code before navigating to a particular route." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/middleware" --- # middleware > Nuxt provides middleware to run code before navigating to a particular route. Nuxt provides a customizable **route middleware** framework you can use throughout your application, ideal for extracting code that you want to run before navigating to a particular route. There are three kinds of route middleware: 1. Anonymous (or inline) route middleware are defined directly within the page. 2. Named route middleware, placed in the `middleware/` and automatically loaded via asynchronous import when used on a page. 3. Global route middleware, placed in the `middleware/` with a `.global` suffix and is run on every route change. The first two kinds of route middleware can be defined in [`definePageMeta`](https://nuxt.com/docs/3.x/api/utils/define-page-meta). ::note Name of middleware are normalized to kebab-case: `myMiddleware` becomes `my-middleware`. :: ::note Route middleware run within the Vue part of your Nuxt app. Despite the similar name, they are completely different from [server middleware](https://nuxt.com/docs/3.x/directory-structure/server#server-middleware), which are run in the Nitro server part of your app. :: ::video-accordion{title="Watch a video from Vue School on all 3 kinds of middleware" videoId="761471577" platform="vimeo"} :: ## Usage Route middleware are navigation guards that receive the current route and the next route as arguments. ```ts twoslash [middleware/my-middleware.ts] export default defineNuxtRouteMiddleware((to, from) => { if (to.params.id === '1') { return abortNavigation() } // In a real app you would probably not redirect every route to `/` // however it is important to check `to.path` before redirecting or you // might get an infinite redirect loop if (to.path !== '/') { return navigateTo('/') } }) ``` Nuxt provides two globally available helpers that can be returned directly from the middleware. 1. [`navigateTo`](https://nuxt.com/docs/3.x/api/utils/navigate-to) - Redirects to the given route 2. [`abortNavigation`](https://nuxt.com/docs/3.x/api/utils/abort-navigation) - Aborts the navigation, with an optional error message. Unlike [navigation guards](https://router.vuejs.org/guide/advanced/navigation-guards#Global-Before-Guards) from `vue-router`, a third `next()` argument is not passed, and **redirect or route cancellation is handled by returning a value from the middleware**. Possible return values are: - nothing (a simple `return` or no return at all) - does not block navigation and will move to the next middleware function, if any, or complete the route navigation - `return navigateTo('/')` - redirects to the given path and will set the redirect code to [`302` Found](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/302) if the redirect happens on the server side - `return navigateTo('/', { redirectCode: 301 })` - redirects to the given path and will set the redirect code to [`301` Moved Permanently](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/301) if the redirect happens on the server side - `return abortNavigation()` - stops the current navigation - `return abortNavigation(error)` - rejects the current navigation with an error ::read-more{to="https://nuxt.com/docs/3.x/api/utils/navigate-to"} :: ::read-more{to="https://nuxt.com/docs/3.x/api/utils/abort-navigation"} :: ::important We recommend using the helper functions above for performing redirects or stopping navigation. Other possible return values described in [the vue-router docs](https://router.vuejs.org/guide/advanced/navigation-guards#Global-Before-Guards) may work but there may be breaking changes in future. :: ## Middleware Order Middleware runs in the following order: 1. Global Middleware 2. Page defined middleware order (if there are multiple middleware declared with the array syntax) For example, assuming you have the following middleware and component: ```bash [middleware/ directory] -| middleware/ ---| analytics.global.ts ---| setup.global.ts ---| auth.ts ``` ```vue twoslash [pages/profile.vue] <script setup lang="ts"> definePageMeta({ middleware: [ function (to, from) { // Custom inline middleware }, 'auth', ], }) </script> ``` You can expect the middleware to be run in the following order: 1. `analytics.global.ts` 2. `setup.global.ts` 3. Custom inline middleware 4. `auth.ts` ### Ordering Global Middleware By default, global middleware is executed alphabetically based on the filename. However, there may be times you want to define a specific order. For example, in the last scenario, `setup.global.ts` may need to run before `analytics.global.ts`. In that case, we recommend prefixing global middleware with 'alphabetical' numbering. ```bash [Directory structure] -| middleware/ ---| 01.setup.global.ts ---| 02.analytics.global.ts ---| auth.ts ``` ::note In case you're new to 'alphabetical' numbering, remember that filenames are sorted as strings, not as numeric values. For example, `10.new.global.ts` would come before `2.new.global.ts`. This is why the example prefixes single digit numbers with `0`. :: ## When Middleware Runs If your site is server-rendered or generated, middleware for the initial page will be executed both when the page is rendered and then again on the client. This might be needed if your middleware needs a browser environment, such as if you have a generated site, aggressively cache responses, or want to read a value from local storage. However, if you want to avoid this behaviour you can do so: ```ts twoslash [middleware/example.ts] export default defineNuxtRouteMiddleware((to) => { // skip middleware on server if (import.meta.server) { return } // skip middleware on client side entirely if (import.meta.client) { return } // or only skip middleware on initial client load const nuxtApp = useNuxtApp() if (import.meta.client && nuxtApp.isHydrating && nuxtApp.payload.serverRendered) { return } }) ``` This is true even if you throw an error in your middleware on the server, and an error page is rendered. The middleware will still run again in the browser. ::note Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](https://nuxt.com/docs/3.x/getting-started/error-handling#useerror) in middleware to check if an error is being handled. :: ## Accessing Route in Middleware Always use the `to` and `from` parameters in your middleware to access the next and previous routes. Avoid using the [`useRoute()`](https://nuxt.com/docs/3.x/api/composables/use-route) composable in this context altogether. There is **no concept of a "current route" in middleware**, as middleware can abort a navigation or redirect to a different route. The `useRoute()` composable will always be inaccurate in this context. ::warning Sometimes, you might call a composable that uses `useRoute()` internally, which can trigger this warning even if there is no direct call in your middleware. This leads to the **same issue as above**, so you should structure your functions to accept the route as an argument instead when they are used in middleware. :: ::code-group ```ts twoslash [middleware/access-route.ts] // @errors: 2304 export default defineNuxtRouteMiddleware((to) => { // passing the route to the function to avoid calling `useRoute()` in middleware doSomethingWithRoute(to) // ❌ this will output a warning and is NOT recommended callsRouteInternally() }) ``` ```ts twoslash [utils/handle-route.ts] // providing the route as an argument so that it can be used in middleware correctly export function doSomethingWithRoute (route = useRoute()) { // ... } ``` ```ts twoslash [utils/dont-do-this.ts] // ❌ this function is not suitable for use in middleware export function callsRouteInternally () { const route = useRoute() // ... } ``` :: ## Adding Middleware Dynamically It is possible to add global or named route middleware manually using the [`addRouteMiddleware()`](https://nuxt.com/docs/3.x/api/utils/add-route-middleware) helper function, such as from within a plugin. ```ts twoslash export default defineNuxtPlugin(() => { addRouteMiddleware('global-test', () => { console.log('this global middleware was added in a plugin and will be run on every route change') }, { global: true }) addRouteMiddleware('named-test', () => { console.log('this named middleware was added in a plugin and would override any existing middleware of the same name') }) }) ``` ## Example ```bash [Directory Structure] -| middleware/ ---| auth.ts ``` In your page file, you can reference this route middleware: ```vue twoslash <script setup lang="ts"> definePageMeta({ middleware: ['auth'], // or middleware: 'auth' }) </script> ``` Now, before navigation to that page can complete, the `auth` route middleware will be run. ::link-example{to="https://nuxt.com/docs/examples/routing/middleware"} :: ## Setting Middleware at Build Time Instead of using `definePageMeta` on each page, you can add named route middleware within the `pages:extend` hook. ```ts twoslash [nuxt.config.ts] import type { NuxtPage } from 'nuxt/schema' export default defineNuxtConfig({ hooks: { 'pages:extend' (pages) { function setMiddleware (pages: NuxtPage[]) { for (const page of pages) { if (/* some condition */ Math.random() > 0.5) { page.meta ||= {} // Note that this will override any middleware set in `definePageMeta` in the page page.meta.middleware = ['named'] } if (page.children) { setMiddleware(page.children) } } } setMiddleware(pages) }, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "modules" description: "Use the modules/ directory to automatically register local modules within your application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/modules" --- # modules > Use the modules/ directory to automatically register local modules within your application. It is a good place to place any local modules you develop while building your application. The auto-registered files patterns are: - `modules/*/index.ts` - `modules/*.ts` You don't need to add those local modules to your [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) separately. ::code-group ```ts twoslash [modules/hello/index.ts] // `nuxt/kit` is a helper subpath import you can use when defining local modules // that means you do not need to add `@nuxt/kit` to your project's dependencies import { addComponentsDir, addServerHandler, createResolver, defineNuxtModule } from 'nuxt/kit' export default defineNuxtModule({ meta: { name: 'hello', }, setup () { const resolver = createResolver(import.meta.url) // Add an API route addServerHandler({ route: '/api/hello', handler: resolver.resolve('./runtime/api-route'), }) // Add components addComponentsDir({ path: resolver.resolve('./runtime/app/components'), pathPrefix: true, // Prefix your exports to avoid conflicts with user code or other modules }) }, }) ``` ```ts twoslash [modules/hello/runtime/api-route.ts] export default defineEventHandler(() => { return { hello: 'world' } }) ``` :: When starting Nuxt, the `hello` module will be registered and the `/api/hello` route will be available. ::note Note that all components, pages, composables and other files that would be normally placed in your `app/` directory need to be in `modules/your-module/runtime/app/`. This ensures they can be type-checked properly. :: Modules are executed in the following sequence: - First, the modules defined in [`nuxt.config.ts`](https://nuxt.com/docs/3.x/api/nuxt-config#modules-1) are loaded. - Then, modules found in the `modules/` directory are executed, and they load in alphabetical order. You can change the order of local module by adding a number to the front of each directory name: ```bash [Directory structure] modules/ 1.first-module/ index.ts 2.second-module.ts ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/modules"} :: ::tip{icon="i-lucide-video" to="https://vueschool.io/lessons/creating-your-first-module-from-scratch?friend=nuxt" target="_blank"} Watch Vue School video about Nuxt private modules. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "node_modules" description: "The package manager stores the dependencies of your project in the node_modules/ directory." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/node_modules" --- # node_modules > The package manager stores the dependencies of your project in the node_modules/ directory. The package manager ([`npm`](https://docs.npmjs.com/cli/commands/npm/) or [`yarn`](https://yarnpkg.com) or [`pnpm`](https://pnpm.io/cli/install) or [`bun`](https://bun.com/package-manager) or [`deno`](https://docs.deno.com/runtime/getting_started/installation/)) creates this directory to store the dependencies of your project. ::important This directory should be added to your [`.gitignore`](https://nuxt.com/docs/3.x/directory-structure/gitignore) file to avoid pushing the dependencies to your repository. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: ".nuxt" description: "Nuxt uses the .nuxt/ directory in development to generate your Vue application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/nuxt" --- # .nuxt > Nuxt uses the .nuxt/ directory in development to generate your Vue application. ::important This directory should be added to your [`.gitignore`](https://nuxt.com/docs/3.x/directory-structure/gitignore) file to avoid pushing the dev build output to your repository. :: This directory is interesting if you want to learn more about the files Nuxt generates based on your directory structure. Nuxt also provides a Virtual File System (VFS) for modules to add templates to this directory without writing them to disk. You can explore the generated files by opening the [Nuxt DevTools](https://nuxt.com/modules/devtools) in development mode and navigating to the **Virtual Files** tab. ::warning You should not touch any files inside since the whole directory will be re-created when running [`nuxt dev`](https://nuxt.com/docs/3.x/api/commands/dev). :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt.config.ts" description: "Nuxt can be easily configured with a single nuxt.config file." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/nuxt-config" --- # nuxt.config.ts > Nuxt can be easily configured with a single nuxt.config file. The `nuxt.config` file extension can either be `.js`, `.ts` or `.mjs`. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ // My Nuxt config }) ``` ::tip `defineNuxtConfig` helper is globally available without import. :: You can explicitly import `defineNuxtConfig` from `nuxt/config` if you prefer: ```ts twoslash [nuxt.config.ts] import { defineNuxtConfig } from 'nuxt/config' export default defineNuxtConfig({ // My Nuxt config }) ``` ::read-more{to="https://nuxt.com/docs/3.x/api/configuration/nuxt-config"} Discover all the available options in the **Nuxt configuration** documentation. :: To ensure your configuration is up to date, Nuxt will make a full restart when detecting changes in the main configuration file, the [`.env`](https://nuxt.com/docs/3.x/directory-structure/env), [`.nuxtignore`](https://nuxt.com/docs/3.x/directory-structure/nuxtignore) and [`.nuxtrc`](https://nuxt.com/docs/3.x/directory-structure/nuxtrc) dotfiles. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: ".nuxtignore" description: "The .nuxtignore file lets Nuxt ignore files in your project’s root directory during the build phase." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/nuxtignore" --- # .nuxtignore > The .nuxtignore file lets Nuxt ignore files in your project’s root directory during the build phase. The `.nuxtignore` file tells Nuxt to ignore files in your project’s root directory ([`rootDir`](https://nuxt.com/docs/3.x/api/nuxt-config#rootdir)) during the build phase. It is subject to the same specification as [`.gitignore`](https://nuxt.com/docs/3.x/directory-structure/gitignore) and `.eslintignore` files, in which each line is a glob pattern indicating which files should be ignored. ::tip You can also configure [`ignoreOptions`](https://nuxt.com/docs/3.x/api/nuxt-config#ignoreoptions), [`ignorePrefix`](https://nuxt.com/docs/3.x/api/nuxt-config#ignoreprefix) and [`ignore`](https://nuxt.com/docs/3.x/api/nuxt-config#ignore) in your `nuxt.config` file. :: ## Usage ```bash [.nuxtignore] # ignore layout foo.vue layouts/foo.vue # ignore layout files whose name ends with -ignore.vue layouts/*-ignore.vue # ignore page bar.vue pages/bar.vue # ignore page inside ignore folder pages/ignore/*.vue # ignore route middleware files under foo folder except foo/bar.js middleware/foo/*.js !middleware/foo/bar.js ``` ::read-more ```yaml [props] icon: i-simple-icons-git title: the git documentation to: https://git-scm.com/docs/gitignore target: _blank ``` More details about the spec are in the **gitignore documentation**. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: ".nuxtrc" description: "The .nuxtrc file allows you to define nuxt configurations in a flat syntax." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/nuxtrc" --- # .nuxtrc > The .nuxtrc file allows you to define nuxt configurations in a flat syntax. The `.nuxtrc` file can be used to configure Nuxt with a flat syntax. It is based on [`unjs/rc9`](https://github.com/unjs/rc9). ::tip For more advanced configurations, use [`nuxt.config`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config). :: ## Usage ```bash [.nuxtrc] # Disable SSR ssr=false # Configuration for `@nuxt/devtools` devtools.enabled=true # Add Nuxt modules modules[]=@nuxt/image modules[]=nuxt-security # Module setups (automatically added by Nuxt) setups.@nuxt/test-utils="3.23.0" ``` If present, the properties in the `nuxt.config` file will overwrite the properties in `.nuxtrc` file. ::note Nuxt automatically adds a `setups` section to track module installation and upgrade state. This is used internally for [module lifecycle hooks](https://nuxt.com/docs/3.x/api/kit/modules#using-lifecycle-hooks-for-module-installation-and-upgrade) and should not be modified manually. :: ::read-more{to="https://nuxt.com/docs/3.x/api/configuration/nuxt-config"} Discover all the available options in the **Nuxt configuration** documentation. :: ## Global `.nuxtrc` File You can also create a global `.nuxtrc` file in your home directory to apply configurations globally. - On macOS/Linux, this file is located at: ```md ~/.nuxtrc ``` - On Windows, it is located at: ```md C:\Users\{username}\.nuxtrc ``` This global `.nuxtrc` file allows you to define default settings that apply to all Nuxt projects on your system. However, project-level `.nuxtrc` files will override these global settings, and `nuxt.config` will take precedence over both. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: ".output" description: "Nuxt creates the .output/ directory when building your application for production." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/output" --- # .output > Nuxt creates the .output/ directory when building your application for production. ::important This directory should be added to your [`.gitignore`](https://nuxt.com/docs/3.x/directory-structure/gitignore) file to avoid pushing the build output to your repository. :: Use this directory to deploy your Nuxt application to production. ::read-more{to="https://nuxt.com/docs/3.x/getting-started/deployment"} :: ::warning You should not touch any files inside since the whole directory will be re-created when running [`nuxt build`](https://nuxt.com/docs/3.x/api/commands/build). :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "package.json" description: "The package.json file contains all the dependencies and scripts for your application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/package" --- # package.json > The package.json file contains all the dependencies and scripts for your application. The minimal `package.json` of your Nuxt application should looks like: ```json [package.json] { "name": "nuxt-app", "private": true, "type": "module", "scripts": { "build": "nuxt build", "dev": "nuxt dev", "generate": "nuxt generate", "preview": "nuxt preview", "postinstall": "nuxt prepare" }, "dependencies": { "nuxt": "latest", "vue": "latest", "vue-router": "latest" } } ``` ::read-more{icon="i-simple-icons-npm" to="https://docs.npmjs.com/cli/configuring-npm/package-json/" target="_blank"} Read more about the `package.json` file. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "pages" description: "Nuxt provides file-based routing to create routes within your web application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/pages" --- # pages > Nuxt provides file-based routing to create routes within your web application. ::note To reduce your application's bundle size, this directory is **optional**, meaning that [`vue-router`](https://router.vuejs.org) won't be included if you only use [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app). To force the pages system, set `pages: true` in `nuxt.config` or have a [`router.options.ts`](https://nuxt.com/docs/3.x/guide/recipes/custom-routing#using-approuteroptions). :: ## Usage Pages are Vue components and can have any [valid extension](https://nuxt.com/docs/3.x/api/nuxt-config#extensions) that Nuxt supports (by default `.vue`, `.js`, `.jsx`, `.mjs`, `.ts` or `.tsx`). Nuxt will automatically create a route for every page in your `~/pages/` directory. ::tip Pages are only automatically registered for prerendering if you have not disabled `nitro.prerender.crawlLinks` and you have at least one page in your `nitro.prerender.routes` list. See the [prerendering guide](https://nuxt.com/docs/3.x/getting-started/prerendering). :: ::code-group ```vue [pages/index.vue] <template> <h1>Index page</h1> </template> ``` ```ts twoslash [pages/index.ts] // https://vuejs.org/guide/extras/render-function.html export default defineComponent({ render () { return h('h1', 'Index page') }, }) ``` ```tsx twoslash [pages/index.tsx] // /docs/examples/advanced/jsx // https://vuejs.org/guide/extras/render-function.html#jsx-tsx export default defineComponent({ render () { return <h1>Index page</h1> }, }) ``` :: The `pages/index.vue` file will be mapped to the `/` route of your application. If you are using [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app), make sure to use the [`<NuxtPage/>`](https://nuxt.com/docs/3.x/api/components/nuxt-page) component to display the current page: ```vue [app.vue] <template> <div> <!-- Markup shared across all pages, ex: NavBar --> <NuxtPage /> </div> </template> ``` Pages **must have a single root element** to allow [route transitions](https://nuxt.com/docs/3.x/getting-started/transitions) between pages. HTML comments are considered elements as well. This means that when the route is server-rendered, or statically generated, you will be able to see its contents correctly, but when you navigate towards that route during client-side navigation the transition between routes will fail and you'll see that the route will not be rendered. Here are some examples to illustrate what a page with a single root element looks like: ::code-group ```vue [pages/working.vue] <template> <div> <!-- This page correctly has only one single root element --> Page content </div> </template> ``` ```vue [pages/bad-1.vue] <template> <!-- This page will not render when route changes during client side navigation, because of this comment --> <div>Page content</div> </template> ``` ```vue [pages/bad-2.vue] <template> <div>This page</div> <div>Has more than one root element</div> <div>And will not render when route changes during client side navigation</div> </template> ``` :: ## Dynamic Routes If you place anything within square brackets, it will be turned into a [dynamic route](https://router.vuejs.org/guide/essentials/dynamic-matching) parameter. You can mix and match multiple parameters and even non-dynamic text within a file name or directory. If you want a parameter to be *optional*, you must enclose it in double square brackets - for example, `~/pages/[[slug]]/index.vue` or `~/pages/[[slug]].vue` will match both `/` and `/test`. ```bash [Directory Structure] -| pages/ ---| index.vue ---| users-[group]/ -----| [id].vue ``` Given the example above, you can access group/id within your component via the `$route` object: ```vue [pages/users-[group\\]/[id\\].vue] <template> <p>{{ $route.params.group }} - {{ $route.params.id }}</p> </template> ``` Navigating to `/users-admins/123` would render: ```html <p>admins - 123</p> ``` If you want to access the route using Composition API, there is a global [`useRoute`](https://nuxt.com/docs/3.x/api/composables/use-route) function that will allow you to access the route just like `this.$route` in the Options API. ```vue twoslash <script setup lang="ts"> const route = useRoute() if (route.params.group === 'admins' && !route.params.id) { console.log('Warning! Make sure user is authenticated!') } </script> ``` ::note Named parent routes will take priority over nested dynamic routes. For the `/foo/hello` route, `~/pages/foo.vue` will take priority over `~/pages/foo/[slug].vue`. Use `~/pages/foo/index.vue` and `~/pages/foo/[slug].vue` to match `/foo` and `/foo/hello` with different pages,. :: ::video-accordion{title="Watch a video from Vue School on dynamic routes" videoId="754465699" platform="vimeo"} :: ## Catch-all Route If you need a catch-all route, you create it by using a file named like `[...slug].vue`. This will match *all* routes under that path. ```vue [pages/[...slug\\].vue] <template> <p>{{ $route.params.slug }}</p> </template> ``` Navigating to `/hello/world` would render: ```html <p>["hello", "world"]</p> ``` ## Nested Routes It is possible to display [nested routes](https://router.vuejs.org/guide/essentials/nested-routes) with `<NuxtPage>`. Example: ```bash [Directory Structure] -| pages/ ---| parent/ -----| child.vue ---| parent.vue ``` This file tree will generate these routes: ```js [ { path: '/parent', component: '~/pages/parent.vue', name: 'parent', children: [ { path: 'child', component: '~/pages/parent/child.vue', name: 'parent-child', }, ], }, ] ``` To display the `child.vue` component, you have to insert the `<NuxtPage>` component inside `pages/parent.vue`: ```vue {}[pages/parent.vue] <template> <div> <h1>I am the parent view</h1> <NuxtPage :foobar="123" /> </div> </template> ``` ```vue {}[pages/parent/child.vue] <script setup lang="ts"> const props = defineProps({ foobar: String, }) console.log(props.foobar) </script> ``` ### Child Route Keys If you want more control over when the `<NuxtPage>` component is re-rendered (for example, for transitions), you can either pass a string or function via the `pageKey` prop, or you can define a `key` value via `definePageMeta`: ```vue {}[pages/parent.vue] <template> <div> <h1>I am the parent view</h1> <NuxtPage :page-key="route => route.fullPath" /> </div> </template> ``` Or alternatively: ```vue twoslash {}[pages/parent/child.vue] <script setup lang="ts"> definePageMeta({ key: route => route.fullPath, }) </script> ``` ::link-example{to="https://nuxt.com/docs/examples/routing/pages"} :: ## Route Groups In some cases, you may want to group a set of routes together in a way which doesn't affect file-based routing. For this purpose, you can put files in a folder which is wrapped in parentheses - `(` and `)`. For example: ```bash [Directory structure] -| pages/ ---| index.vue ---| (marketing)/ -----| about.vue -----| contact.vue ``` This will produce `/`, `/about` and `/contact` pages in your app. The `marketing` group is ignored for purposes of your URL structure. ### Accessing Route Groups Route groups are automatically available in the route metadata as `route.meta.groups`. This allows you to access the group information in your components for conditional logic, styling, or other purposes. ```vue {}[pages/(marketing)/about.vue] <script setup lang="ts"> const route = useRoute() console.log(route.meta.groups) // Output: ['marketing'] </script> <template> <div> <p v-if="route.meta.groups?.includes('marketing')"> This is a marketing page </p> </div> </template> ``` ## Page Metadata You might want to define metadata for each route in your app. You can do this using the `definePageMeta` macro, which will work both in `<script>` and in `<script setup>`: ```vue twoslash <script setup lang="ts"> definePageMeta({ title: 'My home page', }) </script> ``` This data can then be accessed throughout the rest of your app from the `route.meta` object. ```vue twoslash <script setup lang="ts"> const route = useRoute() console.log(route.meta.title) // My home page </script> ``` If you are using nested routes, the page metadata from all these routes will be merged into a single object. For more on route meta, see the [vue-router docs](https://router.vuejs.org/guide/advanced/meta). Much like `defineEmits` or `defineProps` (see [Vue docs](https://vuejs.org/api/sfc-script-setup#defineprops-defineemits)), `definePageMeta` is a **compiler macro**. It will be compiled away so you cannot reference it within your component. Instead, the metadata passed to it will be hoisted out of the component. Therefore, the page meta object cannot reference the component. However, it can reference imported bindings, as well as locally defined **pure functions**. ::warning Make sure not to reference any reactive data or functions that cause side effects. This can lead to unexpected behavior. :: ```vue <script setup lang="ts"> import { someData } from '~/utils/example' function validateIdParam (route) { return route.params.id && !Number.isNaN(Number(route.params.id)) } const title = ref('') definePageMeta({ validate: validateIdParam, someData, title, // do not do this, the ref will be hoisted out of the component }) </script> ``` ### Special Metadata Of course, you are welcome to define metadata for your own use throughout your app. But some metadata defined with `definePageMeta` has a particular purpose: #### `alias` You can define page aliases. They allow you to access the same page from different paths. It can be either a string or an array of strings as defined [in the vue-router documentation](https://router.vuejs.org/guide/essentials/redirect-and-alias#Alias). #### `keepalive` Nuxt will automatically wrap your page in [the Vue `<KeepAlive>` component](https://vuejs.org/guide/built-ins/keep-alive#keepalive) if you set `keepalive: true` in your `definePageMeta`. This might be useful to do, for example, in a parent route that has dynamic child routes, if you want to preserve page state across route changes. When your goal is to preserve state for parent routes use this syntax: `<NuxtPage keepalive />`. You can also set props to be passed to `<KeepAlive>` (see [a full list](https://vuejs.org/api/built-in-components#keepalive)). You can set a default value for this property [in your `nuxt.config`](https://nuxt.com/docs/3.x/api/nuxt-config#keepalive). #### `key` [See above](https://nuxt.com/docs/3.x/directory-structure/pages#child-route-keys). #### `layout` You can define the layout used to render the route. This can be either false (to disable any layout), a string or a ref/computed, if you want to make it reactive in some way. [More about layouts](https://nuxt.com/docs/3.x/directory-structure/layouts). #### `layoutTransition` and `pageTransition` You can define transition properties for the `<transition>` component that wraps your pages and layouts, or pass `false` to disable the `<transition>` wrapper for that route. You can see [a list of options that can be passed](https://vuejs.org/api/built-in-components#transition) or read [more about how transitions work](https://vuejs.org/guide/built-ins/transition#transition). You can set default values for these properties [in your `nuxt.config`](https://nuxt.com/docs/3.x/api/nuxt-config#layouttransition). #### `middleware` You can define middleware to apply before loading this page. It will be merged with all the other middleware used in any matching parent/child routes. It can be a string, a function (an anonymous/inlined middleware function following [the global before guard pattern](https://router.vuejs.org/guide/advanced/navigation-guards#Global-Before-Guards)), or an array of strings/functions. [More about named middleware](https://nuxt.com/docs/3.x/directory-structure/middleware). #### `name` You may define a name for this page's route. #### `path` You may define a path matcher, if you have a more complex pattern than can be expressed with the file name. See [the `vue-router` docs](https://router.vuejs.org/guide/essentials/route-matching-syntax#Custom-regex-in-params) for more information. #### `props` Allows accessing the route `params` as props passed to the page component. See [the `vue-router` docs](https://router.vuejs.org/guide/essentials/passing-props) for more information. ### Typing Custom Metadata If you add custom metadata for your pages, you may wish to do so in a type-safe way. It is possible to augment the type of the object accepted by `definePageMeta`: ```ts [index.d.ts] declare module '#app' { interface PageMeta { pageType?: string } } // It is always important to ensure you import/export something when augmenting a type export {} ``` ## Navigation To navigate between pages of your app, you should use the [`<NuxtLink>`](https://nuxt.com/docs/3.x/api/components/nuxt-link) component. This component is included with Nuxt and therefore you don't have to import it as you do with other components. A simple link to the `index.vue` page in your `pages` folder: ```vue <template> <NuxtLink to="/">Home page</NuxtLink> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/api/components/nuxt-link"} Learn more about `<NuxtLink>` usage. :: ## Programmatic Navigation Nuxt allows programmatic navigation through the `navigateTo()` utility method. Using this utility method, you will be able to programmatically navigate the user in your app. This is great for taking input from the user and navigating them dynamically throughout your application. In this example, we have a simple method called `navigate()` that gets called when the user submits a search form. ::note Make sure to always `await` on `navigateTo` or chain its result by returning from functions. :: ```vue twoslash <script setup lang="ts"> const name = ref('') const type = ref(1) function navigate () { return navigateTo({ path: '/search', query: { name: name.value, type: type.value, }, }) } </script> ``` ## Client-Only Pages You can define a page as [client only](https://nuxt.com/docs/3.x/directory-structure/components#client-components) by giving it a `.client.vue` suffix. None of the content of this page will be rendered on the server. ## Server-Only Pages You can define a page as [server only](https://nuxt.com/docs/3.x/directory-structure/components#server-components) by giving it a `.server.vue` suffix. While you will be able to navigate to the page using client-side navigation, controlled by `vue-router`, it will be rendered with a server component automatically, meaning the code required to render the page will not be in your client-side bundle. ::warning Server-only pages must have a single root element. (HTML comments are considered elements as well.) :: ## Custom Routing As your app gets bigger and more complex, your routing might require more flexibility. For this reason, Nuxt directly exposes the router, routes and router options for customization in different ways. ::read-more{to="https://nuxt.com/docs/3.x/guide/recipes/custom-routing"} :: ## Multiple Pages Directories By default, all your pages should be in one `pages` directory at the root of your project. However, you can use [Nuxt Layers](https://nuxt.com/docs/3.x/getting-started/layers) to create groupings of your app's pages: ```bash [Directory Structure] -| some-app/ ---| nuxt.config.ts ---| pages/ -----| app-page.vue -| nuxt.config.ts ``` ```ts twoslash [some-app/nuxt.config.ts] // some-app/nuxt.config.ts export default defineNuxtConfig({ }) ``` ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ extends: ['./some-app'], }) ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/layers"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "plugins" description: "Nuxt has a plugins system to use Vue plugins and more at the creation of your Vue application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/plugins" --- # plugins > Nuxt has a plugins system to use Vue plugins and more at the creation of your Vue application. Nuxt automatically reads the files in the `plugins/` directory and loads them at the creation of the Vue application. ::note All plugins inside are auto-registered, you don't need to add them to your `nuxt.config` separately. :: ::note You can use `.server` or `.client` suffix in the file name to load a plugin only on the server or client side. :: ## Registered Plugins Only files at the top level of the directory (or index files within any subdirectories) will be auto-registered as plugins. ```bash [Directory structure] -| plugins/ ---| foo.ts // scanned ---| bar/ -----| baz.ts // not scanned -----| foz.vue // not scanned -----| index.ts // currently scanned but deprecated ``` Only `foo.ts` and `bar/index.ts` would be registered. To add plugins in subdirectories, you can use the [`plugins`](https://nuxt.com/docs/3.x/api/nuxt-config#plugins-1) option in `nuxt.config.ts`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ plugins: [ '~/plugins/bar/baz', '~/plugins/bar/foz', ], }) ``` ## Creating Plugins The only argument passed to a plugin is [`nuxtApp`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app). ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin((nuxtApp) => { // Doing something with nuxtApp }) ``` ### Object Syntax Plugins It is also possible to define a plugin using an object syntax, for more advanced use cases. For example: ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin({ name: 'my-plugin', enforce: 'pre', // or 'post' async setup (nuxtApp) { // this is the equivalent of a normal functional plugin }, hooks: { // You can directly register Nuxt app runtime hooks here 'app:created' () { const nuxtApp = useNuxtApp() // do something in the hook }, }, env: { // Set this value to `false` if you don't want the plugin to run when rendering server-only or island components. islands: true, }, }) ``` ::video-accordion{title="Watch a video from Alexander Lichter about the Object Syntax for Nuxt plugins" videoId="2aXZyXB1QGQ"} :: ::note If you are using the object-syntax, the properties are statically analyzed to produce a more optimized build. So you should not define them at runtime. For example, setting `enforce: import.meta.server ? 'pre' : 'post'` would defeat any future optimization Nuxt is able to do for your plugins. Nuxt does statically pre-load any hook listeners when using object-syntax, allowing you to define hooks without needing to worry about order of plugin registration. :: ## Registration Order You can control the order in which plugins are registered by prefixing with 'alphabetical' numbering to the file names. ```bash [Directory structure] plugins/ | - 01.myPlugin.ts | - 02.myOtherPlugin.ts ``` In this example, `02.myOtherPlugin.ts` will be able to access anything that was injected by `01.myPlugin.ts`. This is useful in situations where you have a plugin that depends on another plugin. ::note In case you're new to 'alphabetical' numbering, remember that filenames are sorted as strings, not as numeric values. For example, `10.myPlugin.ts` would come before `2.myOtherPlugin.ts`. This is why the example prefixes single digit numbers with `0`. :: ## Loading Strategy ### Parallel Plugins By default, Nuxt loads plugins sequentially. You can define a plugin as `parallel` so Nuxt won't wait until the end of the plugin's execution before loading the next plugin. ```ts twoslash [plugins/my-plugin.ts] export default defineNuxtPlugin({ name: 'my-plugin', parallel: true, async setup (nuxtApp) { // the next plugin will be executed immediately }, }) ``` ### Plugins With Dependencies If a plugin needs to wait for another plugin before it runs, you can add the plugin's name to the `dependsOn` array. ```ts twoslash [plugins/depending-on-my-plugin.ts] export default defineNuxtPlugin({ name: 'depends-on-my-plugin', dependsOn: ['my-plugin'], async setup (nuxtApp) { // this plugin will wait for the end of `my-plugin`'s execution before it runs }, }) ``` ## Using Composables You can use [composables](https://nuxt.com/docs/3.x/directory-structure/composables) as well as [utils](https://nuxt.com/docs/3.x/directory-structure/utils) within Nuxt plugins: ```ts [plugins/hello.ts] export default defineNuxtPlugin((nuxtApp) => { const foo = useFoo() }) ``` However, keep in mind there are some limitations and differences: ::important **If a composable depends on another plugin registered later, it might not work.** Plugins are called in order sequentially and before everything else. You might use a composable that depends on another plugin which has not been called yet. :: ::important **If a composable depends on the Vue.js lifecycle, it won't work.** Normally, Vue.js composables are bound to the current component instance while plugins are only bound to [`nuxtApp`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app) instance. :: ## Providing Helpers If you would like to provide a helper on the [`NuxtApp`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app) instance, return it from the plugin under a `provide` key. ::code-group ```ts twoslash [plugins/hello.ts] export default defineNuxtPlugin(() => { return { provide: { hello: (msg: string) => `Hello ${msg}!`, }, } }) ``` ```ts twoslash [plugins/hello-object-syntax.ts] export default defineNuxtPlugin({ name: 'hello', setup () { return { provide: { hello: (msg: string) => `Hello ${msg}!`, }, } }, }) ``` :: You can then use the helper in your components: ```vue [components/Hello.vue] <script setup lang="ts"> // alternatively, you can also use it here const { $hello } = useNuxtApp() </script> <template> <div> {{ $hello('world') }} </div> </template> ``` ::important Note that we highly recommend using [`composables`](https://nuxt.com/docs/3.x/directory-structure/composables) instead of providing helpers to avoid polluting the global namespace and keep your main bundle entry small. :: ::warning **If your plugin provides a `ref` or `computed`, it will not be unwrapped in a component `<template>`.** This is due to how Vue works with refs that aren't top-level to the template. You can read more about it [in the Vue documentation](https://vuejs.org/guide/essentials/reactivity-fundamentals#caveat-when-unwrapping-in-templates). :: ## Typing Plugins If you return your helpers from the plugin, they will be typed automatically; you'll find them typed for the return of `useNuxtApp()` and within your templates. ::note If you need to use a provided helper *within* another plugin, you can call [`useNuxtApp()`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app) to get the typed version. But in general, this should be avoided unless you are certain of the plugins' order. :: For advanced use-cases, you can declare the type of injected properties like this: ```ts [index.d.ts] declare module '#app' { interface NuxtApp { $hello (msg: string): string } } declare module 'vue' { interface ComponentCustomProperties { $hello (msg: string): string } } export {} ``` ## Vue Plugins If you want to use Vue plugins, like [vue-gtag](https://github.com/MatteoGabriele/vue-gtag) to add Google Analytics tags, you can use a Nuxt plugin to do so. First, install the Vue plugin dependency: ::code-group{sync="pm"} ```bash [npm] npm install --save-dev vue-gtag-next ``` ```bash [yarn] yarn add --dev vue-gtag-next ``` ```bash [pnpm] pnpm add -D vue-gtag-next ``` ```bash [bun] bun add -D vue-gtag-next ``` ```bash [deno] deno add -D npm:vue-gtag-next ``` :: Then create a plugin file: ```ts [plugins/vue-gtag.client.ts] import VueGtag, { trackRouter } from 'vue-gtag-next' export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.use(VueGtag, { property: { id: 'GA_MEASUREMENT_ID', }, }) trackRouter(useRouter()) }) ``` ## Vue Directives Similarly, you can register a custom Vue directive in a plugin. ```ts twoslash [plugins/my-directive.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.directive('focus', { mounted (el) { el.focus() }, getSSRProps (binding, vnode) { // you can provide SSR-specific props here return {} }, }) }) ``` ::warning If you register a Vue directive, you *must* register it on both client and server side unless you are only using it when rendering one side. If the directive only makes sense from a client side, you can always move it to `~/plugins/my-directive.client.ts` and provide a 'stub' directive for the server in `~/plugins/my-directive.server.ts`. :: ::read-more ```yaml [props] icon: i-simple-icons-vuedotjs title: Custom Directives on Vue Docs to: https://vuejs.org/guide/reusability/custom-directives.html target: _blank ``` :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "public" description: "The public/ directory is used to serve your website's static assets." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/public" --- # public > The public/ directory is used to serve your website's static assets. Files contained within the `public/` directory are served at the root and are not modified by the build process. This is suitable for files that have to keep their names (e.g. `robots.txt`) *or* likely won't change (e.g. `favicon.ico`). ```bash [Directory structure] -| public/ ---| favicon.ico ---| og-image.png ---| robots.txt ``` ```vue [app.vue] <script setup lang="ts"> useSeoMeta({ ogImage: '/og-image.png', }) </script> ``` ::tip{to="https://v2.nuxt.com/docs/directory-structure/static/" target="_blank"} This is known as the [`static/`] directory in Nuxt 2. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "server" description: "The server/ directory is used to register API and server handlers to your application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/server" --- # server > The server/ directory is used to register API and server handlers to your application. Nuxt automatically scans files inside these directories to register API and server handlers with Hot Module Replacement (HMR) support. ```bash [Directory structure] -| server/ ---| api/ -----| hello.ts # /api/hello ---| routes/ -----| bonjour.ts # /bonjour ---| middleware/ -----| log.ts # log all requests ``` Each file should export a default function defined with `defineEventHandler()` or `eventHandler()` (alias). The handler can directly return JSON data, a `Promise`, or use `event.node.res.end()` to send a response. ```ts twoslash [server/api/hello.ts] export default defineEventHandler((event) => { return { hello: 'world', } }) ``` You can now universally call this API in your pages and components: ```vue [pages/index.vue] <script setup lang="ts"> const { data } = await useFetch('/api/hello') </script> <template> <pre>{{ data }}</pre> </template> ``` ## Server Routes Files inside the `~~/server/api` are automatically prefixed with `/api` in their route. ::video-accordion{title="Watch a video from Vue School on API routes" videoId="761468863" platform="vimeo"} :: To add server routes without `/api` prefix, put them into `~~/server/routes` directory. **Example:** ```ts [server/routes/hello.ts] export default defineEventHandler(() => 'Hello World!') ``` Given the example above, the `/hello` route will be accessible at <http://localhost:3000/hello>. ::note Note that currently server routes do not support the full functionality of dynamic routes as [pages](https://nuxt.com/docs/3.x/directory-structure/pages#dynamic-routes) do. :: ## Server Middleware Nuxt will automatically read in any file in the `~~/server/middleware` to create server middleware for your project. Middleware handlers will run on every request before any other server route to add or check headers, log requests, or extend the event's request object. ::note Middleware handlers should not return anything (nor close or respond to the request) and only inspect or extend the request context or throw an error. :: **Examples:** ```ts [server/middleware/log.ts] export default defineEventHandler((event) => { console.log('New request: ' + getRequestURL(event)) }) ``` ```ts [server/middleware/auth.ts] export default defineEventHandler((event) => { event.context.auth = { user: 123 } }) ``` ## Server Plugins Nuxt will automatically read any files in the `~~/server/plugins` directory and register them as Nitro plugins. This allows extending Nitro's runtime behavior and hooking into lifecycle events. **Example:** ```ts [server/plugins/nitroPlugin.ts] export default defineNitroPlugin((nitroApp) => { console.log('Nitro plugin', nitroApp) }) ``` ::read-more{to="https://nitro.build/guide/plugins" title="Nitro Plugins" target="_blank"} :: ## Server Utilities Server routes are powered by [h3js/h3](https://github.com/h3js/h3) which comes with a handy set of helpers. ::read-more{to="https://www.jsdocs.io/package/h3#package-index-functions" title="Available H3 Request Helpers" target="_blank"} :: You can add more helpers yourself inside the `~~/server/utils` directory. For example, you can define a custom handler utility that wraps the original handler and performs additional operations before returning the final response. **Example:** ```ts [server/utils/handler.ts] export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> ( handler: EventHandler<T, D>, ): EventHandler<T, D> => defineEventHandler<T>(async (event) => { try { // do something before the route handler const response = await handler(event) // do something after the route handler return { response } } catch (err) { // Error handling return { err } } }) ``` ```ts [server/api/hello.get.ts] export default defineWrappedResponseHandler(event => 'hello world') ``` ## Server Alias You can use the `#server` alias to import files from anywhere within the `server/` directory, regardless of the importing file's location. ```ts [server/api/users/[id\\]/profile.ts] // Instead of relative paths like this: // import { formatUser } from '../../../utils/formatUser' // Use the #server alias: import { formatUser } from '#server/utils/formatUser' ``` This alias ensures consistent imports across your server code, especially useful in deeply nested route handlers. ::note The `#server` alias can only be used within the `server/` directory. Importing from `#server` in client code will result in an error. :: ## Server Types ::tip This feature is available from Nuxt >= 3.5 :: To improve clarity within your IDE between the auto-imports from 'nitro' and 'vue', you can add a `~/server/tsconfig.json` with the following content: ```json [server/tsconfig.json] { "extends": "../.nuxt/tsconfig.server.json" } ``` Currently, these values won't be respected when type checking ([`nuxt typecheck`](https://nuxt.com/docs/3.x/api/commands/typecheck)), but you should get better type hints in your IDE. ## Recipes ### Route Parameters Server routes can use dynamic parameters within brackets in the file name like `/api/hello/[name].ts` and be accessed via `event.context.params`. ```ts [server/api/hello/[name\\].ts] export default defineEventHandler((event) => { const name = getRouterParam(event, 'name') return `Hello, ${name}!` }) ``` ::tip{to="https://h3.dev/examples/validate-data#validate-params"} Alternatively, use `getValidatedRouterParams` with a schema validator such as Zod for runtime and type safety. :: You can now universally call this API on `/api/hello/nuxt` and get `Hello, nuxt!`. ### Matching HTTP Method Handle file names can be suffixed with `.get`, `.post`, `.put`, `.delete`, ... to match request's [HTTP Method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Methods). ```ts [server/api/test.get.ts] export default defineEventHandler(() => 'Test get handler') ``` ```ts [server/api/test.post.ts] export default defineEventHandler(() => 'Test post handler') ``` Given the example above, fetching `/test` with: - **GET** method: Returns `Test get handler` - **POST** method: Returns `Test post handler` - Any other method: Returns 405 error You can also use `index.[method].ts` inside a directory for structuring your code differently, this is useful to create API namespaces. ::code-group ```ts [server/api/foo/index.get.ts] export default defineEventHandler((event) => { // handle GET requests for the `api/foo` endpoint }) ``` ```ts [server/api/foo/index.post.ts] export default defineEventHandler((event) => { // handle POST requests for the `api/foo` endpoint }) ``` ```ts [server/api/foo/bar.get.ts] export default defineEventHandler((event) => { // handle GET requests for the `api/foo/bar` endpoint }) ``` :: ### Catch-all Route Catch-all routes are helpful for fallback route handling. For example, creating a file named `~~/server/api/foo/[...].ts` will register a catch-all route for all requests that do not match any route handler, such as `/api/foo/bar/baz`. ```ts [server/api/foo/[...\\].ts] export default defineEventHandler((event) => { // event.context.path to get the route path: '/api/foo/bar/baz' // event.context.params._ to get the route segment: 'bar/baz' return `Default foo handler` }) ``` You can set a name for the catch-all route by using `~~/server/api/foo/[...slug].ts` and access it via `event.context.params.slug`. ```ts [server/api/foo/[...slug\\].ts] export default defineEventHandler((event) => { // event.context.params.slug to get the route segment: 'bar/baz' return `Default foo handler` }) ``` ### Body Handling ```ts [server/api/submit.post.ts] export default defineEventHandler(async (event) => { const body = await readBody(event) return { body } }) ``` ::tip{to="https://unjs.io/blog/2023-08-15-h3-towards-the-edge-of-the-web/#runtime-type-safe-request-utils"} Alternatively, use `readValidatedBody` with a schema validator such as Zod for runtime and type safety. :: You can now universally call this API using: ```vue [app.vue] <script setup lang="ts"> async function submit () { const { body } = await $fetch('/api/submit', { method: 'post', body: { test: 123 }, }) } </script> ``` ::note We are using `submit.post.ts` in the filename only to match requests with `POST` method that can accept the request body. When using `readBody` within a GET request, `readBody` will throw a `405 Method Not Allowed` HTTP error. :: ### Query Parameters Sample query `/api/query?foo=bar&baz=qux` ```ts [server/api/query.get.ts] export default defineEventHandler((event) => { const query = getQuery(event) return { a: query.foo, b: query.baz } }) ``` ::tip{to="https://unjs.io/blog/2023-08-15-h3-towards-the-edge-of-the-web#runtime-type-safe-request-utils"} Alternatively, use `getValidatedQuery` with a schema validator such as Zod for runtime and type safety. :: ### Error Handling If no errors are thrown, a status code of `200 OK` will be returned. Any uncaught errors will return a `500 Internal Server Error` HTTP Error. To return other error codes, throw an exception with [`createError`](https://nuxt.com/docs/3.x/api/utils/create-error): ```ts [server/api/validation/[id\\].ts] export default defineEventHandler((event) => { const id = Number.parseInt(event.context.params.id) as number if (!Number.isInteger(id)) { throw createError({ status: 400, statusText: 'ID should be an integer', }) } return 'All good' }) ``` ### Status Codes To return other status codes, use the [`setResponseStatus`](https://nuxt.com/docs/3.x/api/utils/set-response-status) utility. For example, to return `202 Accepted` ```ts [server/api/validation/[id\\].ts] export default defineEventHandler((event) => { setResponseStatus(event, 202) }) ``` ### Runtime Config ::code-group ```ts [server/api/foo.ts] export default defineEventHandler(async (event) => { const config = useRuntimeConfig(event) const repo = await $fetch('https://api.github.com/repos/nuxt/nuxt', { headers: { Authorization: `token ${config.githubToken}`, }, }) return repo }) ``` ```ts [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { githubToken: '', }, }) ``` ```ini [.env] NUXT_GITHUB_TOKEN='<my-super-token>' ``` :: ::note Giving the `event` as argument to `useRuntimeConfig` is optional, but it is recommended to pass it to get the runtime config overwritten by [environment variables](https://nuxt.com/docs/3.x/guide/going-further/runtime-config#environment-variables) at runtime for server routes. :: ### Request Cookies ```ts [server/api/cookies.ts] export default defineEventHandler((event) => { const cookies = parseCookies(event) return { cookies } }) ``` ### Forwarding Context & Headers By default, neither the headers from the incoming request nor the request context are forwarded when making fetch requests in server routes. You can use `event.$fetch` to forward the request context and headers when making fetch requests in server routes. ```ts [server/api/forward.ts] export default defineEventHandler((event) => { return event.$fetch('/api/forwarded') }) ``` ::note Headers that are **not meant to be forwarded** will **not be included** in the request. These headers include, for example: `transfer-encoding`, `connection`, `keep-alive`, `upgrade`, `expect`, `host`, `accept` :: ### Awaiting Promises After Response When handling server requests, you might need to perform asynchronous tasks that shouldn't block the response to the client (for example, caching and logging). You can use `event.waitUntil` to await a promise in the background without delaying the response. The `event.waitUntil` method accepts a promise that will be awaited before the handler terminates, ensuring the task is completed even if the server would otherwise terminate the handler right after the response is sent. This integrates with runtime providers to leverage their native capabilities for handling asynchronous operations after the response is sent. ```ts [server/api/background-task.ts] const timeConsumingBackgroundTask = async () => { await new Promise(resolve => setTimeout(resolve, 1000)) } export default eventHandler((event) => { // schedule a background task without blocking the response event.waitUntil(timeConsumingBackgroundTask()) // immediately send the response to the client return 'done' }) ``` ## Advanced Usage ### Nitro Config You can use `nitro` key in `nuxt.config` to directly set [Nitro configuration](https://nitro.build/config). ::warning This is an advanced option. Custom config can affect production deployments, as the configuration interface might change over time when Nitro is upgraded in semver-minor versions of Nuxt. :: ```ts [nuxt.config.ts] export default defineNuxtConfig({ // https://nitro.build/config nitro: {}, }) ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/concepts/server-engine"} :: ### Nested Router ```ts [server/api/hello/[...slug\\].ts] import { createRouter, defineEventHandler, useBase } from 'h3' const router = createRouter() router.get('/test', defineEventHandler(() => 'Hello World')) export default useBase('/api/hello', router.handler) ``` ### Sending Streams ::tip This is an experimental feature and is available in all environments. :: ```ts [server/api/foo.get.ts] import fs from 'node:fs' import { sendStream } from 'h3' export default defineEventHandler((event) => { return sendStream(event, fs.createReadStream('/path/to/file')) }) ``` ### Sending Redirect ```ts [server/api/foo.get.ts] export default defineEventHandler(async (event) => { await sendRedirect(event, '/path/redirect/to', 302) }) ``` ### Legacy Handler or Middleware ```ts [server/api/legacy.ts] export default fromNodeMiddleware((req, res) => { res.end('Legacy handler') }) ``` ::important Legacy support is possible using [h3js/h3](https://github.com/h3js/h3), but it is advised to avoid legacy handlers as much as you can. :: ```ts [server/middleware/legacy.ts] export default fromNodeMiddleware((req, res, next) => { console.log('Legacy middleware') next() }) ``` ::warning Never combine `next()` callback with a legacy middleware that is `async` or returns a `Promise`. :: ### Server Storage Nitro provides a cross-platform [storage layer](https://nitro.build/guide/storage). In order to configure additional storage mount points, you can use `nitro.storage`, or [server plugins](https://nuxt.com/docs/3.x/directory-structure/server#server-plugins). **Example of adding a Redis storage:** Using `nitro.storage`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { storage: { redis: { driver: 'redis', /* redis connector options */ port: 6379, // Redis port host: '127.0.0.1', // Redis host username: '', // needs Redis >= 6 password: '', db: 0, // Defaults to 0 tls: {}, // tls/ssl }, }, }, }) ``` Then in your API handler: ```ts [server/api/storage/test.ts] export default defineEventHandler(async (event) => { // List all keys with const keys = await useStorage('redis').getKeys() // Set a key with await useStorage('redis').setItem('foo', 'bar') // Remove a key with await useStorage('redis').removeItem('foo') return {} }) ``` ::read-more{to="https://nitro.build/guide/storage" target="_blank"} Read more about Nitro Storage Layer. :: Alternatively, you can create a storage mount point using a server plugin and runtime config: ::code-group ```ts [server/plugins/storage.ts] import redisDriver from 'unstorage/drivers/redis' export default defineNitroPlugin(() => { const storage = useStorage() // Dynamically pass in credentials from runtime configuration, or other sources const driver = redisDriver({ base: 'redis', host: useRuntimeConfig().redis.host, port: useRuntimeConfig().redis.port, /* other redis connector options */ }) // Mount driver storage.mount('redis', driver) }) ``` ```ts [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { redis: { // Default values host: '', port: 0, /* other redis connector options */ }, }, }) ``` :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "shared" description: "Use the shared/ directory to share functionality between the Vue app and the Nitro server." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/shared" --- # shared > Use the shared/ directory to share functionality between the Vue app and the Nitro server. The `shared/` directory allows you to share code that can be used in both the Vue app and the Nitro server. ::note The `shared/` directory is available in Nuxt v3.14+. :: ::important Code in the `shared/` directory cannot import any Vue or Nitro code. :: ::warning Auto-imports are not enabled by default in Nuxt v3 to prevent breaking changes in existing projects. To use these auto-imported utils and types, you must first [set `future.compatibilityVersion: 4` in your `nuxt.config.ts`](https://nuxt.com/docs/3.x/getting-started/upgrade#opting-in-to-nuxt-4). :: ::video-accordion{title="Watch a video from Vue School on sharing utils and types between app and server" videoId="nnAR-MO3q5M"} :: ## Usage **Method 1:** Named export ```ts twoslash [shared/utils/capitalize.ts] export const capitalize = (input: string) => { return input[0] ? input[0].toUpperCase() + input.slice(1) : '' } ``` **Method 2:** Default export ```ts twoslash [shared/utils/capitalize.ts] export default function (input: string) { return input[0] ? input[0].toUpperCase() + input.slice(1) : '' } ``` You can now use [auto-imported](https://nuxt.com/docs/3.x/directory-structure/shared) utilities in your Nuxt app and `server/` directory. ```vue [app.vue] <script setup lang="ts"> const hello = capitalize('hello') </script> <template> <div> {{ hello }} </div> </template> ``` ```ts [server/api/hello.get.ts] export default defineEventHandler((event) => { return { hello: capitalize('hello'), } }) ``` ## How Files Are Scanned Only files in the `shared/utils/` and `shared/types/` directories will be auto-imported. Files nested within subdirectories of these directories will not be auto-imported unless you add these directories to `imports.dirs` and `nitro.imports.dirs`. ::tip The way `shared/utils` and `shared/types` auto-imports work and are scanned is identical to the [`composables/`](https://nuxt.com/docs/3.x/directory-structure/composables) and [`utils/`](https://nuxt.com/docs/3.x/directory-structure/utils) directories. :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/composables#how-files-are-scanned"} :: ```bash [Directory Structure] -| shared/ ---| capitalize.ts # Not auto-imported ---| formatters -----| lower.ts # Not auto-imported ---| utils/ -----| lower.ts # Auto-imported -----| formatters -------| upper.ts # Not auto-imported ---| types/ -----| bar.ts # Auto-imported ``` Any other files you create in the `shared/` folder must be manually imported using the `#shared` alias (automatically configured by Nuxt): ```ts // For files directly in the shared directory import capitalize from '#shared/capitalize' // For files in nested directories import lower from '#shared/formatters/lower' // For files nested in a folder within utils import upper from '#shared/utils/formatters/upper' ``` This alias ensures consistent imports across your application, regardless of the importing file's location. ::read-more{to="https://nuxt.com/docs/3.x/guide/concepts/auto-imports"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "tsconfig.json" description: "Learn how Nuxt manages TypeScript configuration across different parts of your project." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/tsconfig" --- # tsconfig.json > Learn how Nuxt manages TypeScript configuration across different parts of your project. Nuxt [automatically generates](https://nuxt.com/docs/3.x/guide/concepts/typescript) a `.nuxt/tsconfig.json` file with the resolved aliases you are using in your Nuxt project, as well as with other sensible defaults. Your Nuxt project should include the following `tsconfig.json` file at the root of the project: ```json [tsconfig.json] { "extends": "./.nuxt/tsconfig.json" } ``` ::note As you need to, you can customize the contents of this file. However, it is recommended that you don't overwrite `target`, `module` and `moduleResolution`. :: ::note If you need to customize your `paths`, this will override the auto-generated path aliases. Instead, we recommend that you add any path aliases you need to the [`alias`](https://nuxt.com/docs/3.x/api/nuxt-config#alias) property within your `nuxt.config`, where they will get picked up and added to the auto-generated `tsconfig`. :: ## Extending TypeScript Configuration You can customize the TypeScript configuration of your Nuxt project for each context (`app` and `server`) in the `nuxt.config.ts` file. <!-- @case-police-ignore tsConfig --> ```ts twoslash [nuxt.config.ts] // @errors: 2353 export default defineNuxtConfig({ typescript: { // customize tsconfig.app.json tsConfig: { // ... }, }, nitro: { typescript: { // customize tsconfig.server.json tsConfig: { // ... }, }, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "utils" description: "Use the utils/ directory to auto-import your utility functions throughout your application." canonical_url: "https://nuxt.com/docs/3.x/directory-structure/utils" --- # utils > Use the utils/ directory to auto-import your utility functions throughout your application. The main purpose of the [`utils/` directory](https://nuxt.com/docs/3.x/directory-structure/utils) is to allow a semantic distinction between your Vue composables and other auto-imported utility functions. ## Usage **Method 1:** Using named export ```ts twoslash [utils/index.ts] export const { format: formatNumber } = Intl.NumberFormat('en-GB', { notation: 'compact', maximumFractionDigits: 1, }) ``` **Method 2:** Using default export ```ts twoslash [utils/random-entry.ts or utils/randomEntry.ts] // It will be available as randomEntry() (camelCase of file name without extension) export default function (arr: Array<any>) { return arr[Math.floor(Math.random() * arr.length)] } ``` You can now use auto imported utility functions in `.js`, `.ts` and `.vue` files ```vue [app.vue] <template> <p>{{ formatNumber(1234) }}</p> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/concepts/auto-imports"} :: ::link-example{to="https://nuxt.com/docs/examples/features/auto-imports"} :: ::tip The way `utils/` auto-imports work and are scanned is identical to the [`composables/`](https://nuxt.com/docs/3.x/directory-structure/composables) directory. :: ::important These utils are only available within the Vue part of your app. Only `server/utils` are auto-imported in the [`server/`](https://nuxt.com/docs/3.x/directory-structure/server#server-utilities) directory. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Assets" description: "Nuxt offers two options for your assets." canonical_url: "https://nuxt.com/docs/3.x/getting-started/assets" --- # Assets > Nuxt offers two options for your assets. Nuxt uses two directories to handle assets like stylesheets, fonts or images. - The [`public/`](https://nuxt.com/docs/3.x/directory-structure/public) directory content is served at the server root as-is. - The [`assets/`](https://nuxt.com/docs/3.x/directory-structure/assets) directory contains by convention every asset that you want the build tool (Vite or webpack) to process. ## Public Directory The [`public/`](https://nuxt.com/docs/3.x/directory-structure/public) directory is used as a public server for static assets publicly available at a defined URL of your application. You can get a file in the [`public/`](https://nuxt.com/docs/3.x/directory-structure/public) directory from your application's code or from a browser by the root URL `/`. ### Example For example, referencing an image file in the `public/img/` directory, available at the static URL `/img/nuxt.png`: ```vue [app.vue] <template> <img src="/img/nuxt.png" alt="Discover Nuxt" > </template> ``` ## Assets Directory Nuxt uses [Vite](https://vite.dev/guide/assets) (default) or [webpack](https://webpack.js.org/guides/asset-management/) to build and bundle your application. The main function of these build tools is to process JavaScript files, but they can be extended through [plugins](https://vite.dev/plugins/) (for Vite) or [loaders](https://webpack.js.org/loaders/) (for webpack) to process other kinds of assets, like stylesheets, fonts or SVGs. This step transforms the original file, mainly for performance or caching purposes (such as stylesheet minification or browser cache invalidation). By convention, Nuxt uses the [`assets/`](https://nuxt.com/docs/3.x/directory-structure/assets) directory to store these files but there is no auto-scan functionality for this directory, and you can use any other name for it. In your application's code, you can reference a file located in the [`assets/`](https://nuxt.com/docs/3.x/directory-structure/assets) directory by using the `~/assets/` path. ### Example For example, referencing an image file that will be processed if a build tool is configured to handle this file extension: ```vue [app.vue] <template> <img src="~/assets/img/nuxt.png" alt="Discover Nuxt" > </template> ``` ::note Nuxt won't serve files in the [`assets/`](https://nuxt.com/docs/3.x/directory-structure/assets) directory at a static URL like `/assets/my-file.png`. If you need a static URL, use the [`public/`](https://nuxt.com/docs/3.x/getting-started/assets#public-directory) directory. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Configuration" description: "Nuxt is configured with sensible defaults to make you productive." canonical_url: "https://nuxt.com/docs/3.x/getting-started/configuration" --- # Configuration > Nuxt is configured with sensible defaults to make you productive. By default, Nuxt is configured to cover most use cases. The [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) file can override or extend this default configuration. ## Nuxt Configuration The [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) file is located at the root of a Nuxt project and can override or extend the application's behavior. A minimal configuration file exports the `defineNuxtConfig` function containing an object with your configuration. The `defineNuxtConfig` helper is globally available without import. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ // My Nuxt config }) ``` This file will often be mentioned in the documentation, for example to add custom scripts, register modules or change rendering modes. ::read-more{to="https://nuxt.com/docs/3.x/api/configuration/nuxt-config"} Every option is described in the **Configuration Reference**. :: ::note You don't have to use TypeScript to build an application with Nuxt. However, it is strongly recommended to use the `.ts` extension for the `nuxt.config` file. This way you can benefit from hints in your IDE to avoid typos and mistakes while editing your configuration. :: ### Environment Overrides You can configure fully typed, per-environment overrides in your nuxt.config ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ $production: { routeRules: { '/**': { isr: true }, }, }, $development: { // }, $env: { staging: { // }, }, }) ``` To select an environment when running a Nuxt CLI command, simply pass the name to the `--envName` flag, like so: `nuxt build --envName staging`. To learn more about the mechanism behind these overrides, please refer to the `c12` documentation on [environment-specific configuration](https://github.com/unjs/c12?tab=readme-ov-file#environment-specific-configuration). ::video-accordion{title="Watch a video from Alexander Lichter about the env-aware nuxt.config.ts" videoId="DFZI2iVCrNc"} :: ::note If you're authoring layers, you can also use the `$meta` key to provide metadata that you or the consumers of your layer might use. :: ### Environment Variables and Private Tokens The `runtimeConfig` API exposes values like environment variables to the rest of your application. By default, these keys are only available server-side. The keys within `runtimeConfig.public` and `runtimeConfig.app` (which is used by Nuxt internally) are also available client-side. Those values should be defined in `nuxt.config` and can be overridden using environment variables. ::code-group ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { // The private keys which are only available server-side apiSecret: '123', // Keys within public are also exposed client-side public: { apiBase: '/api', }, }, }) ``` ```ini [.env] # This will override the value of apiSecret NUXT_API_SECRET=api_secret_token ``` :: These variables are exposed to the rest of your application using the [`useRuntimeConfig()`](https://nuxt.com/docs/3.x/api/composables/use-runtime-config) composable. ```vue [pages/index.vue] <script setup lang="ts"> const runtimeConfig = useRuntimeConfig() </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/runtime-config"} :: ## App Configuration The `app.config.ts` file, located in the source directory (by default the root of the project), is used to expose public variables that can be determined at build time. Contrary to the `runtimeConfig` option, these cannot be overridden using environment variables. A minimal configuration file exports the `defineAppConfig` function containing an object with your configuration. The `defineAppConfig` helper is globally available without import. ```ts [app.config.ts] export default defineAppConfig({ title: 'Hello Nuxt', theme: { dark: true, colors: { primary: '#ff0000', }, }, }) ``` These variables are exposed to the rest of your application using the [`useAppConfig`](https://nuxt.com/docs/3.x/api/composables/use-app-config) composable. ```vue [pages/index.vue] <script setup lang="ts"> const appConfig = useAppConfig() </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/app-config"} :: ## `runtimeConfig` vs. `app.config` As stated above, `runtimeConfig` and `app.config` are both used to expose variables to the rest of your application. To determine whether you should use one or the other, here are some guidelines: - `runtimeConfig`: Private or public tokens that need to be specified after build using environment variables. - `app.config`: Public tokens that are determined at build time, website configuration such as theme variant, title and any project config that are not sensitive. | Feature | `runtimeConfig` | `app.config` | | ------------------------- | --------------- | ------------ | | Client-side | Hydrated | Bundled | | Environment variables | ✅ Yes | ❌ No | | Reactive | ✅ Yes | ✅ Yes | | Types support | ✅ Partial | ✅ Yes | | Configuration per request | ❌ No | ✅ Yes | | Hot module replacement | ❌ No | ✅ Yes | | Non-primitive JS types | ❌ No | ✅ Yes | ## External Configuration Files Nuxt uses [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) file as the single source of truth for configurations and skips reading external configuration files. During the course of building your project, you may have a need to configure those. The following table highlights common configurations and, where applicable, how they can be configured with Nuxt. | Name | Config File | How To Configure | | --------------------------------- | ----------------------- | ----------------------------------------------------------------------------------------- | | [Nitro](https://nitro.build) | ~~`nitro.config.ts`~~ | Use [`nitro`](https://nuxt.com/docs/3.x/api/nuxt-config#nitro) key in `nuxt.config` | | [PostCSS](https://postcss.org) | ~~`postcss.config.js`~~ | Use [`postcss`](https://nuxt.com/docs/3.x/api/nuxt-config#postcss) key in `nuxt.config` | | [Vite](https://vite.dev) | ~~`vite.config.ts`~~ | Use [`vite`](https://nuxt.com/docs/3.x/api/nuxt-config#vite) key in `nuxt.config` | | [webpack](https://webpack.js.org) | ~~`webpack.config.ts`~~ | Use [`webpack`](https://nuxt.com/docs/3.x/api/nuxt-config#webpack-1) key in `nuxt.config` | Here is a list of other common config files: | Name | Config File | How To Configure | | -------------------------------------------- | --------------------- | ----------------------------------------------------------------------------- | | [TypeScript](https://www.typescriptlang.org) | `tsconfig.json` | [More Info](https://nuxt.com/docs/3.x/directory-structure/tsconfig) | | [ESLint](https://eslint.org) | `eslint.config.js` | [More Info](https://eslint.org/docs/latest/use/configure/configuration-files) | | [Prettier](https://prettier.io) | `prettier.config.js` | [More Info](https://prettier.io/docs/configuration.html) | | [Stylelint](https://stylelint.io) | `stylelint.config.js` | [More Info](https://stylelint.io/user-guide/configure/) | | [TailwindCSS](https://tailwindcss.com) | `tailwind.config.js` | [More Info](https://tailwindcss.nuxtjs.org/tailwindcss/configuration/) | | [Vitest](https://vitest.dev) | `vitest.config.ts` | [More Info](https://vitest.dev/config/) | ## Vue Configuration ### With Vite If you need to pass options to `@vitejs/plugin-vue` or `@vitejs/plugin-vue-jsx`, you can do this in your `nuxt.config` file. - `vite.vue` for `@vitejs/plugin-vue`. Check [available options](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue). - `vite.vueJsx` for `@vitejs/plugin-vue-jsx`. Check [available options](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue-jsx). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ vite: { vue: { customElement: true, }, vueJsx: { mergeProps: true, }, }, }) ``` ::read-more{to="https://nuxt.com/docs/3.x/api/configuration/nuxt-config#vue-1"} :: ### With webpack If you use webpack and need to configure `vue-loader`, you can do this using `webpack.loaders.vue` key inside your `nuxt.config` file. The available options are [defined here](https://github.com/vuejs/vue-loader/blob/main/src/index.ts#L32-L62). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ webpack: { loaders: { vue: { hotReload: true, }, }, }, }) ``` ::read-more{to="https://nuxt.com/docs/3.x/api/configuration/nuxt-config#loaders"} :: ### Enabling Experimental Vue Features You may need to enable experimental features in Vue, such as `propsDestructure`. Nuxt provides an easy way to do that in `nuxt.config.ts`, no matter which builder you are using: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ vue: { propsDestructure: true, }, }) ``` #### experimental `reactivityTransform` migration from Vue 3.4 and Nuxt 3.9 Since Nuxt 3.9 and Vue 3.4, `reactivityTransform` has been moved from Vue to Vue Macros which has a [Nuxt integration](https://vue-macros.dev/guide/nuxt-integration.html). ::read-more{to="https://nuxt.com/docs/3.x/api/configuration/nuxt-config#vue-1"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Data Fetching" description: "Nuxt provides composables to handle data fetching within your application." canonical_url: "https://nuxt.com/docs/3.x/getting-started/data-fetching" --- # Data Fetching > Nuxt provides composables to handle data fetching within your application. Nuxt comes with two composables and a built-in library to perform data-fetching in browser or server environments: `useFetch`, [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) and `$fetch`. In a nutshell: - [`$fetch`](https://nuxt.com/docs/3.x/api/utils/dollarfetch) is the simplest way to make a network request. - [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) is a wrapper around `$fetch` that fetches data only once in [universal rendering](https://nuxt.com/docs/3.x/guide/concepts/rendering#universal-rendering). - [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) is similar to `useFetch` but offers more fine-grained control. Both `useFetch` and `useAsyncData` share a common set of options and patterns that we will detail in the last sections. ## The need for `useFetch` and `useAsyncData` Nuxt is a framework which can run isomorphic (or universal) code in both server and client environments. If the [`$fetch` function](https://nuxt.com/docs/3.x/api/utils/dollarfetch) is used to perform data fetching in the setup function of a Vue component, this may cause data to be fetched twice, once on the server (to render the HTML) and once again on the client (when the HTML is hydrated). This can cause hydration issues, increase the time to interactivity and cause unpredictable behavior. The [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) and [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) composables solve this problem by ensuring that if an API call is made on the server, the data is forwarded to the client in the payload. The payload is a JavaScript object accessible through [`useNuxtApp().payload`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app#payload). It is used on the client to avoid refetching the same data when the code is executed in the browser [during hydration](https://nuxt.com/docs/3.x/guide/concepts/rendering#universal-rendering). ::tip Use the [Nuxt DevTools](https://nuxt.com/modules/devtools) to inspect this data in the **Payload tab**. :: ```vue [app.vue] <script setup lang="ts"> const { data } = await useFetch('/api/data') async function handleFormSubmit () { const res = await $fetch('/api/submit', { method: 'POST', body: { // My form data }, }) } </script> <template> <div v-if="data == null"> No data </div> <div v-else> <form @submit="handleFormSubmit"> <!-- form input tags --> </form> </div> </template> ``` In the example above, `useFetch` would make sure that the request would occur in the server and is properly forwarded to the browser. `$fetch` has no such mechanism and is a better option to use when the request is solely made from the browser. ### Suspense Nuxt uses Vue's [`<Suspense>`](https://vuejs.org/guide/built-ins/suspense) component under the hood to prevent navigation before every async data is available to the view. The data fetching composables can help you leverage this feature and use what suits best on a per-call basis. ::note You can add the [`<NuxtLoadingIndicator>`](https://nuxt.com/docs/3.x/api/components/nuxt-loading-indicator) to add a progress bar between page navigations. :: ## `$fetch` Nuxt includes the [ofetch](https://github.com/unjs/ofetch) library, and is auto-imported as the `$fetch` alias globally across your application. ```vue twoslash [pages/todos.vue] <script setup lang="ts"> async function addTodo () { const todo = await $fetch('/api/todos', { method: 'POST', body: { // My todo data }, }) } </script> ``` ::warning Beware that using only `$fetch` will not provide [network calls de-duplication and navigation prevention](https://nuxt.com/docs/3.x/getting-started/data-fetching#the-need-for-usefetch-and-useasyncdata). It is recommended to use `$fetch` for client-side interactions (event-based) or combined with [`useAsyncData`](https://nuxt.com/docs/3.x/getting-started/data-fetching#useasyncdata) when fetching the initial component data. :: ::read-more{to="https://nuxt.com/docs/3.x/api/utils/dollarfetch"} Read more about `$fetch`. :: ### Pass Client Headers to the API When calling `useFetch` on the server, Nuxt will use [`useRequestFetch`](https://nuxt.com/docs/3.x/api/composables/use-request-fetch) to proxy client headers and cookies (with the exception of headers not meant to be forwarded, like `host`). ```vue <script setup lang="ts"> const { data } = await useFetch('/api/echo') </script> ``` ```ts // /api/echo.ts export default defineEventHandler(event => parseCookies(event)) ``` Alternatively, the example below shows how to use [`useRequestHeaders`](https://nuxt.com/docs/3.x/api/composables/use-request-headers) to access and send cookies to the API from a server-side request (originating on the client). Using an isomorphic `$fetch` call, we ensure that the API endpoint has access to the same `cookie` header originally sent by the user's browser. This is only necessary if you aren't using `useFetch`. ```vue <script setup lang="ts"> const headers = useRequestHeaders(['cookie']) async function getCurrentUser () { return await $fetch('/api/me', { headers }) } </script> ``` ::tip You can also use [`useRequestFetch`](https://nuxt.com/docs/3.x/api/composables/use-request-fetch) to proxy headers to the call automatically. :: ::caution Be very careful before proxying headers to an external API and just include headers that you need. Not all headers are safe to be bypassed and might introduce unwanted behavior. Here is a list of common headers that are NOT to be proxied: - `host`, `accept` - `content-length`, `content-md5`, `content-type` - `x-forwarded-host`, `x-forwarded-port`, `x-forwarded-proto` - `cf-connecting-ip`, `cf-ray` :: ## `useFetch` The [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) composable uses `$fetch` under-the-hood to make SSR-safe network calls in the setup function. ```vue twoslash [app.vue] <script setup lang="ts"> const { data: count } = await useFetch('/api/count') </script> <template> <p>Page visits: {{ count }}</p> </template> ``` This composable is a wrapper around the [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) composable and `$fetch` utility. ::video-accordion{title="Watch a video from Alexander Lichter to avoid using useFetch the wrong way" videoId="njsGVmcWviY"} :: ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-fetch"} :: ::link-example{to="https://nuxt.com/docs/examples/features/data-fetching"} :: ## `useAsyncData` The `useAsyncData` composable is responsible for wrapping async logic and returning the result once it is resolved. ::tip `useFetch(url)` is nearly equivalent to `useAsyncData(url, () => event.$fetch(url))`. It's developer experience sugar for the most common use case. (You can find out more about `event.fetch` at [`useRequestFetch`](https://nuxt.com/docs/3.x/api/composables/use-request-fetch).) :: ::video-accordion{title="Watch a video from Alexander Lichter to dig deeper into the difference between useFetch and useAsyncData" videoId="0X-aOpSGabA"} :: There are some cases when using the [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) composable is not appropriate, for example when a CMS or a third-party provide their own query layer. In this case, you can use [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) to wrap your calls and still keep the benefits provided by the composable. ```vue [pages/users.vue] <script setup lang="ts"> const { data, error } = await useAsyncData('users', () => myGetFunction('users')) // This is also possible: const { data, error } = await useAsyncData(() => myGetFunction('users')) </script> ``` ::note The first argument of [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) is a unique key used to cache the response of the second argument, the querying function. This key can be ignored by directly passing the querying function, the key will be auto-generated. Since the autogenerated key only takes into account the file and line where `useAsyncData` is invoked, it is recommended to always create your own key to avoid unwanted behavior, like when you are creating your own custom composable wrapping `useAsyncData`. Setting a key can be useful to share the same data between components using [`useNuxtData`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-data) or to [refresh specific data](https://nuxt.com/docs/3.x/api/utils/refresh-nuxt-data#refresh-specific-data). :: ```vue [pages/users/[id\\].vue] <script setup lang="ts"> const { id } = useRoute().params const { data, error } = await useAsyncData(`user:${id}`, () => { return myGetFunction('users', { id }) }) </script> ``` The `useAsyncData` composable is a great way to wrap and wait for multiple `$fetch` requests to be completed, and then process the results. ```vue <script setup lang="ts"> const { data: discounts, status } = await useAsyncData('cart-discount', async (_nuxtApp, { signal }) => { const [coupons, offers] = await Promise.all([ $fetch('/cart/coupons', { signal }), $fetch('/cart/offers', { signal }), ]) return { coupons, offers } }) // discounts.value.coupons // discounts.value.offers </script> ``` ::note `useAsyncData` is for fetching and caching data, not triggering side effects like calling Pinia actions, as this can cause unintended behavior such as repeated executions with nullish values. If you need to trigger side effects, use the [`callOnce`](https://nuxt.com/docs/3.x/api/utils/call-once) utility to do so. ```vue <script setup lang="ts"> const offersStore = useOffersStore() // you can't do this await useAsyncData(() => offersStore.getOffer(route.params.slug)) </script> ``` :: ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-async-data"} Read more about `useAsyncData`. :: ## Return Values `useFetch` and `useAsyncData` have the same return values listed below. - `data`: the result of the asynchronous function that is passed in. - `refresh`/`execute`: a function that can be used to refresh the data returned by the `handler` function. - `clear`: a function that can be used to set `data` to `undefined` (or the value of `options.default()` if provided), set `error` to `null`, set `status` to `idle`, and mark any currently pending requests as cancelled. - `error`: an error object if the data fetching failed. - `status`: a string indicating the status of the data request (`"idle"`, `"pending"`, `"success"`, `"error"`). ::note `data`, `error` and `status` are Vue refs accessible with `.value` in `<script setup>` :: By default, Nuxt waits until a `refresh` is finished before it can be executed again. ::note If you have not fetched data on the server (for example, with `server: false`), then the data *will not* be fetched until hydration completes. This means even if you await `useFetch` on client-side, `data` will remain undefined within `<script setup>`. :: ## Options [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) and [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) return the same object type and accept a common set of options as their last argument. They can help you control the composables behavior, such as navigation blocking, caching or execution. ### Lazy By default, data fetching composables will wait for the resolution of their asynchronous function before navigating to a new page by using Vue's Suspense. This feature can be ignored on client-side navigation with the `lazy` option. In that case, you will have to manually handle loading state using the `status` value. ```vue twoslash [app.vue] <script setup lang="ts"> const { status, data: posts } = useFetch('/api/posts', { lazy: true, }) </script> <template> <!-- you will need to handle a loading state --> <div v-if="status === 'pending'"> Loading ... </div> <div v-else> <div v-for="post in posts"> <!-- do something --> </div> </div> </template> ``` You can alternatively use [`useLazyFetch`](https://nuxt.com/docs/3.x/api/composables/use-lazy-fetch) and `useLazyAsyncData` as convenient methods to perform the same. ```vue twoslash <script setup lang="ts"> const { status, data: posts } = useLazyFetch('/api/posts') </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-lazy-fetch"} Read more about `useLazyFetch`. :: ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-lazy-async-data"} Read more about `useLazyAsyncData`. :: ::video-accordion{title="Watch a video from Vue School on blocking vs. non-blocking (lazy) requests" videoId="1022000555" platform="vimeo"} :: ### Client-only fetching By default, data fetching composables will perform their asynchronous function on both client and server environments. Set the `server` option to `false` to only perform the call on the client-side. On initial load, the data will not be fetched before hydration is complete so you have to handle a pending state, though on subsequent client-side navigation the data will be awaited before loading the page. Combined with the `lazy` option, this can be useful for data that is not needed on the first render (for example, non-SEO sensitive data). ```ts twoslash /* This call is performed before hydration */ const articles = await useFetch('/api/article') /* This call will only be performed on the client */ const { status, data: comments } = useFetch('/api/comments', { lazy: true, server: false, }) ``` The `useFetch` composable is meant to be invoked in setup method or called directly at the top level of a function in lifecycle hooks, otherwise you should use [`$fetch` method](https://nuxt.com/docs/3.x/getting-started/data-fetching#fetch). ### Minimize payload size The `pick` option helps you to minimize the payload size stored in your HTML document by only selecting the fields that you want returned from the composables. ```vue <script setup lang="ts"> /* only pick the fields used in your template */ const { data: mountain } = await useFetch('/api/mountains/everest', { pick: ['title', 'description'], }) </script> <template> <h1>{{ mountain.title }}</h1> <p>{{ mountain.description }}</p> </template> ``` If you need more control or map over several objects, you can use the `transform` function to alter the result of the query. ```ts const { data: mountains } = await useFetch('/api/mountains', { transform: (mountains) => { return mountains.map(mountain => ({ title: mountain.title, description: mountain.description })) }, }) ``` ::note Both `pick` and `transform` don't prevent the unwanted data from being fetched initially. But they will prevent unwanted data from being added to the payload transferred from server to client. :: ::video-accordion{title="Watch a video from Vue School on minimizing payload size" videoId="1026410430" platform="vimeo"} :: ### Caching and refetching #### Keys [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) and [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) use keys to prevent refetching the same data. - [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) uses the provided URL as a key. Alternatively, a `key` value can be provided in the `options` object passed as a last argument. - [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data) uses its first argument as a key if it is a string. If the first argument is the handler function that performs the query, then a key that is unique to the file name and line number of the instance of `useAsyncData` will be generated for you. ::tip To get the cached data by key, you can use [`useNuxtData`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-data) :: ::video-accordion{title="Watch a video from Vue School on caching data with the key option" videoId="1026410044" platform="vimeo"} :: #### Shared State and Option Consistency When multiple components use the same key with `useAsyncData` or `useFetch`, they will share the same `data`, `error` and `status` refs. This ensures consistency across components but requires some options to be consistent. The following options **must be consistent** across all calls with the same key: - `handler` function - `deep` option - `transform` function - `pick` array - `getCachedData` function - `default` value ```ts // ❌ This will trigger a development warning const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: false }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { deep: true }) ``` The following options **can safely differ** without triggering warnings: - `server` - `lazy` - `immediate` - `dedupe` - `watch` ```ts // ✅ This is allowed const { data: users1 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: true }) const { data: users2 } = useAsyncData('users', (_nuxtApp, { signal }) => $fetch('/api/users', { signal }), { immediate: false }) ``` If you need independent instances, use different keys: ```ts // These are completely independent instances const { data: users1 } = useAsyncData('users-1', (_nuxtApp, { signal }) => $fetch('/api/users', { signal })) const { data: users2 } = useAsyncData('users-2', (_nuxtApp, { signal }) => $fetch('/api/users', { signal })) ``` #### Reactive Keys You can use computed refs, plain refs or getter functions as keys, allowing for dynamic data fetching that automatically updates when dependencies change: ```ts // Using a computed property as a key const userId = ref('123') const { data: user } = useAsyncData( computed(() => `user-${userId.value}`), () => fetchUser(userId.value), ) // When userId changes, the data will be automatically refetched // and the old data will be cleaned up if no other components use it userId.value = '456' ``` #### Refresh and execute If you want to fetch or refresh data manually, use the `execute` or `refresh` function provided by the composables. ```vue twoslash <script setup lang="ts"> const { data, error, execute, refresh } = await useFetch('/api/users') </script> <template> <div> <p>{{ data }}</p> <button @click="() => refresh()"> Refresh data </button> </div> </template> ``` The `execute` function is an alias for `refresh` that works in exactly the same way but is more semantic for cases when the fetch is [not immediate](https://nuxt.com/docs/3.x/getting-started/data-fetching#not-immediate). ::tip To globally refetch or invalidate cached data, see [`clearNuxtData`](https://nuxt.com/docs/3.x/api/utils/clear-nuxt-data) and [`refreshNuxtData`](https://nuxt.com/docs/3.x/api/utils/refresh-nuxt-data). :: #### Clear If you want to clear the data provided, for whatever reason, without needing to know the specific key to pass to `clearNuxtData`, you can use the `clear` function provided by the composables. ```vue twoslash <script setup lang="ts"> const { data, clear } = await useFetch('/api/users') const route = useRoute() watch(() => route.path, (path) => { if (path === '/') { clear() } }) </script> ``` #### Watch To re-run your fetching function each time other reactive values in your application change, use the `watch` option. You can use it for one or multiple *watchable* elements. ```vue twoslash <script setup lang="ts"> const id = ref(1) const { data, error, refresh } = await useFetch('/api/users', { /* Changing the id will trigger a refetch */ watch: [id], }) </script> ``` Note that **watching a reactive value won't change the URL fetched**. For example, this will keep fetching the same initial ID of the user because the URL is constructed at the moment the function is invoked. ```vue <script setup lang="ts"> const id = ref(1) const { data, error, refresh } = await useFetch(`/api/users/${id.value}`, { watch: [id], }) </script> ``` If you need to change the URL based on a reactive value, you may want to use a [computed URL](https://nuxt.com/docs/3.x/getting-started/data-fetching#computed-url) instead. When reactive fetch options are provided, they'll be automatically watched and trigger refetches. In some cases, it can be useful to opt-out of this behavior by specifying `watch: false`. ```ts const id = ref(1) // Won't automatically refetch when id changes const { data, execute } = await useFetch('/api/users', { query: { id }, // id is watched by default watch: false, // disables automatic watching of id }) // doesn't trigger refetch id.value = 2 ``` #### Computed URL Sometimes you may need to compute a URL from reactive values, and refresh the data each time these change. Instead of juggling your way around, you can attach each param as a reactive value. Nuxt will automatically use the reactive value and re-fetch each time it changes. ```vue <script setup lang="ts"> const id = ref(null) const { data, status } = useLazyFetch('/api/user', { query: { user_id: id, }, }) </script> ``` In the case of more complex URL construction, you may use a callback as a [computed getter](https://vuejs.org/guide/essentials/computed) that returns the URL string. Every time a dependency changes, the data will be fetched using the newly constructed URL. Combine this with [not-immediate](https://nuxt.com/docs/3.x/getting-started/data-fetching#not-immediate), and you can wait until the reactive element changes before fetching. ```vue <script setup lang="ts"> const id = ref(null) const { data, status } = useLazyFetch(() => `/api/users/${id.value}`, { immediate: false, }) const pending = computed(() => status.value === 'pending') </script> <template> <div> <!-- disable the input while fetching --> <input v-model="id" type="number" :disabled="pending" > <div v-if="status === 'idle'"> Type an user ID </div> <div v-else-if="pending"> Loading ... </div> <div v-else> {{ data }} </div> </div> </template> ``` If you need to force a refresh when other reactive values change, you can also [watch other values](https://nuxt.com/docs/3.x/getting-started/data-fetching#watch). ### Not immediate The `useFetch` composable will start fetching data the moment is invoked. You may prevent this by setting `immediate: false`, for example, to wait for user interaction. With that, you will need both the `status` to handle the fetch lifecycle, and `execute` to start the data fetch. ```vue <script setup lang="ts"> const { data, error, execute, status } = await useLazyFetch('/api/comments', { immediate: false, }) </script> <template> <div v-if="status === 'idle'"> <button @click="execute"> Get data </button> </div> <div v-else-if="status === 'pending'"> Loading comments... </div> <div v-else> {{ data }} </div> </template> ``` For finer control, the `status` variable can be: - `idle` when the fetch hasn't started - `pending` when a fetch has started but not yet completed - `error` when the fetch fails - `success` when the fetch is completed successfully ## Passing Headers and Cookies When we call `$fetch` in the browser, user headers like `cookie` will be directly sent to the API. Normally, during server-side-rendering, due to security considerations, the `$fetch` wouldn't include the user's browser cookies, nor pass on cookies from the fetch response. However, when calling `useFetch` with a relative URL on the server, Nuxt will use [`useRequestFetch`](https://nuxt.com/docs/3.x/api/composables/use-request-fetch) to proxy headers and cookies (with the exception of headers not meant to be forwarded, like `host`). ### Pass Cookies From Server-side API Calls on SSR Response If you want to pass on/proxy cookies in the other direction, from an internal request back to the client, you will need to handle this yourself. ```ts [composables/fetch.ts] import { appendResponseHeader } from 'h3' import type { H3Event } from 'h3' export const fetchWithCookie = async (event: H3Event, url: string) => { /* Get the response from the server endpoint */ const res = await $fetch.raw(url) /* Get the cookies from the response */ const cookies = res.headers.getSetCookie() /* Attach each cookie to our incoming Request */ for (const cookie of cookies) { appendResponseHeader(event, 'set-cookie', cookie) } /* Return the data of the response */ return res._data } ``` ```vue <script setup lang="ts"> // This composable will automatically pass cookies to the client const event = useRequestEvent() const { data: result } = await useAsyncData(() => fetchWithCookie(event!, '/api/with-cookie')) onMounted(() => console.log(document.cookie)) </script> ``` ## Options API Support Nuxt provides a way to perform `asyncData` fetching within the Options API. You must wrap your component definition within `defineNuxtComponent` for this to work. ```vue <script> export default defineNuxtComponent({ /* Use the fetchKey option to provide a unique key */ fetchKey: 'hello', async asyncData () { return { hello: await $fetch('/api/hello'), } }, }) </script> ``` ::note Using `<script setup>` or `<script setup lang="ts">` are the recommended way of declaring Vue components in Nuxt. :: ::read-more{to="https://nuxt.com/docs/3.x/api/utils/define-nuxt-component"} :: ## Serializing Data From Server to Client When using `useAsyncData` and `useLazyAsyncData` to transfer data fetched on server to the client (as well as anything else that utilizes [the Nuxt payload](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app#payload)), the payload is serialized with [`devalue`](https://github.com/sveltejs/devalue). This allows us to transfer not just basic JSON but also to serialize and revive/deserialize more advanced kinds of data, such as regular expressions, Dates, Map and Set, `ref`, `reactive`, `shallowRef`, `shallowReactive` and `NuxtError` - and more. It is also possible to define your own serializer/deserializer for types that are not supported by Nuxt. You can read more in the [`useNuxtApp`](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app#payload) docs. ::note Note that this *does not apply* to data passed from your server routes when fetched with `$fetch` or `useFetch` - see the next section for more information. :: ## Serializing Data From API Routes When fetching data from the `server` directory, the response is serialized using `JSON.stringify`. However, since serialization is limited to only JavaScript primitive types, Nuxt does its best to convert the return type of `$fetch` and [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) to match the actual value. ::read-more{icon="i-simple-icons-mdnwebdocs" to="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#description" target="_blank"} Learn more about `JSON.stringify` limitations. :: ### Example ```ts [server/api/foo.ts] export default defineEventHandler(() => { return new Date() }) ``` ```vue [app.vue] <script setup lang="ts"> // Type of `data` is inferred as string even though we returned a Date object const { data } = await useFetch('/api/foo') </script> ``` ### Custom serializer function To customize the serialization behavior, you can define a `toJSON` function on your returned object. If you define a `toJSON` method, Nuxt will respect the return type of the function and will not try to convert the types. ```ts [server/api/bar.ts] export default defineEventHandler(() => { const data = { createdAt: new Date(), toJSON () { return { createdAt: { year: this.createdAt.getFullYear(), month: this.createdAt.getMonth(), day: this.createdAt.getDate(), }, } }, } return data }) ``` ```vue [app.vue] <script setup lang="ts"> // Type of `data` is inferred as // { // createdAt: { // year: number // month: number // day: number // } // } const { data } = await useFetch('/api/bar') </script> ``` ### Using an alternative serializer Nuxt does not currently support an alternative serializer to `JSON.stringify`. However, you can return your payload as a normal string and utilize the `toJSON` method to maintain type safety. In the example below, we use [superjson](https://github.com/flightcontrolhq/superjson) as our serializer. ```ts [server/api/superjson.ts] import superjson from 'superjson' export default defineEventHandler(() => { const data = { createdAt: new Date(), // Workaround the type conversion toJSON () { return this }, } // Serialize the output to string, using superjson return superjson.stringify(data) as unknown as typeof data }) ``` ```vue [app.vue] <script setup lang="ts"> import superjson from 'superjson' // `date` is inferred as { createdAt: Date } and you can safely use the Date object methods const { data } = await useFetch('/api/superjson', { transform: (value) => { return superjson.parse(value as unknown as string) }, }) </script> ``` ## Recipes ### Consuming SSE (Server-Sent Events) via POST request ::tip If you're consuming SSE via GET request, you can use [`EventSource`](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) or VueUse composable [`useEventSource`](https://vueuse.org/core/useeventsource/). :: When consuming SSE via POST request, you need to handle the connection manually. Here's how you can do it: ```ts // Make a POST request to the SSE endpoint const response = await $fetch<ReadableStream>('/chats/ask-ai', { method: 'POST', body: { query: 'Hello AI, how are you?', }, responseType: 'stream', }) // Create a new ReadableStream from the response with TextDecoderStream to get the data as text const reader = response.pipeThrough(new TextDecoderStream()).getReader() // Read the chunk of data as we get it while (true) { const { value, done } = await reader.read() if (done) { break } console.log('Received:', value) } ``` ### Making parallel requests When requests don't rely on each other, you can make them in parallel with `Promise.all()` to boost performance. ```ts const { data } = await useAsyncData((_nuxtApp, { signal }) => { return Promise.all([ $fetch('/api/comments/', { signal }), $fetch('/api/author/12', { signal }), ]) }) const comments = computed(() => data.value?.[0]) const author = computed(() => data.value?.[1]) ``` ::video-accordion{title="Watch a video from Vue School on parallel data fetching" videoId="1024262536" platform="vimeo"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Deployment" description: "Learn how to deploy your Nuxt application to any hosting provider." canonical_url: "https://nuxt.com/docs/3.x/getting-started/deployment" --- # Deployment > Learn how to deploy your Nuxt application to any hosting provider. A Nuxt application can be deployed on a Node.js server, pre-rendered for static hosting, or deployed to serverless or edge (CDN) environments. ::tip If you are looking for a list of cloud providers that support Nuxt, see the [Hosting providers](https://nuxt.com/deploy) section. :: ## Node.js Server Discover the Node.js server preset with Nitro to deploy on any Node hosting. - **Default output format** if none is specified or auto-detected <br> - Loads only the required chunks to render the request for optimal cold start timing <br> - Useful for deploying Nuxt apps to any Node.js hosting ### Entry Point When running `nuxt build` with the Node server preset, the result will be an entry point that launches a ready-to-run Node server. ```bash [Terminal] NODE_ENV=production node .output/server/index.mjs ``` This will launch your production Nuxt server that listens on port 3000 by default. ::important Set `NODE_ENV=production` when running the server. Some dependencies (notably Vue Router) only strip development-only warnings when this is set, so leaving it unset can flood your logs with messages like `[Vue Router warn]: No match found for location with path …` on unmatched routes. :: It respects the following runtime environment variables: - `NITRO_PORT` or `PORT` (defaults to `3000`) - `NITRO_HOST` or `HOST` (defaults to `'0.0.0.0'`) - `NITRO_SSL_CERT` and `NITRO_SSL_KEY` - if both are present, this will launch the server in HTTPS mode. In the vast majority of cases, this should not be used other than for testing, and the Nitro server should be run behind a reverse proxy like nginx or Cloudflare which terminates SSL. ### PM2 [PM2](https://pm2.keymetrics.io/) (Process Manager 2) is a fast and easy solution for hosting your Nuxt application on your server or VM. To use `pm2`, use an `ecosystem.config.cjs`: ```ts [ecosystem.config.cjs] module.exports = { apps: [ { name: 'NuxtAppName', port: '3000', exec_mode: 'cluster', instances: 'max', script: './.output/server/index.mjs', env: { NODE_ENV: 'production', }, }, ], } ``` ### Cluster Mode You can use `NITRO_PRESET=node_cluster` in order to leverage multi-process performance using Node.js [cluster](https://nodejs.org/dist/latest/docs/api/cluster.html) module. By default, the workload gets distributed to the workers with the round robin strategy. ### Learn More ::read-more{to="https://nitro.build/deploy/runtimes/node" title="the Nitro documentation for node-server preset"} :: ::video-accordion{title="Watch Daniel Roe's short video on the topic" videoId="0x1H6K5yOfs"} :: ## Static Hosting There are two ways to deploy a Nuxt application to any static hosting services: - Static site generation (SSG) with `ssr: true` pre-renders routes of your application at build time. (This is the default behavior when running `nuxt generate`.) It will also generate `/200.html` and `/404.html` single-page app fallback pages, which can render dynamic routes or 404 errors on the client (though you may need to configure this on your static host). - Alternatively, you can prerender your site with `ssr: false` (static single-page app). This will produce HTML pages with an empty `<div id="__nuxt"></div>` where your Vue app would normally be rendered. You will lose many SEO benefits of prerendering your site, so it is suggested instead to use [`<ClientOnly>`](https://nuxt.com/docs/3.x/api/components/client-only) to wrap the portions of your site that cannot be server rendered (if any). ::read-more{title="Nuxt prerendering" to="https://nuxt.com/docs/3.x/getting-started/prerendering"} :: ### Client-side Only Rendering If you don't want to pre-render your routes, another way of using static hosting is to set the `ssr` property to `false` in the `nuxt.config` file. The `nuxt generate` command will then output an `.output/public/index.html` entrypoint and JavaScript bundles like a classic client-side Vue.js application. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ ssr: false, }) ``` ## Hosting Providers Nuxt can be deployed to several cloud providers with a minimal amount of configuration: ::read-more{to="https://nuxt.com/deploy"} :: ## Presets In addition to Node.js servers and static hosting services, a Nuxt project can be deployed with several well-tested presets and minimal amount of configuration. You can explicitly set the desired preset in the [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) file: ```ts twoslash [nuxt.config.ts] // @errors: 2353 export default defineNuxtConfig({ nitro: { preset: 'node-server', }, }) ``` ... or use the `NITRO_PRESET` environment variable when running `nuxt build`: ```bash [Terminal] NITRO_PRESET=node-server nuxt build ``` 🔎 Check [the Nitro deployment](https://nitro.build/deploy) for all possible deployment presets and providers. ## CDN Proxy In most cases, Nuxt can work with third-party content that is not generated or created by Nuxt itself. But sometimes such content can cause problems, especially Cloudflare's "Minification and Security Options". Accordingly, you should make sure that the following options are unchecked / disabled in Cloudflare. Otherwise, unnecessary re-rendering or hydration errors could impact your production application. 1. Speed > Settings > Content Optimization > Disable "Rocket Loader™" 2. Security > Settings > Disable "Email Address Obfuscation" With these settings, you can be sure that Cloudflare won't inject scripts into your Nuxt application that may cause unwanted side effects. ::tip Their location on the Cloudflare dashboard sometimes changes so don't hesitate to look around. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Error Handling" description: "Learn how to catch and handle errors in Nuxt." canonical_url: "https://nuxt.com/docs/3.x/getting-started/error-handling" --- # Error Handling > Learn how to catch and handle errors in Nuxt. Nuxt is a full-stack framework, which means there are several sources of unpreventable user runtime errors that can happen in different contexts: - Errors during the Vue rendering lifecycle (SSR & CSR) - Server and client startup errors (SSR + CSR) - Errors during Nitro server lifecycle ([`server/`](https://nuxt.com/docs/3.x/directory-structure/server) directory) - Errors downloading JS chunks ::tip **SSR** stands for **Server-Side Rendering** and **CSR** for **Client-Side Rendering**. :: ## Vue Errors You can hook into Vue errors using [`onErrorCaptured`](https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured). In addition, Nuxt provides a [`vue:error`](https://nuxt.com/docs/3.x/api/advanced/hooks#app-hooks-runtime) hook that will be called if any errors propagate up to the top level. If you are using an error reporting framework, you can provide a global handler through [`vueApp.config.errorHandler`](https://vuejs.org/api/application#app-config-errorhandler). It will receive all Vue errors, even if they are handled. ```ts twoslash [plugins/error-handler.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.vueApp.config.errorHandler = (error, instance, info) => { // handle error, e.g. report to a service } // Also possible nuxtApp.hook('vue:error', (error, instance, info) => { // handle error, e.g. report to a service }) }) ``` ::note Note that the `vue:error` hook is based on [`onErrorCaptured`](https://vuejs.org/api/composition-api-lifecycle#onerrorcaptured) lifecycle hook. :: ## Startup Errors Nuxt will call the `app:error` hook if there are any errors in starting your Nuxt application. This includes: - running [Nuxt plugins](https://nuxt.com/docs/3.x/directory-structure/plugins) - processing `app:created` and `app:beforeMount` hooks - rendering your Vue app to HTML (during SSR) - mounting the app (on client-side), though you should handle this case with `onErrorCaptured` or with `vue:error` - processing the `app:mounted` hook ## Nitro Server Errors You cannot currently define a server-side handler for these errors, but can render an error page, see the [Render an Error Page](https://nuxt.com/docs/3.x/getting-started/error-handling#error-page) section. ## Errors with JS Chunks You might encounter chunk loading errors due to a network connectivity failure or a new deployment (which invalidates your old, hashed JS chunk URLs). Nuxt provides built-in support for handling chunk loading errors by performing a hard reload when a chunk fails to load during route navigation. You can change this behavior by setting `experimental.emitRouteChunkError` to `false` (to disable hooking into these errors at all) or to `manual` if you want to handle them yourself. If you want to handle chunk loading errors manually, you can check out the [the automatic implementation](https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/plugins/chunk-reload.client.ts) for ideas. ## Error Page ::note When Nuxt encounters a fatal error (any unhandled error on the server, or an error created with `fatal: true` on the client) it will either render a JSON response (if requested with `Accept: application/json` header) or trigger a full-screen error page. :: An error may occur during the server lifecycle when: - processing your Nuxt plugins - rendering your Vue app into HTML - a server API route throws an error It can also occur on the client side when: - processing your Nuxt plugins - before mounting the application (`app:beforeMount` hook) - mounting your app if the error was not handled with `onErrorCaptured` or `vue:error` hook - the Vue app is initialized and mounted in browser (`app:mounted`). ::read-more{to="https://nuxt.com/docs/3.x/api/advanced/hooks"} Discover all the Nuxt lifecycle hooks. :: Customize the default error page by adding `~/error.vue` in the source directory of your application, alongside `app.vue`. <!-- TODO:twoslash: Twoslash does not support tsconfig paths yet --> ```vue [error.vue] <script setup lang="ts"> import type { NuxtError } from '#app' const props = defineProps({ error: Object as () => NuxtError, }) const handleError = () => clearError({ redirect: '/' }) </script> <template> <div> <h2>{{ error?.status }}</h2> <button @click="handleError"> Clear errors </button> </div> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/error"} Read more about `error.vue` and its uses. :: For custom errors we highly recommend using `onErrorCaptured` composable that can be called in a page/component setup function or `vue:error` runtime nuxt hook that can be configured in a nuxt plugin. ```ts twoslash [plugins/error-handler.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('vue:error', (err) => { // }) }) ``` When you are ready to remove the error page, you can call the [`clearError`](https://nuxt.com/docs/3.x/api/utils/clear-error) helper function, which takes an optional path to redirect to (for example, if you want to navigate to a 'safe' page). ::important Make sure to check before using anything dependent on Nuxt plugins, such as `$route` or `useRouter`, as if a plugin threw an error, then it won't be re-run until you clear the error. :: ::note Rendering an error page is an entirely separate page load, meaning any registered middleware will run again. You can use [`useError`](https://nuxt.com/docs/3.x/getting-started/error-handling#useerror) in middleware to check if an error is being handled. :: ::note If you are running on Node 16 and you set any cookies when rendering your error page, they will [overwrite cookies previously set](https://github.com/nuxt/nuxt/pull/20585). We recommend using a newer version of Node as Node 16 reached end-of-life in September 2023. :: ## Error Utils ### `useError` ```ts [TS Signature] function useError (): Ref<Error | { url, status, statusText, message, description, data }> ``` This function will return the global Nuxt error that is being handled. ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-error"} Read more about `useError` composable. :: ### `createError` ```ts [TS Signature] function createError (err: string | { cause, data, message, name, stack, status, statusText, fatal }): Error ``` Create an error object with additional metadata. You can pass a string to be set as the error `message` or an object containing error properties. It is usable in both the Vue and Server portions of your app, and is meant to be thrown. If you throw an error created with `createError`: - on server-side, it will trigger a full-screen error page which you can clear with [`clearError`](https://nuxt.com/docs/3.x/getting-started/error-handling#clearerror). - on client-side, it will throw a non-fatal error for you to handle. If you need to trigger a full-screen error page, then you can do this by setting `fatal: true`. ```vue twoslash [pages/movies/[slug\\].vue] <script setup lang="ts"> const route = useRoute() const { data } = await useFetch(`/api/movies/${route.params.slug}`) if (!data.value) { throw createError({ status: 404, statusText: 'Page Not Found', }) } </script> ``` ::tip The `statusText` property is intended for short, HTTP-compliant status texts (e.g., "Not Found"). It should only contain horizontal tabs, spaces, and visible ASCII characters (`[\t\u0020-\u007E]`). For any detailed descriptions, multi-line messages, or content with non-ASCII characters, you should always use the `message` property instead. :: ::read-more{to="https://nuxt.com/docs/3.x/api/utils/create-error"} Read more about `createError` util. :: ### `showError` ```ts [TS Signature] function showError (err: string | Error | { status, statusText }): Error ``` You can call this function at any point on client-side, or (on server side) directly within middleware, plugins or `setup()` functions. It will trigger a full-screen error page which you can clear with [`clearError`](https://nuxt.com/docs/3.x/getting-started/error-handling#clearerror). It is recommended instead to use `throw createError()`. ::read-more{to="https://nuxt.com/docs/3.x/api/utils/show-error"} Read more about `showError` util. :: ### `clearError` ```ts [TS Signature] function clearError (options?: { redirect?: string }): Promise<void> ``` This function will clear the currently handled Nuxt error. It also takes an optional path to redirect to (for example, if you want to navigate to a 'safe' page). ::read-more{to="https://nuxt.com/docs/3.x/api/utils/clear-error"} Read more about `clearError` util. :: ## Render Error in Component Nuxt also provides a [`<NuxtErrorBoundary>`](https://nuxt.com/docs/3.x/api/components/nuxt-error-boundary) component that allows you to handle client-side errors within your app, without replacing your entire site with an error page. This component is responsible for handling errors that occur within its default slot. On client-side, it will prevent the error from bubbling up to the top level, and will render the `#error` slot instead. The `#error` slot will receive `error` as a prop. (If you set `error = null` it will trigger re-rendering the default slot; you'll need to ensure that the error is fully resolved first or the error slot will just be rendered a second time.) ::tip If you navigate to another route, the error will be cleared automatically. :: ```vue [pages/index.vue] <template> <!-- some content --> <NuxtErrorBoundary @error="someErrorLogger"> <!-- You use the default slot to render your content --> <template #error="{ error, clearError }"> You can display the error locally here: {{ error }} <button @click="clearError"> This will clear the error. </button> </template> </NuxtErrorBoundary> </template> ``` ::link-example{to="https://nuxt.com/docs/examples/advanced/error-handling"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Installation" description: "Get started with Nuxt quickly with our online starters or start locally with your terminal." canonical_url: "https://nuxt.com/docs/3.x/getting-started/installation" --- # Installation > Get started with Nuxt quickly with our online starters or start locally with your terminal. ## Play Online If you just want to play around with Nuxt in your browser without setting up a project, you can use this online sandbox: ::card-group :::card ```yaml [props] title: Open on StackBlitz color: info icon: i-simple-icons-stackblitz to: https://stackblitz.com/github/nuxt/starter/tree/v3?file=app.vue target: _blank ``` Online playground with code editor and terminal access. ::: :: ## New Project ::steps ### Prerequisites - **Node.js** - [`22.x`](https://nodejs.org/en) or newer (but we recommend the [active LTS release](https://github.com/nodejs/release#release-schedule)) - **Text editor** - There is no IDE requirement, but we recommend [Visual Studio Code](https://code.visualstudio.com/) with the [official Vue extension](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (previously known as Volar) or [WebStorm](https://www.jetbrains.com/webstorm/), which, along with [other JetBrains IDEs](https://www.jetbrains.com/ides/), offers great Nuxt support right out-of-the-box. If you use another editor, such as Neovim, you can configure [Vue Language Server](https://github.com/vuejs/language-tools) support by following the [Vue Language Tools setup guides](https://github.com/vuejs/language-tools/wiki). - **Terminal** - In order to run Nuxt commands :::callout ::::collapsible{name="additional notes for an optimal setup"} - **Node.js**: Make sure to use an even numbered version (22, 24, etc.) - **Neovim**: When configuring the Vue TypeScript plugin, make sure `location` points to the `@vue/language-server` package directory, not its binary. See the [Neovim setup guide](https://github.com/vuejs/language-tools/wiki/Neovim) for a working configuration. - **WSL**: If you are using Windows and experience slow HMR, you may want to try using [WSL (Windows Subsystem for Linux)](https://learn.microsoft.com/en-us/windows/wsl/install) which may solve some performance issues. - **Windows slow DNS resolution**: Instead of using `localhost:3000` for local dev server on Windows, use `127.0.0.1` for much faster loading experience on browsers. :::: ::: ### Create a New Project Open a terminal (if you're using [Visual Studio Code](https://code.visualstudio.com), you can open an [integrated terminal](https://code.visualstudio.com/docs/terminal/basics)) and use the following command to create a new starter project: :::code-group{sync="pm"} ```bash [npm] npm create nuxt@latest <project-name> -- -t v3 ``` ```bash [yarn] yarn create nuxt <project-name> -t v3 ``` ```bash [pnpm] pnpm create nuxt@latest <project-name> -t v3 ``` ```bash [bun] # you can use this when https://github.com/oven-sh/bun/issues/29087 is resolved # bun create nuxt@latest <project-name> -t v3 bunx create-nuxt@latest init <project-name> -t v3 ``` ```bash [deno] deno -A npm:create-nuxt@latest <project-name> -t v3 ``` ::: Open your project folder in Visual Studio Code: ```bash [Terminal] code <project-name> ``` Or change directory into your new project from your terminal: ```bash cd <project-name> ``` ### Development Server Now you'll be able to start your Nuxt app in development mode: :::code-group{sync="pm"} ```bash [npm] npm run dev -- -o ``` ```bash [yarn] yarn dev --open ``` ```bash [pnpm] pnpm dev -o ``` ```bash [bun] bun run dev -o # To use the Bun runtime during development # bun --bun run dev -o ``` ```bash [deno] deno run dev -o ``` ::: :::tip{icon="i-lucide-circle-check"} Well done! A browser window should automatically open for <http://localhost:3000>. ::: :: ## Next Steps Now that you've created your Nuxt project, you are ready to start building your application. ::read-more{title="Nuxt Concepts" to="https://nuxt.com/docs/3.x/guide/concepts"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Introduction" description: "Nuxt's goal is to make web development intuitive and performant with a great Developer Experience in mind." canonical_url: "https://nuxt.com/docs/3.x/getting-started/introduction" --- # Introduction > Nuxt's goal is to make web development intuitive and performant with a great Developer Experience in mind. Nuxt is a free and [open-source framework](https://github.com/nuxt/nuxt) with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with [Vue.js](https://vuejs.org). We made everything so you can start writing `.vue` files from the beginning while enjoying hot module replacement in development and a performant application in production with server-side rendering by default. Nuxt has no vendor lock-in, allowing you to deploy your application [**everywhere, even on the edge**](https://nuxt.com/blog/nuxt-on-the-edge). ::tip If you want to play around with Nuxt in your browser, you can [try it out in one of our online sandboxes](https://nuxt.com/docs/3.x/getting-started/installation#play-online). :: ## Automation and Conventions Nuxt uses conventions and an opinionated directory structure to automate repetitive tasks and allow developers to focus on pushing features. The configuration file can still customize and override its default behaviors. - **File-based routing:** define routes based on the structure of your [`pages/` directory](https://nuxt.com/docs/3.x/directory-structure/pages). This can make it easier to organize your application and avoid the need for manual route configuration. - **Code splitting:** Nuxt automatically splits your code into smaller chunks, which can help reduce the initial load time of your application. - **Server-side rendering out of the box:** Nuxt comes with built-in SSR capabilities, so you don't have to set up a separate server yourself. - **Auto-imports:** write Vue composables and components in their respective directories and use them without having to import them with the benefits of tree-shaking and optimized JS bundles. - **Data-fetching utilities:** Nuxt provides composables to handle SSR-compatible data fetching as well as different strategies. - **Zero-config TypeScript support:** write type-safe code without having to learn TypeScript with our auto-generated types and `tsconfig.json`. - **Configured build tools:** we use [Vite](https://vite.dev) by default to support hot module replacement (HMR) in development and bundling your code for production with best-practices baked-in. Nuxt takes care of these and provides both frontend and backend functionality so you can focus on what matters: **creating your web application**. ## Server-Side Rendering Nuxt comes with built-in server-side rendering (SSR) capabilities by default, without having to configure a server yourself, which has many benefits for web applications: - **Faster initial page load time:** Nuxt sends a fully rendered HTML page to the browser, which can be displayed immediately. This can provide a faster perceived page load time and a better user experience (UX), especially on slower networks or devices. - **Improved SEO:** search engines can better index SSR pages because the HTML content is available immediately, rather than requiring JavaScript to render the content on the client-side. - **Better performance on low-powered devices:** it reduces the amount of JavaScript that needs to be downloaded and executed on the client-side, which can be beneficial for low-powered devices that may struggle with processing heavy JavaScript applications. - **Better accessibility:** the content is immediately available on the initial page load, improving accessibility for users who rely on screen readers or other assistive technologies. - **Easier caching:** pages can be cached on the server-side, which can further improve performance by reducing the amount of time it takes to generate and send the content to the client. Overall, server-side rendering can provide a faster and more efficient user experience, as well as improve search engine optimization and accessibility. As Nuxt is a versatile framework, it gives you the possibility to statically render your whole application to a static hosting with `nuxt generate`, disable SSR globally with the `ssr: false` option or leverage hybrid rendering by setting up the `routeRules` option. ::read-more{title="Nuxt rendering modes" to="https://nuxt.com/docs/3.x/guide/concepts/rendering"} :: ### Server engine The Nuxt server engine [Nitro](https://nitro.build/) unlocks new full-stack capabilities. In development, it uses Rollup and Node.js workers for your server code and context isolation. It also generates your server API by reading files in `server/api/` and server middleware from `server/middleware/`. In production, Nitro builds your app and server into one universal `.output` directory. This output is light: minified and removed from any Node.js modules (except polyfills). You can deploy this output on any system supporting JavaScript, from Node.js, Serverless, Workers, Edge-side rendering or purely static. ::read-more{title="Nuxt server engine" to="https://nuxt.com/docs/3.x/guide/concepts/server-engine"} :: ### Production-ready A Nuxt application can be deployed on a Node or Deno server, pre-rendered to be hosted in static environments, or deployed to serverless and edge providers. ::read-more{title="Deployment section" to="https://nuxt.com/docs/3.x/getting-started/deployment"} :: ### Modular A module system allows you to extend Nuxt with custom features and integrations with third-party services. ::read-more{title="Nuxt Modules Concept" to="https://nuxt.com/docs/3.x/guide/concepts/modules"} :: ### Architecture Nuxt is composed of different [core packages](https://github.com/nuxt/nuxt/tree/main/packages): - Core engine: [nuxt](https://github.com/nuxt/nuxt/tree/main/packages/nuxt) - Bundlers: [@nuxt/vite-builder](https://github.com/nuxt/nuxt/tree/main/packages/vite), [@nuxt/rspack-builder](https://github.com/nuxt/nuxt/tree/main/packages/rspack) and [@nuxt/webpack-builder](https://github.com/nuxt/nuxt/tree/main/packages/webpack) - Command line interface: [@nuxt/cli](https://github.com/nuxt/cli) - Server engine: [nitro](https://github.com/nitrojs/nitro) - Development kit: [@nuxt/kit](https://github.com/nuxt/nuxt/tree/main/packages/kit) We recommend reading each concept to have a full vision of Nuxt capabilities and the scope of each package. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Layers" description: "Nuxt provides a powerful system that allows you to extend the default files, configs, and much more." canonical_url: "https://nuxt.com/docs/3.x/getting-started/layers" --- # Layers > Nuxt provides a powerful system that allows you to extend the default files, configs, and much more. One of the core features of Nuxt is the layers and extending support. You can extend a default Nuxt application to reuse components, utils, and configuration. The layers structure is almost identical to a standard Nuxt application which makes them easy to author and maintain. ## Use Cases - Share reusable configuration presets across projects using `nuxt.config` and `app.config` - Create a component library using [`components/`](https://nuxt.com/docs/3.x/directory-structure/components) directory - Create utility and composable library using [`composables/`](https://nuxt.com/docs/3.x/directory-structure/composables) and [`utils/`](https://nuxt.com/docs/3.x/directory-structure/utils) directories - Create Nuxt module presets - Share standard setup across projects - Create Nuxt themes - Enhance code organization by implementing a modular architecture and support Domain-Driven Design (DDD) pattern in large scale projects. ## Usage By default, any layers within your project in the `~~/layers` directory will be automatically registered as layers in your project. ::note Layer auto-registration was introduced in Nuxt v3.12.0. :: In addition, named layer aliases to the `srcDir` of each of these layers will automatically be created. For example, you will be able to access the `~~/layers/test` layer via `#layers/test`. ::note Named layer aliases were introduced in Nuxt v3.16.0. :: In addition, you can extend from a layer by adding the [extends](https://nuxt.com/docs/3.x/api/nuxt-config#extends) property to your [`nuxt.config`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ // Extend from a local layer '../base', // Extend from an installed npm package '@my-themes/awesome', // Extend from a git repository 'github:my-themes/awesome#v1', ], }) ``` You can also pass an authentication token if you are extending from a private GitHub repository: ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ // per layer configuration ['github:my-themes/private-awesome', { auth: process.env.GITHUB_TOKEN }], ], }) ``` ::note If a branch is not specified, this will clone `main`. :: ::tip You can override a layer's alias by specifying it in the options next to the layer source. ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ [ 'github:my-themes/awesome', { meta: { name: 'my-awesome-theme', }, }, ], ], }) ``` :: Nuxt uses [unjs/c12](https://github.com/unjs/c12) and [unjs/giget](https://github.com/unjs/giget) for extending remote layers. Check the documentation for more information and all available options. ## Layer Priority When using multiple layers, it's important to understand the override order. Layers with **higher priority** override layers with lower priority when they define the same files or components. ### Priority Order From highest to lowest priority: 1. **Your project files** - always have the highest priority 2. **Auto-scanned layers** from `~~/layers` directory - sorted alphabetically (Z has higher priority than A) 3. **Layers in `extends`** config - first entry has higher priority than second ### Practical Example Consider multiple layers defining the same component: ```bash [Directory structure] layers/ 1.base/ components/Button.vue # Base button style 2.theme/ components/Button.vue # Themed button (overrides base) app/ components/Button.vue # Project button (overrides all layers) ``` In this case: - If only layers exist, `2.theme/Button.vue` is used (higher alphabetically) - If `components/Button.vue` exists in your project, it overrides all layers ### Controlling Priority You can prefix layer directories with numbers to control the order: ```bash [Directory structure] layers/ 1.base/ # Lowest priority 2.features/ # Medium priority 3.admin/ # Highest priority (among layers) ``` ::tip This pattern is useful for creating base layers with defaults that can be progressively overridden by more specific layers. :: ### When to Use Each - **`~~/layers` directory** - Use for local layers that are part of your project - **`extends`** - Use for external dependencies (npm packages, remote repositories) or layers outside your project directory ### Full Example with extends ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: [ '../base', // Local layer outside project '@my-themes/awesome', // NPM package 'github:my-themes/awesome#v1', // Remote repository ], }) ``` If you also have `~~/layers/custom`, the priority order is: - Your project files (highest) - `~~/layers/custom` - `../base` - `@my-themes/awesome` - `github:my-themes/awesome#v1` (lowest) ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/layers"} Learn about the **layers/ directory** to organize and share reusable code, components, composables, and configurations across your Nuxt application. :: ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/layers"} Read more about layers in the **Layer Author Guide**. :: ::video-accordion{title="Watch a video from Learn Vue about Nuxt Layers" videoId="lnFCM7c9f7I"} :: ::video-accordion{title="Watch a video from Alexander Lichter about Nuxt Layers" videoId="fr5yo3aVkfA"} :: ## Examples ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: Content Wind to: https://github.com/Atinux/content-wind target: _blank ``` A lightweight Nuxt theme to build a Markdown driven website. Powered by Nuxt Content, TailwindCSS and Iconify. ::: :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Prerendering" description: "Nuxt allows pages to be statically rendered at build time to improve certain performance or SEO metrics" canonical_url: "https://nuxt.com/docs/3.x/getting-started/prerendering" --- # Prerendering > Nuxt allows pages to be statically rendered at build time to improve certain performance or SEO metrics Nuxt allows for select pages from your application to be rendered at build time. Nuxt will serve the prebuilt pages when requested instead of generating them on the fly. ::read-more{title="Nuxt rendering modes" to="https://nuxt.com/docs/3.x/guide/concepts/rendering"} :: ## Crawl-based Pre-rendering Use the [`nuxt generate` command](https://nuxt.com/docs/3.x/api/commands/generate) to build and pre-render your application using the [Nitro](https://nuxt.com/docs/3.x/guide/concepts/server-engine) crawler. This command is similar to `nuxt build` with the `nitro.static` option set to `true`, or running `nuxt build --prerender`. This will build your site, stand up a nuxt instance, and, by default, prerender the root page `/` along with any of your site's pages it links to, any of your site's pages they link to, and so on. ::code-group{sync="pm"} ```bash [npm] npx nuxt generate ``` ```bash [yarn] yarn nuxt generate ``` ```bash [pnpm] pnpm nuxt generate ``` ```bash [bun] bun x nuxt generate ``` ```bash [deno] deno x nuxt generate ``` :: You can now deploy the `.output/public` directory to any static hosting service or preview it locally with `npx serve .output/public`. Working of the Nitro crawler: 1. Load the HTML of your application's root route (`/`), any non-dynamic pages in your `~/pages` directory, and any other routes in the `nitro.prerender.routes` array. 2. Save the HTML and `payload.json` to the `~/.output/public/` directory to be served statically. 3. Find all anchor tags (`<a href="...">`) in the HTML to navigate to other routes. 4. Repeat steps 1-3 for each anchor tag found until there are no more anchor tags to crawl. This is important to understand since pages that are not linked to a discoverable page can't be pre-rendered automatically. ### Payload Extraction Nuxt generates `_payload.json` alongside HTML for: - Prerendered routes (at build time) - ISR/SWR routes (on first request) Payloads contain serialized data from `useAsyncData` and `useFetch`. Client-side navigation loads these cached payloads instead of re-fetching data. Configure dynamic routes like `pages/[...slug].vue` with route rules: `'/**': { isr: true }`. ::read-more{to="https://nuxt.com/docs/3.x/api/commands/generate"} Read more about the `nuxt generate` command. :: ### Selective Pre-rendering You can manually specify routes that [Nitro](https://nuxt.com/docs/3.x/guide/concepts/server-engine) will fetch and pre-render during the build or ignore routes that you don't want to pre-render like `/dynamic` in the `nuxt.config` file: ```ts twoslash [nuxt.config.ts] // @errors: 2353 export default defineNuxtConfig({ nitro: { prerender: { routes: ['/user/1', '/user/2'], ignore: ['/dynamic'], }, }, }) ``` You can combine this with the `crawlLinks` option to pre-render a set of routes that the crawler can't discover like your `/sitemap.xml` or `/robots.txt`: ```ts twoslash [nuxt.config.ts] // @errors: 2353 export default defineNuxtConfig({ nitro: { prerender: { crawlLinks: true, routes: ['/sitemap.xml', '/robots.txt'], }, }, }) ``` Setting `nitro.prerender` to `true` is similar to `nitro.prerender.crawlLinks` to `true`. ::read-more{to="https://nitro.build/config#prerender"} Read more about pre-rendering in the Nitro documentation. :: Lastly, you can manually configure this using routeRules. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Set prerender to true to configure it to be prerendered '/rss.xml': { prerender: true }, // Set it to false to configure it to be skipped for prerendering '/this-DOES-NOT-get-prerendered': { prerender: false }, // Everything under /blog gets prerendered as long as it // is linked to from another page '/blog/**': { prerender: true }, }, }) ``` ::read-more{to="https://nitro.build/config#routerules"} Read more about Nitro's `routeRules` configuration. :: As a shorthand, you can also configure this in a page file using [`defineRouteRules`](https://nuxt.com/docs/3.x/api/utils/define-route-rules). ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/experimental-features#inlinerouterules" icon="i-lucide-star"} This feature is experimental and in order to use it you must enable the `experimental.inlineRouteRules` option in your `nuxt.config`. :: ```vue [pages/index.vue] <script setup> // Or set at the page level defineRouteRules({ prerender: true, }) </script> <template> <div> <h1>Homepage</h1> <p>Pre-rendered at build time</p> </div> </template> ``` This will be translated to: ```ts [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { '/': { prerender: true }, }, }) ``` ## Runtime Prerender Configuration ### `prerenderRoutes` You can use this at runtime within a [Nuxt context](https://nuxt.com/docs/3.x/guide/going-further/nuxt-app#the-nuxt-context) to add more routes for Nitro to prerender. ```vue [pages/index.vue] <script setup> prerenderRoutes(['/some/other/url']) prerenderRoutes('/api/content/article/my-article') </script> <template> <div> <h1>This will register other routes for prerendering when prerendered</h1> </div> </template> ``` ::read-more{title="prerenderRoutes" to="https://nuxt.com/docs/3.x/api/utils/prerender-routes"} :: ### `prerender:routes` Nuxt hook This is called before prerendering for additional routes to be registered. ```ts [nuxt.config.ts] export default defineNuxtConfig({ hooks: { async 'prerender:routes' (ctx) { const { pages } = await fetch('https://api.some-cms.com/pages').then( res => res.json(), ) for (const page of pages) { ctx.routes.add(`/${page.name}`) } }, }, }) ``` ### `prerender:generate` Nitro hook This is called for each route during prerendering. You can use this for fine-grained handling of each route that gets prerendered. ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { hooks: { 'prerender:generate' (route) { if (route.route?.includes('private')) { route.skip = true } }, }, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Routing" description: "Nuxt file-system routing creates a route for every file in the pages/ directory." canonical_url: "https://nuxt.com/docs/3.x/getting-started/routing" --- # Routing > Nuxt file-system routing creates a route for every file in the pages/ directory. One core feature of Nuxt is the file system router. Every Vue file inside the [`pages/`](https://nuxt.com/docs/3.x/directory-structure/pages) directory creates a corresponding URL (or route) that displays the contents of the file. By using dynamic imports for each page, Nuxt leverages code-splitting to ship the minimum amount of JavaScript for the requested route. ## Pages Nuxt routing is based on [vue-router](https://router.vuejs.org) and generates the routes from every component created in the [`pages/` directory](https://nuxt.com/docs/3.x/directory-structure/pages), based on their filename. This file system routing uses naming conventions to create dynamic and nested routes: ::code-group ```bash [Directory Structure] -| pages/ ---| about.vue ---| index.vue ---| posts/ -----| [id].vue ``` ```json [Generated Router File] { "routes": [ { "path": "/about", "component": "pages/about.vue" }, { "path": "/", "component": "pages/index.vue" }, { "path": "/posts/:id", "component": "pages/posts/[id].vue" } ] } ``` :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/pages"} :: ## Navigation The [`<NuxtLink>`](https://nuxt.com/docs/3.x/api/components/nuxt-link) component links pages between them. It renders an `<a>` tag with the `href` attribute set to the route of the page. Once the application is hydrated, page transitions are performed in JavaScript by updating the browser URL. This prevents full-page refreshes and allows for animated transitions. When a [`<NuxtLink>`](https://nuxt.com/docs/3.x/api/components/nuxt-link) enters the viewport on the client side, Nuxt will automatically prefetch components and payload (generated pages) of the linked pages ahead of time, resulting in faster navigation. ```vue [pages/index.vue] <template> <header> <nav> <ul> <li><NuxtLink to="/about">About</NuxtLink></li> <li><NuxtLink to="/posts/1">Post 1</NuxtLink></li> <li><NuxtLink to="/posts/2">Post 2</NuxtLink></li> </ul> </nav> </header> </template> ``` ::read-more{to="https://nuxt.com/docs/3.x/api/components/nuxt-link"} :: ## Route Parameters The [`useRoute()`](https://nuxt.com/docs/3.x/api/composables/use-route) composable can be used in a `<script setup>` block or a `setup()` method of a Vue component to access the current route details. ```vue twoslash [pages/posts/[id\\].vue] <script setup lang="ts"> const route = useRoute() // When accessing /posts/1, route.params.id will be 1 console.log(route.params.id) </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-route"} :: ## Route Middleware Nuxt provides a customizable route middleware framework you can use throughout your application, ideal for extracting code that you want to run before navigating to a particular route. ::note Route middleware runs within the Vue part of your Nuxt app. Despite the similar name, they are completely different from server middleware, which are run in the Nitro server part of your app. :: ::important Route middleware does **not** run for server routes (e.g. `/api/*`) or other server requests. To apply middleware to these requests, use [server middleware](https://nuxt.com/docs/3.x/directory-structure/server#server-middleware) instead. :: There are three kinds of route middleware: 1. Anonymous (or inline) route middleware, which are defined directly in the pages where they are used. 2. Named route middleware, which are placed in the [`middleware/`](https://nuxt.com/docs/3.x/directory-structure/middleware) directory and will be automatically loaded via asynchronous import when used on a page. (**Note**: The route middleware name is normalized to kebab-case, so `someMiddleware` becomes `some-middleware`.) 3. Global route middleware, which are placed in the [`middleware/`](https://nuxt.com/docs/3.x/directory-structure/middleware) directory (with a `.global` suffix) and will be automatically run on every route change. Example of an `auth` middleware protecting the `/dashboard` page: ::code-group ```ts twoslash [middleware/auth.ts] function isAuthenticated (): boolean { return false } // ---cut--- export default defineNuxtRouteMiddleware((to, from) => { // isAuthenticated() is an example method verifying if a user is authenticated if (isAuthenticated() === false) { return navigateTo('/login') } }) ``` ```vue twoslash [pages/dashboard.vue] <script setup lang="ts"> definePageMeta({ middleware: 'auth', }) </script> <template> <h1>Welcome to your dashboard</h1> </template> ``` :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/middleware"} :: ## Route Validation Nuxt offers route validation via the `validate` property in [`definePageMeta()`](https://nuxt.com/docs/3.x/api/utils/define-page-meta) in each page you wish to validate. The `validate` property accepts the `route` as an argument. You can return a boolean value to determine whether or not this is a valid route to be rendered with this page. If you return `false`, this will cause a 404 error. You can also directly return an object with `status`/`statusText` to customize the error returned. If you have a more complex use case, then you can use anonymous route middleware instead. ```vue twoslash [pages/posts/[id\\].vue] <script setup lang="ts"> definePageMeta({ validate (route) { // Check if the id is made up of digits return typeof route.params.id === 'string' && /^\d+$/.test(route.params.id) }, }) </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/api/utils/define-page-meta"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "SEO and Meta" description: "Improve your Nuxt app's SEO with powerful head config, composables and components." canonical_url: "https://nuxt.com/docs/3.x/getting-started/seo-meta" --- # SEO and Meta > Improve your Nuxt app's SEO with powerful head config, composables and components. Nuxt head tag management is powered by [Unhead](https://unhead.unjs.io). It provides sensible defaults, several powerful composables and numerous configuration options to manage your app's head and SEO meta tags. ## Nuxt Config Providing an [`app.head`](https://nuxt.com/docs/3.x/api/nuxt-config#head) property in your [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) allows you to statically customize the head for your entire app. ::important This method does not allow you to provide reactive data. We recommend using `useHead()` in `app.vue`. :: It's good practice to set tags here that won't change such as your site title default, language and favicon. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { title: 'Nuxt', // default fallback title htmlAttrs: { lang: 'en', }, link: [ { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }, ], }, }, }) ``` You can also provide any of the keys listed below in [Types](https://nuxt.com/docs/3.x/getting-started/seo-meta#types). ### Defaults Tags Some tags are provided by Nuxt by default to ensure your website works well out of the box. - `viewport`: `width=device-width, initial-scale=1` - `charset`: `utf-8` While most sites won't need to override these defaults, you can update them using the keyed shortcuts. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { // update Nuxt defaults charset: 'utf-16', viewport: 'width=device-width, initial-scale=1, maximum-scale=1', }, }, }) ``` ## `useHead` The [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) composable function supports reactive input, allowing you to manage your head tags programmatically. ```vue twoslash [app.vue] <script setup lang="ts"> useHead({ title: 'My App', meta: [ { name: 'description', content: 'My amazing site.' }, ], bodyAttrs: { class: 'test', }, script: [{ innerHTML: 'console.log(\'Hello world\')' }], }) </script> ``` We recommend taking a look at the [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) and [`useHeadSafe`](https://nuxt.com/docs/3.x/api/composables/use-head-safe) composables. ## `useSeoMeta` The [`useSeoMeta`](https://nuxt.com/docs/3.x/api/composables/use-seo-meta) composable lets you define your site's SEO meta tags as an object with full type safety. This helps you avoid typos and common mistakes, such as using `name` instead of `property`. ```vue twoslash [app.vue] <script setup lang="ts"> useSeoMeta({ title: 'My Amazing Site', ogTitle: 'My Amazing Site', description: 'This is my amazing site, let me tell you all about it.', ogDescription: 'This is my amazing site, let me tell you all about it.', ogImage: 'https://example.com/image.png', twitterCard: 'summary_large_image', }) </script> ``` ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-seo-meta"} :: ## Components While using [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) is recommended in all cases, you may have a personal preference for defining your head tags in your template using components. Nuxt provides the following components for this purpose: `<Title>`, `<Base>`, `<NoScript>`, `<Style>`, `<Meta>`, `<Link>`, `<Body>`, `<Html>` and `<Head>`. Note the capitalization of these components ensuring we don't use invalid native HTML tags. `<Head>` and `<Body>` can accept nested meta tags (for aesthetic reasons) but this does not affect *where* the nested meta tags are rendered in the final HTML. <!-- @case-police-ignore html --> ```vue [app.vue] <script setup lang="ts"> const title = ref('Hello World') </script> <template> <div> <Head> <Title>{{ title }}

{{ title }}

``` It's suggested to wrap your components in either a `` or `` components as tags will be deduped more intuitively. ::warning If you need to duplicate tags across client-server boundaries, apply a `key` attribute on the `` component. :: ## Types Below are the non-reactive types used for [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head), [`app.head`](https://nuxt.com/docs/3.x/api/nuxt-config#head) and components. ```ts interface MetaObject { title?: string titleTemplate?: string | ((title?: string) => string) templateParams?: Record> base?: Base link?: Link[] meta?: Meta[] style?: Style[] script?: Script[] noscript?: Noscript[] htmlAttrs?: HtmlAttributes bodyAttrs?: BodyAttributes } ``` See [@unhead/vue](https://github.com/unjs/unhead/blob/main/packages/vue/src/types/schema.ts) for more detailed types. ## Features ### Reactivity Reactivity is supported on all properties, by providing a computed value, a getter, or a reactive object. ::code-group ```vue twoslash [useHead] ``` ```vue twoslash [useSeoMeta] ``` ```vue [Components] ``` :: ### Title Template You can use the `titleTemplate` option to provide a dynamic template for customizing the title of your site. For example, you could add the name of your site to the title of every page. The `titleTemplate` can either be a string, where `%s` is replaced with the title, or a function. If you want to use a function (for full control), then this cannot be set in your `nuxt.config`. It is recommended instead to set it within your `app.vue` file where it will apply to all pages on your site: ::code-group ```vue twoslash [useHead] ``` :: Now, if you set the title to `My Page` with [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) on another page of your site, the title would appear as 'My Page - Site Title' in the browser tab. You could also pass `null` to default to 'Site Title'. ### Template Params You can use `templateParams` to provide additional placeholders in your `titleTemplate` besides the default `%s`. This allows for more dynamic title generation. ::code-group ```vue twoslash [useHead] ``` :: ### Body Tags You can use the `tagPosition: 'bodyClose'` option on applicable tags to append them to the end of the `` tag. For example: ```vue twoslash ``` ## Examples ### With `definePageMeta` Within your [`pages/` directory](https://nuxt.com/docs/3.x/directory-structure/pages), you can use `definePageMeta` along with [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) to set metadata based on the current route. For example, you can first set the current page title (this is extracted at build time via a macro, so it can't be set dynamically): ```vue twoslash [pages/some-page.vue] ``` And then in your layout file, you might use the route's metadata you have previously set: ```vue twoslash [layouts/default.vue] ``` ::link-example{to="https://nuxt.com/docs/examples/features/meta-tags"} :: ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/pages/#page-metadata"} :: ### Dynamic Title In the example below, `titleTemplate` is set either as a string with the `%s` placeholder or as a `function`, which allows greater flexibility in setting the page title dynamically for each route of your Nuxt app: ```vue twoslash [app.vue] ``` ```vue twoslash [app.vue] ``` `nuxt.config` is also used as an alternative way of setting the page title. However, `nuxt.config` does not allow the page title to be dynamic. Therefore, it is recommended to use `titleTemplate` in the `app.vue` file to add a dynamic title, which is then applied to all routes of your Nuxt app. ### External CSS The example below shows how you might enable Google Fonts using either the `link` property of the [`useHead`](https://nuxt.com/docs/3.x/api/composables/use-head) composable or using the `` component: ::code-group ```vue twoslash [useHead] ``` ```vue [Components] ``` :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Server" description: "Build full-stack applications with Nuxt's server framework. You can fetch data from your database or another server, create APIs, or even generate static server-side content like a sitemap or a RSS feed - all from a single codebase." canonical_url: "https://nuxt.com/docs/3.x/getting-started/server" --- # Server > Build full-stack applications with Nuxt's server framework. You can fetch data from your database or another server, create APIs, or even generate static server-side content like a sitemap or a RSS feed - all from a single codebase. ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/server"} :: ## Powered by Nitro ![Server engine](https://nuxt.com/assets/docs/getting-started/server.svg) Nuxt's server is [Nitro](https://github.com/nitrojs/nitro). It was originally created for Nuxt but is now part of [UnJS](https://unjs.io) and open for other frameworks - and can even be used on its own. Using Nitro gives Nuxt superpowers: - Full control of the server-side part of your app - Universal deployment on any provider (many zero-config) - Hybrid rendering Nitro is internally using [h3](https://github.com/h3js/h3), a minimal H(TTP) framework built for high performance and portability. ::video-accordion{title="Watch a video from Alexander Lichter to understand the responsibilities of Nuxt and Nitro in your application" videoId="DkvgJa-X31k"} :: ## Server Endpoints & Middleware You can easily manage the server-only part of your Nuxt app, from API endpoints to middleware. Both endpoints and middleware can be defined like this: ```ts twoslash [server/api/test.ts] export default defineEventHandler(async (event) => { // ... Do whatever you want here }) ``` And you can directly return `text`, `json`, `html` or even a `stream`. Out-of-the-box, it supports **hot module replacement** and **auto-import** like the other parts of your Nuxt application. ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/server"} :: ## Universal Deployment Nitro offers the ability to deploy your Nuxt app anywhere, from a bare metal server to the edge network, with a start time of just a few milliseconds. That's fast! ::read-more{to="https://nuxt.com/blog/nuxt-on-the-edge"} :: There are more than 15 presets to build your Nuxt app for different cloud providers and servers, including: - [Cloudflare Workers](https://workers.cloudflare.com) - [Netlify Functions](https://www.netlify.com/platform/core/functions/) - [Vercel Cloud](https://vercel.com/home) Or for other runtimes: ::card-group :::card ```yaml [props] icon: i-logos-deno title: Deno to: https://deno.com target: _blank ``` ::: :::card ```yaml [props] icon: i-logos-bun title: Bun to: https://bun.com target: _blank ``` ::: :: ::read-more{to="https://nuxt.com/docs/3.x/getting-started/deployment"} :: ## Hybrid Rendering Nitro has a powerful feature called `routeRules` which allows you to define a set of rules to customize how each route of your Nuxt app is rendered (and more). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Generated at build time for SEO purpose '/': { prerender: true }, // Cached for 1 hour '/api/*': { cache: { maxAge: 60 * 60 } }, // Redirection to avoid 404 '/old-page': { redirect: { to: '/new-page', statusCode: 302 }, }, // ... }, }) ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/concepts/rendering#hybrid-rendering"} Learn about all available route rules are available to customize the rendering mode of your routes. :: In addition, there are some route rules (for example, `ssr`, `appMiddleware`, and `noScripts`) that are Nuxt specific to change the behavior when rendering your pages to HTML. Some route rules (`appMiddleware`, `redirect` and `prerender`) also affect client-side behavior. Nitro is used to build the app for server side rendering, as well as pre-rendering. ::read-more{to="https://nuxt.com/docs/3.x/guide/concepts/rendering"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "State Management" description: "Nuxt provides powerful state management libraries and the useState composable to create a reactive and SSR-friendly shared state." canonical_url: "https://nuxt.com/docs/3.x/getting-started/state-management" --- # State Management > Nuxt provides powerful state management libraries and the useState composable to create a reactive and SSR-friendly shared state. Nuxt provides the [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) composable to create a reactive and SSR-friendly shared state across components. [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) is an SSR-friendly [`ref`](https://vuejs.org/api/reactivity-core#ref) replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key. ::video-accordion{title="Watch a video from Alexander Lichter about why and when to use useState" videoId="mv0WcBABcIk"} :: ::important Because the data inside [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) will be serialized to JSON, it is important that it does not contain anything that cannot be serialized, such as classes, functions or symbols. :: ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-state"} Read more about `useState` composable. :: ## Best Practices ::warning Never define `const state = ref()` outside of ` ``` ::link-example{to="https://nuxt.com/docs/examples/features/state-management"} :: ::note To globally invalidate cached state, see [`clearNuxtState`](https://nuxt.com/docs/3.x/api/utils/clear-nuxt-state) util. :: ### Initializing State Most of the time, you will want to initialize your state with data that resolves asynchronously. You can use the [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app) component with the [`callOnce`](https://nuxt.com/docs/3.x/api/utils/call-once) util to do so. ```vue twoslash [app.vue] ``` ::tip This is similar to the [`nuxtServerInit` action](https://v2.nuxt.com/docs/directory-structure/store/#the-nuxtserverinit-action) in Nuxt 2, which allows filling the initial state of your store server-side before rendering the page. :: ::read-more{to="https://nuxt.com/docs/3.x/api/utils/call-once"} :: ### Usage with Pinia In this example, we leverage the [Pinia module](https://nuxt.com/modules/pinia) to create a global store and use it across the app. ::important Make sure to install the Pinia module with `npx nuxt module add pinia` or follow the [module's installation steps](https://pinia.vuejs.org/ssr/nuxt.html#Installation). :: ::code-group ```ts [stores/website.ts] export const useWebsiteStore = defineStore('websiteStore', { state: () => ({ name: '', description: '', }), actions: { async fetch () { const infos = await $fetch('https://api.nuxt.com/modules/pinia') this.name = infos.name this.description = infos.description }, }, }) ``` ```vue [app.vue] ``` :: ## Advanced Usage ::code-group ```ts [composables/locale.ts] import type { Ref } from 'vue' export const useLocale = () => { return useState('locale', () => useDefaultLocale().value) } export const useDefaultLocale = (fallback = 'en-US') => { const locale = ref(fallback) if (import.meta.server) { const reqLocale = useRequestHeaders()['accept-language']?.split(',')[0] if (reqLocale) { locale.value = reqLocale } } else if (import.meta.client) { const navLang = navigator.language if (navLang) { locale.value = navLang } } return locale } export const useLocales = () => { const locale = useLocale() const locales = ref([ 'en-US', 'en-GB', // ..., 'ja-JP-u-ca-japanese', ]) if (!locales.value.includes(locale.value)) { locales.value.unshift(locale.value) } return locales } export const useLocaleDate = (date: Ref | Date, locale = useLocale()) => { return computed(() => new Intl.DateTimeFormat(locale.value, { dateStyle: 'full' }).format(unref(date))) } ``` ```vue [app.vue] ``` :: ::link-example{to="https://nuxt.com/docs/examples/advanced/locale"} :: ## Shared State By using [auto-imported composables](https://nuxt.com/docs/3.x/directory-structure/composables) we can define global type-safe states and import them across the app. ```ts twoslash [composables/states.ts] export const useColor = () => useState('color', () => 'pink') ``` ```vue [app.vue] ``` ::video-accordion{title="Watch a video from Daniel Roe on how to deal with global state and SSR in Nuxt" videoId="dZSNW07sO-A"} :: ## Using third-party libraries Nuxt **used to rely** on the Vuex library to provide global state management. If you are migrating from Nuxt 2, please head to [the migration guide](https://nuxt.com/docs/3.x/migration/configuration#vuex). Nuxt is not opinionated about state management, so feel free to choose the right solution for your needs. There are multiple integrations with the most popular state management libraries, including: - [Pinia](https://nuxt.com/modules/pinia) - the official Vue recommendation - [Harlem](https://nuxt.com/modules/harlem) - immutable global state management - [XState](https://nuxt.com/modules/xstate) - state machine approach with tools for visualizing and testing your state logic ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Styling" description: "Learn how to style your Nuxt application." canonical_url: "https://nuxt.com/docs/3.x/getting-started/styling" --- # Styling > Learn how to style your Nuxt application. Nuxt is highly flexible when it comes to styling. Write your own styles, or reference local and external stylesheets. You can use CSS preprocessors, CSS frameworks, UI libraries and Nuxt modules to style your application. ## Local Stylesheets If you're writing local stylesheets, the natural place to put them is the [`assets/` directory](https://nuxt.com/docs/3.x/directory-structure/assets). ### Importing Within Components You can import stylesheets in your pages, layouts and components directly. You can use a JavaScript import, or a CSS [`@import` statement](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@import). ```vue [pages/index.vue] ``` ::tip The stylesheets will be inlined in the HTML rendered by Nuxt. :: ### The CSS Property You can also use the `css` property in the Nuxt configuration. The natural place for your stylesheets is the [`assets/` directory](https://nuxt.com/docs/3.x/directory-structure/assets). You can then reference its path and Nuxt will include it to all the pages of your application. ```ts [nuxt.config.ts] export default defineNuxtConfig({ css: ['~/assets/css/main.css'], }) ``` ::tip The stylesheets will be inlined in the HTML rendered by Nuxt, injected globally and present in all pages. :: ### Working With Fonts Place your local fonts files in your `public/` directory, for example in `public/fonts`. You can then reference them in your stylesheets using `url()`. ```css [assets/css/main.css] @font-face { font-family: 'FarAwayGalaxy'; src: url('/fonts/FarAwayGalaxy.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap; } ``` Then reference your fonts by name in your stylesheets, pages or components: ```vue ``` ### Stylesheets Distributed Through NPM You can also reference stylesheets that are distributed through npm. Let's use the popular `animate.css` library as an example. ::code-group{sync="pm"} ```bash [npm] npm install animate.css ``` ```bash [yarn] yarn add animate.css ``` ```bash [pnpm] pnpm install animate.css ``` ```bash [bun] bun install animate.css ``` ```bash [deno] deno install npm:animate.css ``` :: Then you can reference it directly in your pages, layouts and components: ```vue [app.vue] ``` The package can also be referenced as a string in the css property of your Nuxt configuration. ```ts [nuxt.config.ts] export default defineNuxtConfig({ css: ['animate.css'], }) ``` ## External Stylesheets You can include external stylesheets in your application by adding a link element in the head section of your nuxt.config file. You can achieve this result using different methods. Note that local stylesheets can also be included this way. You can manipulate the head with the [`app.head`](https://nuxt.com/docs/3.x/api/nuxt-config#head) property of your Nuxt configuration: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }], }, }, }) ``` ### Dynamically Adding Stylesheets You can use the useHead composable to dynamically set a value in your head in your code. ::read-more{to="https://nuxt.com/docs/3.x/api/composables/use-head"} :: ```ts twoslash useHead({ link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }], }) ``` Nuxt uses `unhead` under the hood, and you can refer to [its full documentation](https://unhead.unjs.io). ### Modifying The Rendered Head With A Nitro Plugin If you need more advanced control, you can intercept the rendered html with a hook and modify the head programmatically. Create a plugin in `~~/server/plugins/my-plugin.ts` like this: ```ts [server/plugins/my-plugin.ts] export default defineNitroPlugin((nitro) => { nitro.hooks.hook('render:html', (html) => { html.head.push('') }) }) ``` External stylesheets are render-blocking resources: they must be loaded and processed before the browser renders the page. Web pages that contain unnecessarily large styles take longer to render. You can read more about it on [web.dev](https://web.dev/articles/defer-non-critical-css). ## Using Preprocessors To use a preprocessor like SCSS, Sass, Less or Stylus, install it first. ::code-group ```bash [Sass & SCSS] npm install -D sass ``` ```bash [Less] npm install -D less ``` ```bash [Stylus] npm install -D stylus ``` :: The natural place to write your stylesheets is the `assets` directory. You can then import your source files in your `app.vue` (or layouts files) using your preprocessor's syntax. ```vue [pages/app.vue] ``` Alternatively, you can use the `css` property of your Nuxt configuration. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ css: ['~/assets/scss/main.scss'], }) ``` ::tip In both cases, the compiled stylesheets will be inlined in the HTML rendered by Nuxt. :: If you need to inject code in pre-processed files, like a [Sass partial](https://sass-lang.com/documentation/at-rules/use/#partials) with color variables, you can do so with the Vite [preprocessors options](https://vite.dev/config/shared-options#css-preprocessoroptions). Create some partials in your `assets` directory: ::code-group{sync="preprocessor"} ```scss [assets/_colors.scss] $primary: #49240F; $secondary: #E4A79D; ``` ```sass [assets/_colors.sass] $primary: #49240F $secondary: #E4A79D ``` :: Then in your `nuxt.config` : ::code-group ```ts twoslash [SCSS] export default defineNuxtConfig({ vite: { css: { preprocessorOptions: { scss: { additionalData: '@use "~/assets/_colors.scss" as *;', }, }, }, }, }) ``` ```ts twoslash [SASS] export default defineNuxtConfig({ vite: { css: { preprocessorOptions: { sass: { additionalData: '@use "~/assets/_colors.sass" as *\n', }, }, }, }, }) ``` :: Nuxt uses Vite by default. If you wish to use webpack instead, refer to each preprocessor loader [documentation](https://webpack.js.org/loaders/sass-loader/). ### Preprocessor Workers (Experimental) Vite has made available an [experimental option](https://vite.dev/config/shared-options#css-preprocessormaxworkers) which can speed up using preprocessors. You can enable this in your `nuxt.config`: ```ts export default defineNuxtConfig({ vite: { css: { preprocessorMaxWorkers: true, // number of CPUs minus 1 }, }, }) ``` ::note This is an experimental option and you should refer to the Vite documentation and [provide feedback](https://github.com/vitejs/vite/discussions/15835). :: ## Single File Components (SFC) Styling One of the best things about Vue and SFC is how great it is at naturally dealing with styling. You can directly write CSS or preprocessor code in the style block of your components file, therefore you will have fantastic developer experience without having to use something like CSS-in-JS. However if you wish to use CSS-in-JS, you can find 3rd party libraries and modules that support it, such as [pinceau](https://github.com/Tahul/pinceau). You can refer to the [Vue docs](https://vuejs.org/api/sfc-css-features) for a comprehensive reference about styling components in SFC. ### Class And Style Bindings You can leverage Vue SFC features to style your components with class and style attributes. ::code-group ```vue [Ref and Reactive] ``` ```vue [Computed] ``` ```vue [Array] ``` ```vue [Style] ``` :: Refer to the [Vue docs](https://vuejs.org/guide/essentials/class-and-style) for more information. ### Dynamic Styles With `v-bind` You can reference JavaScript variable and expression within your style blocks with the v-bind function. The binding will be dynamic, meaning that if the variable value changes, the style will be updated. ```vue ``` ### Scoped Styles The scoped attribute allows you to style components in isolation. The styles declared with this attribute will only apply to this component. ```vue ``` ### CSS Modules You can use [CSS Modules](https://github.com/css-modules/css-modules) with the module attribute. Access it with the injected `$style` variable. ```vue ``` ### Preprocessors Support SFC style blocks support preprocessor syntax. Vite comes with built-in support for .scss, .sass, .less, .styl and .stylus files without configuration. You just need to install them first, and they will be available directly in SFC with the lang attribute. ::code-group ```vue [SCSS] ``` ```vue [Sass] ``` ```vue [LESS] ``` ```vue [Stylus] ``` :: You can refer to the [Vite CSS docs](https://vite.dev/guide/features#css) and the [@vitejs/plugin-vue docs](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue). For webpack users, refer to the [vue loader docs](https://vue-loader.vuejs.org). ## Using PostCSS Nuxt comes with postcss built-in. You can configure it in your `nuxt.config` file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ postcss: { plugins: { 'postcss-nested': {}, 'postcss-custom-media': {}, }, }, }) ``` For proper syntax highlighting in SFC, you can use the postcss lang attribute. ```vue ``` By default, Nuxt comes with the following plugins already pre-configured: - [postcss-import](https://github.com/postcss/postcss-import): Improves the `@import` rule - [postcss-url](https://github.com/postcss/postcss-url): Transforms `url()` statements - [autoprefixer](https://github.com/postcss/autoprefixer): Automatically adds vendor prefixes - [cssnano](https://cssnano.github.io/cssnano/): Minification and purge ## Leveraging Layouts For Multiple Styles If you need to style different parts of your application completely differently, you can use layouts. Use different styles for different layouts. ```vue ``` ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/layouts"} :: ## Third Party Libraries And Modules Nuxt isn't opinionated when it comes to styling and provides you with a wide variety of options. You can use any styling tool that you want, such as popular libraries like [UnoCSS](https://unocss.dev) or [Tailwind CSS](https://tailwindcss.com). The community and the Nuxt team have developed plenty of Nuxt modules to make the integration easier. You can discover them on the [modules section](https://nuxt.com/modules) of the website. Here are a few modules to help you get started: - [UnoCSS](https://nuxt.com/modules/unocss): Instant on-demand atomic CSS engine - [Tailwind CSS](https://nuxt.com/modules/tailwindcss): Utility-first CSS framework - [Fontaine](https://github.com/nuxt-modules/fontaine): Font metric fallback - [Pinceau](https://github.com/Tahul/pinceau): Adaptable styling framework - [Nuxt UI](https://ui.nuxt.com): A UI Library for Modern Web Apps - [Panda CSS](https://panda-css.com/docs/installation/nuxt): CSS-in-JS engine that generates atomic CSS at build time Nuxt modules provide you with a good developer experience out of the box, but remember that if your favorite tool doesn't have a module, it doesn't mean that you can't use it with Nuxt! You can configure it yourself for your own project. Depending on the tool, you might need to use a [Nuxt plugin](https://nuxt.com/docs/3.x/directory-structure/plugins) and/or [make your own module](https://nuxt.com/docs/3.x/guide/modules). Share them with the [community](https://nuxt.com/modules) if you do! ### Easily Load Webfonts You can use [the Nuxt Google Fonts module](https://github.com/nuxt-modules/google-fonts) to load Google Fonts. If you are using [UnoCSS](https://unocss.dev/integrations/nuxt), note that it comes with a [web fonts presets](https://unocss.dev/presets/web-fonts) to conveniently load fonts from common providers, including Google Fonts and more. ## Advanced ### Transitions Nuxt comes with the same `` element that Vue has, and also has support for the experimental [View Transitions API](https://nuxt.com/docs/3.x/getting-started/transitions#view-transitions-api-experimental). ::read-more{to="https://nuxt.com/docs/3.x/getting-started/transitions"} :: ### Font Advanced Optimization We would recommend using [Fontaine](https://github.com/nuxt-modules/fontaine) to reduce your [CLS](https://web.dev/articles/cls). If you need something more advanced, consider creating a Nuxt module to extend the build process or the Nuxt runtime. ::tip Always remember to take advantage of the various tools and techniques available in the Web ecosystem at large to make styling your application easier and more efficient. Whether you're using native CSS, a preprocessor, postcss, a UI library or a module, Nuxt has got you covered. Happy styling! :: ### LCP Advanced Optimizations You can do the following to speed-up the download of your global CSS files: - Use a CDN so the files are physically closer to your users - Compress your assets, ideally using Brotli - Use HTTP2/HTTP3 for delivery - Host your assets on the same domain (do not use a different subdomain) Most of these things should be done for you automatically if you're using modern platforms like Cloudflare, Netlify or Vercel. You can find an LCP optimization guide on [web.dev](https://web.dev/articles/optimize-lcp). If all of your CSS is inlined by Nuxt, you can (experimentally) completely stop external CSS files from being referenced in your rendered HTML. You can achieve that with a hook, that you can place in a module, or in your Nuxt configuration file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ hooks: { 'build:manifest': (manifest) => { // find the app entry, css list const css = Object.values(manifest).find(options => options.isEntry)?.css if (css) { // start from the end of the array and go to the beginning for (let i = css.length - 1; i >= 0; i--) { // if it starts with 'entry', remove it from the list if (css[i].startsWith('entry')) { css.splice(i, 1) } } } }, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Testing" description: "How to test your Nuxt application." canonical_url: "https://nuxt.com/docs/3.x/getting-started/testing" --- # Testing > How to test your Nuxt application. ::tip If you are a module author, you can find more specific information in the [Module Author's guide](https://nuxt.com/docs/3.x/guide/modules/testing). :: Nuxt offers first-class support for end-to-end and unit testing of your Nuxt application via `@nuxt/test-utils`, a library of test utilities and configuration that currently powers the [tests we use on Nuxt itself](https://github.com/nuxt/nuxt/tree/main/test) and tests throughout the module ecosystem. ::video-accordion{title="Watch a video from Alexander Lichter about getting started with @nuxt/test-utils" videoId="yGzwk9xi9gU"} :: ## Installation In order to allow you to manage your other testing dependencies, `@nuxt/test-utils` ships with various optional peer dependencies. For example: - you can choose between `happy-dom` and `jsdom` for a runtime Nuxt environment - you can choose between `vitest`, `cucumber`, `jest` and `playwright` for end-to-end test runners - `playwright-core` is only required if you wish to use the built-in browser testing utilities (and are not using `@playwright/test` as your test runner) ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [yarn] yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [pnpm] pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [bun] bun add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` :: ## Unit Testing We currently ship an environment for unit testing code that needs a [Nuxt](https://nuxt.com) runtime environment. It currently *only has support for `vitest`* (although contribution to add other runtimes would be welcome). ### Setup 1. Add `@nuxt/test-utils/module` to your `nuxt.config` file (optional). It adds a Vitest integration to your Nuxt DevTools which supports running your unit tests in development. ```ts twoslash export default defineNuxtConfig({ modules: [ '@nuxt/test-utils/module', ], }) ``` 2. Create a `vitest.config.ts` with the following content: ```ts twoslash import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' export default defineConfig({ test: { projects: [ { test: { name: 'unit', include: ['test/unit/*.{test,spec}.ts'], environment: 'node', }, }, { test: { name: 'e2e', include: ['test/e2e/*.{test,spec}.ts'], environment: 'node', }, }, await defineVitestProject({ test: { name: 'nuxt', include: ['test/nuxt/*.{test,spec}.ts'], environment: 'nuxt', }, }), ], }, }) ``` ::tip When importing `@nuxt/test-utils` in your vitest config, It is necessary to have `"type": "module"` specified in your `package.json` or rename your vitest config file appropriately. > i.e., `vitest.config.m{ts,js}`. :: ::tip It is possible to set environment variables for testing by using the `.env.test` file. :: ### Using a Nuxt Runtime Environment Using [Vitest projects](https://vitest.dev/guide/projects.html#test-projects), you have fine-grained control over which tests run in which environment: - **Unit tests**: Place regular unit tests in `test/unit/` - these run in a Node environment for speed - **Nuxt tests**: Place tests that rely on the Nuxt runtime environment in `test/nuxt/` - these will run within a Nuxt runtime environment #### Alternative: Simple Setup If you prefer a simpler setup and want all tests to run in the Nuxt environment, you can use the basic configuration: ```ts twoslash import { defineVitestConfig } from '@nuxt/test-utils/config' import { fileURLToPath } from 'node:url' export default defineVitestConfig({ test: { environment: 'nuxt', // you can optionally set Nuxt-specific environment options // environmentOptions: { // nuxt: { // rootDir: fileURLToPath(new URL('./playground', import.meta.url)), // domEnvironment: 'happy-dom', // 'happy-dom' (default) or 'jsdom' // overrides: { // // other Nuxt config you want to pass // } // } // } }, }) ``` If you're using the simple setup with `environment: 'nuxt'` by default, you can opt *out* of the [Nuxt environment](https://vitest.dev/guide/environment.html#test-environment) per test file as needed. ```ts twoslash // @vitest-environment node import { test } from 'vitest' test('my test', () => { // ... test without Nuxt environment! }) ``` ::warning This approach is not recommended as it creates a hybrid environment where Nuxt Vite plugins run but the Nuxt entry and `nuxtApp` are not initialized. This can lead to hard-to-debug errors. :: ### Organizing Your Tests With the project-based setup, you might organize your tests as follows: ```bash [Directory structure] test/ ├── e2e/ │ └── ssr.test.ts ├── nuxt/ │ ├── components.test.ts │ └── composables.test.ts ├── unit/ │ └── utils.test.ts ``` You can of course opt for any test structure, but keeping the Nuxt runtime environment separated from Nuxt end-to-end tests is important for test stability. #### Running Tests With the project setup, you can run different test suites: ```bash # Run all tests npx vitest # Run only unit tests npx vitest --project unit # Run only Nuxt tests npx vitest --project nuxt # Run tests in watch mode npx vitest --watch ``` ::warning When you run your tests within the Nuxt environment, they will be running in a [`happy-dom`](https://github.com/capricorn86/happy-dom) or [`jsdom`](https://github.com/jsdom/jsdom) environment. Before your tests run, a global Nuxt app will be initialized (including, for example, running any plugins or code you've defined in your `app.vue`). This means you should take particular care not to mutate the global state in your tests (or, if you need to, to reset it afterwards). :: ### 🎭 Built-In Mocks `@nuxt/test-utils` provides some built-in mocks for the DOM environment. #### `intersectionObserver` Default `true`, creates a dummy class without any functionality for the IntersectionObserver API #### `indexedDB` Default `false`, uses [`fake-indexeddb`](https://github.com/dumbmatter/fakeIndexedDB) to create a functional mock of the IndexedDB API These can be configured in the `environmentOptions` section of your `vitest.config.ts` file: ```ts twoslash import { defineVitestConfig } from '@nuxt/test-utils/config' export default defineVitestConfig({ test: { environmentOptions: { nuxt: { mock: { intersectionObserver: true, indexedDb: true, }, }, }, }, }) ``` ### 🛠️ Helpers `@nuxt/test-utils` provides a number of helpers to make testing Nuxt apps easier. #### `mountSuspended` `mountSuspended` allows you to mount any Vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins. ::note Under the hood, `mountSuspended` wraps `mount` from `@vue/test-utils`, so you can check out [the Vue Test Utils documentation](https://test-utils.vuejs.org/guide/) for more on the options you can pass, and how to use this utility. :: For example: ```ts twoslash // @noErrors import { expect, it } from 'vitest' import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- // tests/components/SomeComponents.nuxt.spec.ts import { mountSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' it('can mount some component', async () => { const component = await mountSuspended(SomeComponent) expect(component.text()).toMatchInlineSnapshot( '"This is an auto-imported component"', ) }) ``` ```ts twoslash // @noErrors import { expect, it } from 'vitest' // ---cut--- // tests/components/SomeComponents.nuxt.spec.ts import { mountSuspended } from '@nuxt/test-utils/runtime' import App from '~/app.vue' // tests/App.nuxt.spec.ts it('can also mount an app', async () => { const component = await mountSuspended(App, { route: '/test' }) expect(component.html()).toMatchInlineSnapshot(` "
This is an auto-imported component
I am a global component
/
Test link " `) }) ``` The options object accepts `@vue/test-utils` mount options and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). #### `renderSuspended` `renderSuspended` allows you to render any Vue component within the Nuxt environment using `@testing-library/vue`, allowing async setup and access to injections from your Nuxt plugins. This should be used together with utilities from Testing Library, e.g. `screen` and `fireEvent`. Install [@testing-library/vue](https://testing-library.com/docs/vue-testing-library/intro/) in your project to use these. Additionally, Testing Library also relies on testing globals for cleanup. You should turn these on in your [Vitest config](https://vitest.dev/config/globals). The passed in component will be rendered inside a `
`. Examples: ```ts twoslash // @noErrors import { expect, it } from 'vitest' import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- // tests/components/SomeComponents.nuxt.spec.ts import { renderSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' import { screen } from '@testing-library/vue' it('can render some component', async () => { await renderSuspended(SomeComponent) expect(screen.getByText('This is an auto-imported component')).toBeDefined() }) ``` ~~~ts twoslash // @noErrors import { expect, it } from 'vitest' // ---cut--- // tests/App.nuxt.spec.ts import { renderSuspended } from '@nuxt/test-utils/runtime' import App from '~/app.vue' it('can also render an app', async () => { const html = await renderSuspended(App, { route: '/test' }) expect(html).toMatchInlineSnapshot(` "
This is an auto-imported component
I am a global component
Index page
Test link
" `) }) ~~~ The options object accepts `@testing-library/vue` render options and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). #### `mockNuxtImport` `mockNuxtImport` allows you to mock Nuxt's auto import functionality. For example, to mock `useState`, you can do so like this: ```ts twoslash import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', () => { return () => { return { value: 'mocked storage' } } }) // your tests here ``` You can explicitly type the mock for type safety, and use the original implementation passed to the factory function when mocking complex functionality. ```ts twoslash [test/nuxt/import.test.ts] import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', (original) => { return (...args) => { return { ...original('some-key'), value: 'mocked state' } } }) // or specify the target to mock mockNuxtImport(useState, (original) => { return (...args) => { return { ...original('some-key'), value: 'mocked state' } } }) // your tests here ``` ::note `mockNuxtImport` can only be used once per mocked import per test file. It is actually a macro that gets transformed to `vi.mock` and `vi.mock` is hoisted, as described [in the Vitest docs](https://vitest.dev/api/vi#vi-mock). :: If you need to mock a Nuxt import and provide different implementations between tests, you can do it by creating and exposing your mocks using [`vi.hoisted`](https://vitest.dev/api/vi#vi-hoisted), and then use those mocks in `mockNuxtImport`. You then have access to the mocked imports, and can change the implementation between tests. Be careful to [restore mocks](https://vitest.dev/api/mock#mockrestore) before or after each test to undo mock state changes between runs. ```ts twoslash import { vi } from 'vitest' import { mockNuxtImport } from '@nuxt/test-utils/runtime' const { useStateMock } = vi.hoisted(() => { return { useStateMock: vi.fn(() => { return { value: 'mocked storage' } }), } }) mockNuxtImport('useState', () => { return useStateMock }) // Then, inside a test useStateMock.mockImplementation(() => { return { value: 'something else' } }) ``` If you need to mock behavior only inside a test, you can also use the following approach. ```ts twoslash import { beforeEach, vi } from 'vitest' import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport(useRoute, original => vi.fn(original)) beforeEach(() => { vi.resetAllMocks() }) // Then, inside a test const useRouteOriginal = vi.mocked(useRoute).getMockImplementation()! vi.mocked(useRoute).mockImplementation( (...args) => ({ ...useRouteOriginal(...args), path: '/mocked' }), ) ``` #### `mockComponent` `mockComponent` allows you to mock Nuxt's component. The first argument can be the component name in PascalCase, or the relative path of the component. The second argument is a factory function that returns the mocked component. For example, to mock `MyComponent`, you can: ```ts twoslash import { mockComponent } from '@nuxt/test-utils/runtime' mockComponent('MyComponent', { props: { value: String, }, setup (props) { // ... }, }) // relative path or alias also works mockComponent('~/components/my-component.vue', () => { // or a factory function return defineComponent({ setup (props) { // ... }, }) }) // or you can use SFC for redirecting to a mock component mockComponent('MyComponent', () => import('./MockComponent.vue')) // your tests here ``` > **Note**: You can't reference local variables in the factory function since they are hoisted. If you need to access Vue APIs or other variables, you need to import them in your factory function. ```ts twoslash import { mockComponent } from '@nuxt/test-utils/runtime' mockComponent('MyComponent', async () => { const { ref, h } = await import('vue') return defineComponent({ setup (props) { const counter = ref(0) return () => h('div', null, counter.value) }, }) }) ``` #### `registerEndpoint` `registerEndpoint` allows you create Nitro endpoint that returns mocked data. It can come in handy if you want to test a component that makes requests to API to display some data. The first argument is the endpoint name (e.g. `/test/`). The second argument is a factory function that returns the mocked data. For example, to mock `/test/` endpoint, you can do: ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' registerEndpoint('/test/', () => ({ test: 'test-field', })) ``` By default, your request will be made using the `GET` method. You may use another method by setting an object as the second argument instead of a function. ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' registerEndpoint('/test/', { method: 'POST', handler: () => ({ test: 'test-field' }), }) ``` This object accepts the following properties: - `handler`: the event handler function - `method`: (optional) HTTP method to match (e.g., 'GET', 'POST') - `once`: (optional) if true, the handler will only be used for the first matching request and then automatically removed > **Note**: If your requests in a component go to an external API, you can use `baseURL` and then make it empty using [Nuxt Environment Override Config](https://nuxt.com/docs/3.x/getting-started/configuration#environment-overrides) (`$test`) so all your requests will go to Nitro server. #### Conflict with End-To-End Testing `@nuxt/test-utils/runtime` and `@nuxt/test-utils/e2e` need to run in different testing environments and so can't be used in the same file. If you would like to use both the end-to-end and unit testing functionality of `@nuxt/test-utils`, you can split your tests into separate files. You then either specify a test environment per-file with the special `// @vitest-environment nuxt` comment, or name your runtime unit test files with the `.nuxt.spec.ts` extension. `app.nuxt.spec.ts` ```ts twoslash import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', () => { return () => { return { value: 'mocked storage' } } }) ``` `app.e2e.spec.ts` ```ts twoslash import { $fetch, setup } from '@nuxt/test-utils/e2e' await setup({ setupTimeout: 10000, }) // ... ``` ### Using `@vue/test-utils` If you prefer to use `@vue/test-utils` on its own for unit testing in Nuxt, and you are only testing components which do not rely on Nuxt composables, auto-imports or context, you can follow these steps to set it up. 1. Install the needed dependencies ::code-group{sync="pm"} ```bash [npm] npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [yarn] yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [pnpm] pnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [bun] bun add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` :: 2. Create a `vitest.config.ts` with the following content: ```ts import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'happy-dom', }, }) ``` 3. Add a new command for test in your `package.json` ```json "scripts": { "build": "nuxt build", "dev": "nuxt dev", ... "test": "vitest" }, ``` 4. Create a simple `` component `components/HelloWorld.vue` with the following content: ```vue ``` 5. Create a simple unit test for this newly created component `~/components/HelloWorld.spec.ts` ```ts twoslash import { describe, expect, it } from 'vitest' import { mount } from '@vue/test-utils' import HelloWorld from './HelloWorld.vue' describe('HelloWorld', () => { it('component renders Hello world properly', () => { const wrapper = mount(HelloWorld) expect(wrapper.text()).toContain('Hello world') }) }) ``` 6. Run vitest command ::code-group{sync="pm"} ```bash [npm] npm run test ``` ```bash [yarn] yarn test ``` ```bash [pnpm] pnpm run test ``` ```bash [bun] bun run test ``` :: Congratulations, you're all set to start unit testing with `@vue/test-utils` in Nuxt! Happy testing! ## End-To-End Testing For end-to-end testing, we support [Vitest](https://github.com/vitest-dev/vitest), [Jest](https://jestjs.io), [Cucumber](https://cucumber.io/) and [Playwright](https://playwright.dev/) as test runners. ### Setup In each `describe` block where you are taking advantage of the `@nuxt/test-utils/e2e` helper methods, you will need to set up the test context before beginning. ```ts twoslash [test/my-test.spec.ts] import { describe, test } from 'vitest' import { $fetch, setup } from '@nuxt/test-utils/e2e' describe('My test', async () => { await setup({ // test context options }) test('my test', () => { // ... }) }) ``` Behind the scenes, `setup` performs a number of tasks in `beforeAll`, `beforeEach`, `afterEach` and `afterAll` to set up the Nuxt test environment correctly. Please use the options below for the `setup` method. #### Nuxt Config - `rootDir`: Path to a directory with a Nuxt app to be put under test. - Type: `string` - Default: `'.'` - `configFile`: Name of the configuration file. - Type: `string` - Default: `'nuxt.config'` #### Timings - `setupTimeout`: The amount of time (in milliseconds) to allow for `setupTest` to complete its work (which could include building or generating files for a Nuxt application, depending on the options that are passed). - Type: `number` - Default: `120000` or `240000` on windows - `teardownTimeout`: The amount of time (in milliseconds) to allow tearing down the test environment, such as closing the browser. - Type: `number` - Default: `30000` #### Features - `build`: Whether to run a separate build step. - Type: `boolean` - Default: `true` (`false` if `browser` or `server` is disabled, or if a `host` is provided) - `server`: Whether to launch a server to respond to requests in the test suite. - Type: `boolean` - Default: `true` (`false` if a `host` is provided) - `port`: If provided, set the launched test server port to the value. - Type: `number | undefined` - Default: `undefined` - `host`: If provided, a URL to use as the test target instead of building and running a new server. Useful for running "real" end-to-end tests against a deployed version of your application, or against an already running local server (which may provide a significant reduction in test execution timings). See the [target host end-to-end example below](https://nuxt.com/docs/3.x/getting-started/testing#target-host-end-to-end-example). - Type: `string` - Default: `undefined` - `browser`: Under the hood, Nuxt test utils uses [`playwright`](https://playwright.dev) to carry out browser testing. If this option is set, a browser will be launched and can be controlled in the subsequent test suite. - Type: `boolean` - Default: `false` - `browserOptions` - Type: `object` with the following properties - `type`: The type of browser to launch - either `chromium`, `firefox` or `webkit` - `launch`: `object` of options that will be passed to playwright when launching the browser. See [full API reference](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). - `runner`: Specify the runner for the test suite. Currently, [Vitest](https://vitest.dev) is recommended. - Type: `'vitest' | 'jest' | 'cucumber'` - Default: `'vitest'` ##### Target `host` end-to-end example A common use-case for end-to-end testing is running the tests against a deployed application running in the same environment typically used for Production. For local development or automated deploy pipelines, testing against a separate local server can be more efficient and is typically faster than allowing the test framework to rebuild between tests. To utilize a separate target host for end-to-end tests, simply provide the `host` property of the `setup` function with the desired URL. ```ts import { createPage, setup } from '@nuxt/test-utils/e2e' import { describe, expect, it } from 'vitest' describe('login page', async () => { await setup({ host: 'http://localhost:8787', }) it('displays the email and password fields', async () => { const page = await createPage('/login') expect(await page.getByTestId('email').isVisible()).toBe(true) expect(await page.getByTestId('password').isVisible()).toBe(true) }) }) ``` ### APIs #### `$fetch(url)` Get the HTML of a server-rendered page. ```ts twoslash import { $fetch } from '@nuxt/test-utils/e2e' const html = await $fetch('/') ``` #### `fetch(url)` Get the response of a server-rendered page. ```ts twoslash import { fetch } from '@nuxt/test-utils/e2e' const res = await fetch('/') const { body, headers } = res ``` #### `url(path)` Get the full URL for a given page (including the port the test server is running on.) ```ts twoslash import { url } from '@nuxt/test-utils/e2e' const pageUrl = url('/page') // 'http://localhost:6840/page' ``` ### Testing in a Browser We provide built-in support using Playwright within `@nuxt/test-utils`, either programmatically or via the Playwright test runner. #### `createPage(url)` Within `vitest`, `jest` or `cucumber`, you can create a configured Playwright browser instance with `createPage`, and (optionally) point it at a path from the running server. You can find out more about the API methods available from [in the Playwright documentation](https://playwright.dev/docs/api/class-page). ```ts twoslash import { createPage } from '@nuxt/test-utils/e2e' const page = await createPage('/page') // you can access all the Playwright APIs from the `page` variable ``` #### Testing with Playwright Test Runner We also provide first-class support for testing Nuxt within [the Playwright test runner](https://playwright.dev/docs/intro). ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @playwright/test @nuxt/test-utils ``` ```bash [yarn] yarn add --dev @playwright/test @nuxt/test-utils ``` ```bash [pnpm] pnpm add -D @playwright/test @nuxt/test-utils ``` ```bash [bun] bun add --dev @playwright/test @nuxt/test-utils ``` ```bash [deno] deno add --dev npm:@playwright/test npm:@nuxt/test-utils ``` :: You can provide global Nuxt configuration, with the same configuration details as the `setup()` function mentioned earlier in this section. ```ts [playwright.config.ts] import { fileURLToPath } from 'node:url' import { defineConfig, devices } from '@playwright/test' import type { ConfigOptions } from '@nuxt/test-utils/playwright' export default defineConfig({ use: { nuxt: { rootDir: fileURLToPath(new URL('.', import.meta.url)), }, }, // ... }) ``` ::read-more{title="See full example config" to="https://github.com/nuxt/test-utils/blob/main/examples/app-playwright/playwright.config.ts" target="_blank"} :: Your test file should then use `expect` and `test` directly from `@nuxt/test-utils/playwright`: ```ts [tests/example.test.ts] import { expect, test } from '@nuxt/test-utils/playwright' test('test', async ({ page, goto }) => { await goto('/', { waitUntil: 'hydration' }) await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') }) ``` You can alternatively configure your Nuxt server directly within your test file: ```ts [tests/example.test.ts] import { expect, test } from '@nuxt/test-utils/playwright' test.use({ nuxt: { rootDir: fileURLToPath(new URL('..', import.meta.url)), }, }) test('test', async ({ page, goto }) => { await goto('/', { waitUntil: 'hydration' }) await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Transitions" description: "Apply transitions between pages and layouts with Vue or native browser View Transitions." canonical_url: "https://nuxt.com/docs/3.x/getting-started/transitions" --- # Transitions > Apply transitions between pages and layouts with Vue or native browser View Transitions. ::note Nuxt leverages Vue's [``](https://vuejs.org/guide/built-ins/transition#the-transition-component) component to apply transitions between pages and layouts. :: ## Page Transitions You can enable page transitions to apply an automatic transition for all your [pages](https://nuxt.com/docs/3.x/directory-structure/pages). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: { name: 'page', mode: 'out-in' }, }, }) ``` ::note If you are changing layouts as well as page, the page transition you set here will not run. Instead, you should set a [layout transition](https://nuxt.com/docs/3.x/getting-started/transitions#layout-transitions). :: To start adding transition between your pages, add the following CSS to your [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app): ::code-group ```vue [app.vue] ``` ```vue [pages/index.vue] ``` ```vue [pages/about.vue] ``` :: This produces the following result when navigating between pages: To set a different transition for a page, set the `pageTransition` key in [`definePageMeta`](https://nuxt.com/docs/3.x/api/utils/define-page-meta) of the page: ::code-group ```vue twoslash [pages/about.vue] ``` ```vue [app.vue] ``` :: Moving to the about page will add the 3d rotation effect: ## Layout Transitions You can enable layout transitions to apply an automatic transition for all your [layouts](https://nuxt.com/docs/3.x/directory-structure/layouts). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { layoutTransition: { name: 'layout', mode: 'out-in' }, }, }) ``` To start adding transition between your pages and layouts, add the following CSS to your [`app.vue`](https://nuxt.com/docs/3.x/directory-structure/app): ::code-group ```vue [app.vue] ``` ```vue [layouts/default.vue] ``` ```vue [layouts/orange.vue] ``` ```vue [pages/index.vue] ``` ```vue [pages/about.vue] ``` :: This produces the following result when navigating between pages: Similar to `pageTransition`, you can apply a custom `layoutTransition` to the page component using `definePageMeta`: ```vue twoslash [pages/about.vue] ``` ## Global Settings You can customize these default transition names globally using `nuxt.config`. Both `pageTransition` and `layoutTransition` keys accept [`TransitionProps`](https://vuejs.org/api/built-in-components#transition) as JSON serializable values where you can pass the `name`, `mode` and other valid transition-props of the custom CSS transition. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: { name: 'fade', mode: 'out-in', // default }, layoutTransition: { name: 'slide', mode: 'out-in', // default }, }, }) ``` ::warning If you change the `name` property, you also have to rename the CSS classes accordingly. :: To override the global transition property, use the `definePageMeta` to define page or layout transitions for a single Nuxt page and override any page or layout transitions that are defined globally in `nuxt.config` file. ```vue twoslash [pages/some-page.vue] ``` ## Disable Transitions `pageTransition` and `layoutTransition` can be disabled for a specific route: ```vue twoslash [pages/some-page.vue] ``` Or globally in the `nuxt.config`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: false, layoutTransition: false, }, }) ``` ## JavaScript Hooks For advanced use-cases, you can use JavaScript hooks to create highly dynamic and custom transitions for your Nuxt pages. This way presents perfect use-cases for JavaScript animation libraries such as [GSAP](https://gsap.com). ```vue twoslash [pages/some-page.vue] ``` ::tip Learn more about additional [JavaScript hooks](https://vuejs.org/guide/built-ins/transition#javascript-hooks) available in the `Transition` component. :: ## Dynamic Transitions To apply dynamic transitions using conditional logic, you can leverage inline [middleware](https://nuxt.com/docs/3.x/directory-structure/middleware) to assign a different transition name to `to.meta.pageTransition`. ::code-group ```vue twoslash [pages/[id\\].vue] ``` ```vue [layouts/default.vue] ``` :: The page now applies the `slide-left` transition when going to the next id and `slide-right` for the previous: ## Transition with NuxtPage When `` is used in `app.vue`, transitions can be configured with the `transition` prop to activate transitions globally. ```vue [app.vue] ``` ::note Remember, this page transition cannot be overridden with `definePageMeta` on individual pages. :: ## View Transitions API (experimental) Nuxt ships with an experimental implementation of the [**View Transitions API**](https://developer.chrome.com/docs/web-platform/view-transitions) (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API)). This is an exciting new way to implement native browser transitions which (among other things) have the ability to transition between unrelated elements on different pages. You can check a demo [on StackBlitz](https://stackblitz.com/edit/nuxt-view-transitions). The Nuxt integration can be enabled with the `experimental.viewTransition` option in your configuration file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { viewTransition: true, }, }) ``` The possible values are: `false`, `true`, or `'always'`. If set to true, Nuxt will not apply transitions if the user's browser matches `prefers-reduced-motion: reduce` (recommended). If set to `always`, Nuxt will always apply the transition and it is up to you to respect the user's preference. By default, view transitions are enabled for all [pages](https://nuxt.com/docs/3.x/directory-structure/pages), but you can set a different global default. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { // Disable view transitions globally, and opt-in on a per page basis viewTransition: false, }, }) ``` It is possible to override the default `viewTransition` value for a page by setting the `viewTransition` key in [`definePageMeta`](https://nuxt.com/docs/3.x/api/utils/define-page-meta) of the page: ```vue twoslash [pages/about.vue] ``` ::warning Overriding view transitions on a per-page basis will only have an effect if you have enabled the `experimental.viewTransition` option. :: If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to *disable* Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents: ```ts export default defineNuxtRouteMiddleware((to) => { if (import.meta.server || !document.startViewTransition) { return } // Disable built-in Vue transitions to.meta.pageTransition = false to.meta.layoutTransition = false }) ``` ### Known Issues - If you perform data fetching within your page setup functions, you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates whilst they are taking place.) We're looking at restricting the View Transition to the final moments before `` resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Upgrade Guide" description: "Learn how to upgrade to the latest Nuxt version." canonical_url: "https://nuxt.com/docs/3.x/getting-started/upgrade" --- # Upgrade Guide > Learn how to upgrade to the latest Nuxt version. ## Upgrading Nuxt ### Latest Nuxt 3 release To upgrade Nuxt to the [latest v3 release](https://github.com/nuxt/nuxt/releases), use the `nuxt upgrade` command with `--channel=v3` flag. ::code-group{sync="pm"} ```bash [npm] npx nuxt upgrade --dedupe --channel=v3 ``` ```bash [yarn] yarn nuxt upgrade --dedupe --channel=v3 ``` ```bash [pnpm] pnpm nuxt upgrade --dedupe --channel=v3 ``` ```bash [bun] bun x nuxt upgrade --dedupe --channel=v3 ``` :: ::note This will only work if you *already have* a version of `@nuxt/cli` which has the `--channel` flag implemented. If this does not work, you can instead use `nuxi@latest` for the initial upgrade. E.g. `npx nuxi@latest upgrade --dedupe --channel=v3` :: ### Latest release To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxt upgrade` command. ::code-group{sync="pm"} ```bash [npm] npx nuxt upgrade ``` ```bash [yarn] yarn nuxt upgrade ``` ```bash [pnpm] pnpm nuxt upgrade ``` ```bash [bun] bun x nuxt upgrade ``` ```bash [deno] deno x nuxt upgrade ``` :: ### Nightly Release Channel To use the latest Nuxt build and test features before their release, read about the [nightly release channel](https://nuxt.com/docs/3.x/guide/going-further/nightly-release-channel) guide. ::warning The nightly release channel `latest` tag is currently tracking the Nuxt v4 branch, meaning that it is particularly likely to have breaking changes right now — be careful! You can opt in to the 3.x branch nightly releases with `"nuxt": "npm:nuxt-nightly@3x"`. :: ## Testing Nuxt 4 Nuxt 4 is **scheduled for release in Q2 2025**. It will include all the features currently available through `compatibilityVersion: 4`. Until the release, it is possible to test many of Nuxt 4's breaking changes from Nuxt version 3.12+. ::video-accordion{title="Watch a video from Alexander Lichter showing how to opt in to Nuxt 4's breaking changes already" videoId="r4wFKlcJK6c"} :: ### Opting in to Nuxt 4 First, upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases). Then you can set your `compatibilityVersion` to match Nuxt 4 behavior: ::code-collapse ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ future: { compatibilityVersion: 4, }, // To re-enable _all_ Nuxt v3 behavior, set the following options: // srcDir: '.', // dir: { // app: 'app' // }, // experimental: { // scanPageMeta: 'after-resolve', // sharedPrerenderData: false, // compileTemplate: true, // resetAsyncDataToUndefined: true, // templateUtils: true, // relativeWatchPaths: true, // normalizeComponentNames: false, // spaLoadingTemplateLocation: 'within', // parseErrorData: false, // pendingWhenIdle: true, // alwaysRunFetchOnKeyChange: true, // defaults: { // useAsyncData: { // deep: true // } // } // }, // features: { // inlineStyles: true // }, // unhead: { // renderSSRHeadOptions: { // omitLineBreaks: false // } // } }) ``` :: ::note For now, you need to define the compatibility version in each layer that opts into Nuxt 4 behavior. This will not be required after Nuxt 4 is released. :: When you set your `compatibilityVersion` to `4`, defaults throughout your Nuxt configuration will change to opt in to Nuxt v4 behavior, but you can granularly re-enable Nuxt v3 behavior when testing, following the commented out lines above. Please file issues if so, so that we can address them in Nuxt or in the ecosystem. Breaking or significant changes are documented below along with migration steps and available configuration options. ### Migrating Using Codemods To facilitate the upgrade process, we have collaborated with the [Codemod](https://github.com/codemod/codemod) team to automate many migration steps with some open-source codemods. ::note If you encounter any issues, please report them to the Codemod team with `npx codemod feedback` 🙏 :: For a complete list of Nuxt 4 codemods, detailed information on each, their source, and various ways to run them, visit the [Codemod Registry](https://app.codemod.com/registry). You can run all the codemods mentioned in this guide using the following `codemod` recipe: ::code-group ```bash [npm] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 npx codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [yarn] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 yarn dlx codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [pnpm] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 pnpm dlx codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [bun] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 bun x codemod@0.18.7 nuxt/4/migration-recipe ``` ```bash [deno] # Using pinned version due to https://github.com/codemod/codemod/issues/1710 deno x codemod@0.18.7 nuxt/4/migration-recipe ``` :: This command will execute all codemods in sequence, with the option to deselect any that you do not wish to run. Each codemod is also listed below alongside its respective change and can be executed independently. ### New Directory Structure 🚦 **Impact Level**: Significant Nuxt now defaults to a new directory structure, with backwards compatibility (so if Nuxt detects you are using the old structure, such as with a top-level `pages/` directory, this new structure will not apply). 👉 [See full RFC](https://github.com/nuxt/nuxt/issues/26444) #### What Changed - the new Nuxt default `srcDir` is `app/` by default, and most things are resolved from there. - `serverDir` now defaults to `/server` rather than `/server` - `layers/`, `modules/` and `public/` are resolved relative to `` by default - if using [Nuxt Content v2.13+](https://github.com/nuxt/content/pull/2649), `content/` is resolved relative to `` - a new `dir.app` is added, which is the directory we look for `router.options.ts` and `spa-loading-template.html` - this defaults to `/` - a new [`shared/`](https://nuxt.com/docs/3.x/directory-structure/shared) directory is available for code shared between the Vue app and the Nitro server, with auto-imports for `shared/utils/` and `shared/types/`
An example v4 folder structure. ```sh .output/ .nuxt/ app/ assets/ components/ composables/ layouts/ middleware/ pages/ plugins/ utils/ app.config.ts app.vue router.options.ts content/ layers/ modules/ node_modules/ public/ shared/ types/ utils/ server/ api/ middleware/ plugins/ routes/ utils/ nuxt.config.ts ``` ::note With this new structure, the `~` alias now points to the `app/` directory by default (your `srcDir`). This means `~/components` resolves to `components/`, `~/pages` to `pages/`, etc. ::
👉 For more details, see the [PR implementing this change](https://github.com/nuxt/nuxt/pull/27029). #### Reasons for Change 1. **Performance** - placing all your code in the root of your repo causes issues with `.git/` and `node_modules/` folders being scanned/included by FS watchers which can significantly delay startup on non-Mac OSes. 2. **IDE type-safety** - `server/` and the rest of your app are running in two entirely different contexts with different global imports available, and making sure `server/` isn't *inside* the same folder as the rest of your app is a big first step to ensuring you get good auto-completes in your IDE. ::video-accordion{title="Watch a video from Vue School on the new directory structure" videoId="1031028378" platform="vimeo"} :: #### Migration Steps 1. Create a new directory called `app/`. 2. Move your `assets/`, `components/`, `composables/`, `layouts/`, `middleware/`, `pages/`, `plugins/` and `utils/` folders under it, as well as `app.vue`, `error.vue`, `app.config.ts`. If you have an `app/router-options.ts` or `app/spa-loading-template.html`, these paths remain the same. 3. Make sure your `nuxt.config.ts`, `content/`, `layers/`, `modules/`, `public/`, `shared/` and `server/` folders remain outside the `app/` folder, in the root of your project. 4. Remember to update any third-party configuration files to work with the new directory structure, such as your `tailwindcss` or `eslint` configuration (if required - `@nuxtjs/tailwindcss` should automatically configure `tailwindcss` correctly). ::tip You can automate this migration by running `npx codemod@latest nuxt/4/file-structure` :: However, migration is *not required*. If you wish to keep your current folder structure, Nuxt should auto-detect it. (If it does not, please raise an issue.) The one exception is that if you *already* have a custom `srcDir`. In this case, you should be aware that your `modules/`, `public/`, `shared/` and `server/` folders will be resolved from your `rootDir` rather than from your custom `srcDir`. You can override this by configuring `dir.modules`, `dir.public` and `serverDir` if you need to. You can also force a v3 folder structure with the following configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ // This reverts the new srcDir default from `app` back to your root directory srcDir: '.', // This specifies the directory prefix for `app/router.options.ts` and `app/spa-loading-template.html` dir: { app: 'app', }, }) ``` ### Singleton Data Fetching Layer 🚦 **Impact Level**: Moderate #### What Changed Nuxt's data fetching system (`useAsyncData` and `useFetch`) has been significantly reorganized for better performance and consistency: 1. **Shared refs for the same key**: All calls to `useAsyncData` or `useFetch` with the same key now share the same `data`, `error` and `status` refs. This means that it is important that all calls with an explicit key must not have conflicting `deep`, `transform`, `pick`, `getCachedData` or `default` options. 2. **More control over `getCachedData`**: The `getCachedData` function is now called every time data is fetched, even if this is caused by a watcher or calling `refreshNuxtData`. (Previously, new data was always fetched and this function was not called in these cases.) To allow more control over when to use cached data and when to refetch, the function now receives a context object with the cause of the request. 3. **Reactive key support**: You can now use computed refs, plain refs or getter functions as keys, which enables automatic data refetching (and stores data separately). 4. **Data cleanup**: When the last component using data fetched with `useAsyncData` is unmounted, Nuxt will remove that data to avoid ever-growing memory usage. #### Reasons for Change These changes have been made to improve memory usage and increase consistency with loading states across calls of `useAsyncData`. #### Migration Steps 1. **Check for inconsistent options**: Review any components using the same key with different options or fetch functions. ```ts // This will now trigger a warning const { data: users1 } = useAsyncData('users', () => $fetch('/api/users'), { deep: false }) const { data: users2 } = useAsyncData('users', () => $fetch('/api/users'), { deep: true }) ``` It may be beneficial to extract any calls to `useAsyncData` that share an explicit key (and have custom options) into their own composable: ```ts [composables/useUserData.ts] export function useUserData (userId: string) { return useAsyncData( `user-${userId}`, () => fetchUser(userId), { deep: true, transform: user => ({ ...user, lastAccessed: new Date() }), }, ) } ``` 2. **Update `getCachedData` implementations**: ```diff useAsyncData('key', fetchFunction, { - getCachedData: (key, nuxtApp) => { - return cachedData[key] - } + getCachedData: (key, nuxtApp, ctx) => { + // ctx.cause - can be 'initial' | 'refresh:hook' | 'refresh:manual' | 'watch' + + // Example: Don't use cache on manual refresh + if (ctx.cause === 'refresh:manual') return undefined + + return cachedData[key] + } }) ``` Alternatively, for now, you can disable this behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { granularCachedData: false, purgeCachedData: false, }, }) ``` ### Corrected Module Loading Order in Layers 🚦 **Impact Level**: Minimal #### What Changed The order in which modules are loaded when using [Nuxt layers](https://nuxt.com/docs/3.x/guide/going-further/layers) has been corrected. Previously, modules from the project root were loaded before modules from extended layers, which was the reverse of the expected behavior. Now modules are loaded in the correct order: 1. **Layer modules first** (in extend order - deeper layers first) 2. **Project modules last** (highest priority) This affects both: - Modules defined in the `modules` array in `nuxt.config.ts` - Auto-discovered modules from the `modules/` directory #### Reasons for Change This change ensures that: - Extended layers have lower priority than the consuming project - Module execution order matches the intuitive layer inheritance pattern - Module configuration and hooks work as expected in multi-layer setups #### Migration Steps **Most projects will not need changes**, as this corrects the loading order to match expected behavior. However, if your project was relying on the previous incorrect order, you may need to: 1. **Review module dependencies**: Check if any modules depend on specific loading order 2. **Adjust module configuration**: If modules were configured to work around the incorrect order 3. **Test thoroughly**: Ensure all functionality works as expected with the corrected order Example of the new correct order: ```ts // Layer: my-layer/nuxt.config.ts export default defineNuxtConfig({ modules: ['layer-module-1', 'layer-module-2'], }) // Project: nuxt.config.ts export default defineNuxtConfig({ extends: ['./my-layer'], modules: ['project-module-1', 'project-module-2'], }) // Loading order (corrected): // 1. layer-module-1 // 2. layer-module-2 // 3. project-module-1 (can override layer modules) // 4. project-module-2 (can override layer modules) ``` If you encounter issues with module order dependencies due to needing to register a hook, consider using the [`modules:done` hook](https://nuxt.com/docs/3.x/guide/modules/recipes-advanced#create-custom-hooks) for modules that need to call a hook. This is run after all other modules have been loaded, which means it is safe to use. 👉 See [PR #31507](https://github.com/nuxt/nuxt/pull/31507) and [issue #25719](https://github.com/nuxt/nuxt/issues/25719) for more details. ### Deduplication of Route Metadata 🚦 **Impact Level**: Minimal #### What Changed It's possible to set some route metadata using `definePageMeta`, such as the `name`, `path`, and so on. Previously these were available both on the route and on route metadata (for example, `route.name` and `route.meta.name`). Now, they are only accessible on the route object. #### Reasons for Change This is a result of enabling `experimental.scanPageMeta` by default, and is a performance optimization. #### Migration Steps The migration should be straightforward: ```diff const route = useRoute() - console.log(route.meta.name) + console.log(route.name) ``` ### Normalized Component Names 🚦 **Impact Level**: Moderate Vue will now generate component names that match the Nuxt pattern for component naming. #### What Changed By default, if you haven't set it manually, Vue will assign a component name that matches the filename of the component. ```bash [Directory structure] ├─ components/ ├─── SomeFolder/ ├───── MyComponent.vue ``` In this case, the component name would be `MyComponent`, as far as Vue is concerned. If you wanted to use `` with it, or identify it in the Vue DevTools, you would need to use this name. But in order to auto-import it, you would need to use `SomeFolderMyComponent`. With this change, these two values will match, and Vue will generate a component name that matches the Nuxt pattern for component naming. #### Migration Steps Ensure that you use the updated name in any tests which use `findComponent` from `@vue/test-utils` and in any `` which depends on the name of your component. Alternatively, for now, you can disable this behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { normalizeComponentNames: false, }, }) ``` ### Unhead v2 🚦 **Impact Level**: Minimal #### What Changed [Unhead](https://unhead.unjs.io/), used to generate `` tags, has been updated to version 2. While mostly compatible it includes several breaking changes for lower-level APIs. - Removed props: `vmid`, `hid`, `children`, `body`. - Promise input no longer supported. - Tags are now sorted using Capo.js by default. #### Migration Steps The above changes should have minimal impact on your app. If you have issues you should verify: - You're not using any of the removed props. ```diff useHead({ meta: [{ name: 'description', // meta tags don't need a vmid, or a key - vmid: 'description' - hid: 'description' }] }) ``` - If you're using [Template Params](https://unhead.unjs.io/docs/head/guides/plugins/template-params) or [Alias Tag Sorting](https://unhead.unjs.io/docs/head/guides/plugins/alias-sorting), you will need to explicitly opt in to these features now. ```ts import { AliasSortingPlugin, TemplateParamsPlugin } from '@unhead/vue/plugins' export default defineNuxtPlugin({ setup () { const unhead = injectHead() unhead.use(TemplateParamsPlugin) unhead.use(AliasSortingPlugin) }, }) ``` While not required it's recommended to update any imports from `@unhead/vue` to `#imports` or `nuxt/app`. ```diff -import { useHead } from '@unhead/vue' +import { useHead } from '#imports' ``` If you still have issues you may revert to the v1 behavior by enabling the `head.legacy` config. ```ts export default defineNuxtConfig({ unhead: { legacy: true, }, }) ``` ### New DOM Location for SPA Loading Screen 🚦 **Impact Level**: Minimal #### What Changed When rendering a client-only page (with `ssr: false`), we optionally render a loading screen (from `~/app/spa-loading-template.html` - note that this has also changed to `~/spa-loading-template.html` in Nuxt 4), within the Nuxt app root: ```html
``` Now, we default to rendering the template alongside the Nuxt app root: ```html
``` #### Reasons for Change This allows the spa loading template to remain in the DOM until the Vue app suspense resolves, preventing a flash of white. #### Migration Steps If you were targeting the spa loading template with CSS or `document.queryElement` you will need to update your selectors. For this purpose you can use the new `app.spaLoaderTag` and `app.spaLoaderAttrs` configuration options. Alternatively, you can revert to the previous behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { spaLoadingTemplateLocation: 'within', }, }) ``` ### Parsed `error.data` 🚦 **Impact Level**: Minimal It was possible to throw an error with a `data` property, but this was not parsed. Now, it is parsed and made available in the `error` object. Although a fix, this is technically a breaking change if you were relying on the previous behavior and parsing it manually. #### Migration Steps Update your custom `error.vue` to remove any additional parsing of `error.data`: ```diff ``` Alternatively, you can disable this change: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { parseErrorData: false, }, }) ``` ### More Granular Inline Styles 🚦 **Impact Level**: Moderate Nuxt will now only inline styles for Vue components, not global CSS. #### What Changed Previously, Nuxt would inline all CSS, including global styles, and remove `` elements to separate CSS files. Now, Nuxt will only do this for Vue components (which previously produced separate chunks of CSS). We think this is a better balance of reducing separate network requests (just as before, there will not be separate requests for individual `.css` files per-page or per-component on the initial load), as well as allowing caching of a single global CSS file and reducing the document download size of the initial request. #### Migration Steps This feature is fully configurable and you can revert to the previous behavior by setting `inlineStyles: true` to inline global CSS as well as per-component CSS. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ features: { inlineStyles: true, }, }) ``` ### Scan Page Meta After Resolution 🚦 **Impact Level**: Minimal #### What Changed We now scan page metadata (defined in `definePageMeta`) *after* calling the `pages:extend` hook rather than before. #### Reasons for Change This was to allow scanning metadata for pages that users wanted to add in `pages:extend`. We still offer an opportunity to change or override page metadata in a new `pages:resolved` hook. #### Migration Steps If you want to override page metadata, do that in `pages:resolved` rather than in `pages:extend`. ```diff export default defineNuxtConfig({ hooks: { - 'pages:extend'(pages) { + 'pages:resolved'(pages) { const myPage = pages.find(page => page.path === '/') myPage.meta ||= {} myPage.meta.layout = 'overridden-layout' } } }) ``` Alternatively, you can revert to the previous behaviour with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { scanPageMeta: true, }, }) ``` ### Shared Prerender Data 🚦 **Impact Level**: Medium #### What Changed We enabled a previously experimental feature to share data from `useAsyncData` and `useFetch` calls, across different pages. See [original PR](https://github.com/nuxt/nuxt/pull/24894). #### Reasons for Change This feature automatically shares payload *data* between pages that are prerendered. This can result in a significant performance improvement when prerendering sites that use `useAsyncData` or `useFetch` and fetch the same data in different pages. For example, if your site requires a `useFetch` call for every page (for example, to get navigation data for a menu, or site settings from a CMS), this data would only be fetched once when prerendering the first page that uses it, and then cached for use when prerendering other pages. #### Migration Steps Make sure that any unique key of your data is always resolvable to the same data. For example, if you are using `useAsyncData` to fetch data related to a particular page, you should provide a key that uniquely matches that data. (`useFetch` should do this automatically for you.) ```ts [pages/test/[slug\\].vue] // This would be unsafe in a dynamic page (e.g. `[slug].vue`) because the route slug makes a difference // to the data fetched, but Nuxt can't know that because it's not reflected in the key. const route = useRoute() const { data } = await useAsyncData(async () => { return await $fetch(`/api/my-page/${route.params.slug}`) }) // Instead, you should use a key that uniquely identifies the data fetched. const { data } = await useAsyncData(route.params.slug, async () => { return await $fetch(`/api/my-page/${route.params.slug}`) }) ``` Alternatively, you can disable this feature with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { sharedPrerenderData: false, }, }) ``` ### Default `data` and `error` values in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal #### What Changed `data` and `error` objects returned from `useAsyncData` will now default to `undefined`. #### Reasons for Change Previously `data` was initialized to `null` but reset in `clearNuxtData` to `undefined`. `error` was initialized to `null`. This change is to bring greater consistency. #### Migration Steps If you were checking if `data.value` or `error.value` were `null`, you can update these checks to check for `undefined` instead. ::tip You can automate this step by running `npx codemod@latest nuxt/4/default-data-error-value` :: If you encounter any issues you can revert back to the previous behavior with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { useAsyncData: { value: 'null', errorValue: 'null', }, }, }, }) ``` Please report an issue if you are doing this, as we do not plan to keep this as configurable. ### Removal of deprecated `boolean` values for `dedupe` option when calling `refresh` in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal #### What Changed Previously it was possible to pass `dedupe: boolean` to `refresh`. These were aliases of `cancel` (`true`) and `defer` (`false`). ```ts twoslash [app.vue] // @errors: 2322 const { refresh } = await useAsyncData(() => Promise.resolve({ message: 'Hello, Nuxt!' })) async function refreshData () { await refresh({ dedupe: true }) } ``` #### Reasons for Change These aliases were removed, for greater clarity. The issue came up when adding `dedupe` as an option to `useAsyncData`, and we removed the boolean values as they ended up being *opposites*. `refresh({ dedupe: false })` meant **do not *cancel* existing requests in favour of this new one**. But passing `dedupe: true` within the options of `useAsyncData` means **do not make any new requests if there is an existing pending request.** (See [PR](https://github.com/nuxt/nuxt/pull/24564#pullrequestreview-1764584361).) #### Migration Steps The migration should be straightforward: ```diff const { refresh } = await useAsyncData(async () => ({ message: 'Hello, Nuxt 3!' })) async function refreshData () { - await refresh({ dedupe: true }) + await refresh({ dedupe: 'cancel' }) - await refresh({ dedupe: false }) + await refresh({ dedupe: 'defer' }) } ``` ::tip You can automate this step by running `npx codemod@latest nuxt/4/deprecated-dedupe-value` :: ### Respect defaults when clearing `data` in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal #### What Changed If you provide a custom `default` value for `useAsyncData`, this will now be used when calling `clear` or `clearNuxtData` and it will be reset to its default value rather than simply unset. #### Reasons for Change Often users set an appropriately empty value, such as an empty array, to avoid the need to check for `null`/`undefined` when iterating over it. This should be respected when resetting/clearing the data. #### Migration Steps If you encounter any issues you can revert back to the previous behavior, for now, with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { resetAsyncDataToUndefined: true, }, }) ``` Please report an issue if you are doing so, as we do not plan to keep this as configurable. ### Alignment of `pending` value in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Medium The `pending` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a computed property that is `true` only when `status` is also pending. #### What Changed Now, when `immediate: false` is passed, `pending` will be `false` until the first request is made. This is a change from the previous behavior, where `pending` was always `true` until the first request was made. #### Reasons for Change This aligns the meaning of `pending` with the `status` property, which is also `pending` when the request is in progress. #### Migration Steps If you rely on the `pending` property, ensure that your logic accounts for the new behavior where `pending` will only be `true` when the status is also pending. ```diff ``` Alternatively, you can temporarily revert to the previous behavior with: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { pendingWhenIdle: true, }, }) ``` ### Key Change Behavior in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Medium #### What Changed When using reactive keys in `useAsyncData` or `useFetch`, Nuxt automatically refetches data when the key changes. When `immediate: false` is set, `useAsyncData` will only fetch data when the key changes if the data has already been fetched once. Previously, `useFetch` had slightly different behavior. It would always fetch data when the key changed. Now, `useFetch` and `useAsyncData` behave consistently - by only fetch data when the key changes if the data has already been fetched once. #### Reasons for Change This ensures consistent behavior between `useAsyncData` and `useFetch`, and prevents unexpected fetches. If you have set `immediate: false`, then you must call `refresh` or `execute` or data will never be fetched in `useFetch` or `useAsyncData`. #### Migration Steps This change should generally improve the expected behavior, but if you were expecting changing the key or options of a non-immediate `useFetch`, you now will need to trigger it manually the first time. ```diff const id = ref('123') const { data, execute } = await useFetch('/api/test', { query: { id }, immediate: false ) + watch(id, () => execute(), { once: true }) ``` To opt out of this behavior: ```ts // Or globally in your Nuxt config export default defineNuxtConfig({ experimental: { alwaysRunFetchOnKeyChange: true, }, }) ``` ### Shallow Data Reactivity in `useAsyncData` and `useFetch` 🚦 **Impact Level**: Minimal The `data` object returned from `useAsyncData`, `useFetch`, `useLazyAsyncData` and `useLazyFetch` is now a `shallowRef` rather than a `ref`. #### What Changed When new data is fetched, anything depending on `data` will still be reactive because the entire object is replaced. But if your code changes a property *within* that data structure, this will not trigger any reactivity in your app. #### Reasons for Change This brings a **significant** performance improvement for deeply nested objects and arrays because Vue does not need to watch every single property/array for modification. In most cases, `data` should also be immutable. #### Migration Steps In most cases, no migration steps are required, but if you rely on the reactivity of the data object then you have two options: 1. You can granularly opt in to deep reactivity on a per-composable basis: ```diff - const { data } = useFetch('/api/test') + const { data } = useFetch('/api/test', { deep: true }) ``` 2. You can change the default behavior on a project-wide basis (not recommended): ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { useAsyncData: { deep: true, }, }, }, }) ``` ::tip If you need to, you can automate this step by running `npx codemod@latest nuxt/4/shallow-function-reactivity` :: ### Absolute Watch Paths in `builder:watch` 🚦 **Impact Level**: Minimal #### What Changed The Nuxt `builder:watch` hook now emits a path which is absolute rather than relative to your project `srcDir`. #### Reasons for Change This allows us to support watching paths which are outside your `srcDir`, and offers better support for layers and other more complex patterns. #### Migration Steps We have already proactively migrated the public Nuxt modules which we are aware use this hook. See [issue #25339](https://github.com/nuxt/nuxt/issues/25339). However, if you are a module author using the `builder:watch` hook and wishing to remain backwards/forwards compatible, you can use the following code to ensure that your code works the same in both Nuxt v3 and Nuxt v4: ```diff + import { relative, resolve } from 'node:fs' // ... nuxt.hook('builder:watch', async (event, path) => { + path = relative(nuxt.options.srcDir, resolve(nuxt.options.srcDir, path)) // ... }) ``` ::tip You can automate this step by running `npx codemod@latest nuxt/4/absolute-watch-path` :: ### Removal of `window.__NUXT__` object #### What Changed We are removing the global `window.__NUXT__` object after the app finishes hydration. #### Reasons for Change This opens the way to multi-app patterns ([#21635](https://github.com/nuxt/nuxt/issues/21635)) and enables us to focus on a single way to access Nuxt app data - `useNuxtApp()`. #### Migration Steps The data is still available, but can be accessed with `useNuxtApp().payload`: ```diff - console.log(window.__NUXT__) + console.log(useNuxtApp().payload) ``` ### Directory index scanning 🚦 **Impact Level**: Medium #### What Changed Child folders in your `middleware/` folder are also scanned for `index` files and these are now also registered as middleware in your project. #### Reasons for Change Nuxt scans a number of folders automatically, including `middleware/` and `plugins/`. Child folders in your `plugins/` folder are scanned for `index` files and we wanted to make this behavior consistent between scanned directories. #### Migration Steps Probably no migration is necessary but if you wish to revert to previous behavior you can add a hook to filter out these middleware: ```ts export default defineNuxtConfig({ hooks: { 'app:resolve' (app) { app.middleware = app.middleware.filter(mw => !/\/index\.[^/]+$/.test(mw.path)) }, }, }) ``` ### Template Compilation Changes 🚦 **Impact Level**: Minimal #### What Changed Previously, Nuxt used `lodash/template` to compile templates located on the file system using the `.ejs` file format/syntax. In addition, we provided some template utilities (`serialize`, `importName`, `importSources`) which could be used for code-generation within these templates, which are now being removed. #### Reasons for Change In Nuxt v3 we moved to a 'virtual' syntax with a `getContents()` function which is much more flexible and performant. In addition, `lodash/template` has had a succession of security issues. These do not really apply to Nuxt projects because it is being used at build-time, not runtime, and by trusted code. However, they still appear in security audits. Moreover, `lodash` is a hefty dependency and is unused by most projects. Finally, providing code serialization functions directly within Nuxt is not ideal. Instead, we maintain projects like [unjs/knitwork](https://github.com/unjs/knitwork) which can be dependencies of your project, and where security issues can be reported/resolved directly without requiring an upgrade of Nuxt itself. #### Migration Steps We have raised PRs to update modules using EJS syntax, but if you need to do this yourself, you have three backwards/forwards-compatible alternatives: - Moving your string interpolation logic directly into `getContents()`. - Using a custom function to handle the replacement, such as in . - Use `es-toolkit/compat` (a drop-in replacement for lodash template), as a dependency of *your* project rather than Nuxt: ```diff + import { readFileSync } from 'node:fs' + import { template } from 'es-toolkit/compat' // ... addTemplate({ fileName: 'appinsights-vue.js' options: { /* some options */ }, - src: resolver.resolve('./runtime/plugin.ejs'), + getContents({ options }) { + const contents = readFileSync(resolver.resolve('./runtime/plugin.ejs'), 'utf-8') + return template(contents)({ options }) + }, }) ``` Finally, if you are using the template utilities (`serialize`, `importName`, `importSources`), you can replace them as follows with utilities from `knitwork`: ```ts import { genDynamicImport, genImport, genSafeVariableName } from 'knitwork' const serialize = (data: any) => JSON.stringify(data, null, 2).replace(/"\{(.+)\}"(?=,?$)/gm, r => JSON.parse(r).replace(/^\{(.*)\}$/, '$1')) const importSources = (sources: string | string[], { lazy = false } = {}) => { return toArray(sources).map((src) => { if (lazy) { return `const ${genSafeVariableName(src)} = ${genDynamicImport(src, { comment: `webpackChunkName: ${JSON.stringify(src)}` })}` } return genImport(src, genSafeVariableName(src)) }).join('\n') } const importName = genSafeVariableName ``` ::tip You can automate this step by running `npx codemod@latest nuxt/4/template-compilation-changes` :: ### Default TypeScript Configuration Changes 🚦 **Impact Level**: Minimal #### What Changed `compilerOptions.noUncheckedIndexedAccess` is now `true` instead of `false`. #### Reasons for Change This change is a follow up to a prior [3.12 config update](https://github.com/nuxt/nuxt/pull/27485) where we improved our defaults, mostly adhering to [TotalTypeScript's recommendations](https://www.totaltypescript.com/tsconfig-cheat-sheet). #### Migration Steps There are two approaches: 1. Run a typecheck on your app and fix any new errors (recommended). 2. Override the new default in your `nuxt.config.ts`: ```ts export default defineNuxtConfig({ typescript: { tsConfig: { compilerOptions: { noUncheckedIndexedAccess: false, }, }, }, }) ``` ### TypeScript Configuration Splitting 🚦 **Impact Level**: Minimal #### What Changed Nuxt now generates separate TypeScript configurations for different contexts to provide better type-checking experiences: 1. **New TypeScript configuration files**: Nuxt now generates additional TypeScript configurations: - `.nuxt/tsconfig.app.json` - For your app code (Vue components, composables, etc.) - `.nuxt/tsconfig.server.json` - For your server-side code (Nitro/server directory) - `.nuxt/tsconfig.node.json` - For your build-time code (modules, `nuxt.config.ts`, etc.) - `.nuxt/tsconfig.shared.json` - For code shared between app and server contexts (like types and non-environment specific utilities) - `.nuxt/tsconfig.json` - Legacy configuration for backward compatibility 2. **Backward compatibility**: Existing projects that extend `.nuxt/tsconfig.json` will continue to work as before. 3. **Opt-in project references**: New projects or those wanting better type checking can adopt TypeScript's project references feature. 4. **Context-specific type checking**: Each context now has appropriate compiler options and includes/excludes for its specific environment. 5. **New `typescript.nodeTsConfig` option**: You can now customize the TypeScript configuration for Node.js build-time code. #### Reasons for Change This change provides several benefits: 1. **Better type safety**: Each context (app, server, build-time) gets appropriate type checking with context-specific globals and APIs. 2. **Improved IDE experience**: Better IntelliSense and error reporting for different parts of your codebase. 3. **Cleaner separation**: Server code won't incorrectly suggest client-side APIs and vice versa. 4. **Performance**: TypeScript can more efficiently check code with properly scoped configurations. For example, auto-imports are not available in your `nuxt.config.ts` (but previously this was not flagged by TypeScript). And while IDEs recognized the separate context hinted by `tsconfig.json` in your `server/` directory, this was not reflected in type-checking (requiring a separate step). #### Migration Steps **No migration is required** - existing projects will continue to work as before. However, to take advantage of improved type checking, you can opt in to the new project references approach: 1. **Update your root `tsconfig.json`** to use project references: ::note If your `tsconfig.json` currently has an `"extends": "./.nuxt/tsconfig.json"` line, **remove it** before adding the references. Project references and extends are mutually exclusive. :: ```json { // Remove "extends": "./.nuxt/tsconfig.json" if present "files": [], "references": [ { "path": "./.nuxt/tsconfig.app.json" }, { "path": "./.nuxt/tsconfig.server.json" }, { "path": "./.nuxt/tsconfig.shared.json" }, { "path": "./.nuxt/tsconfig.node.json" } ] } ``` 2. **Remove any manual server `tsconfig.json`** files (like `server/tsconfig.json`) that extended `.nuxt/tsconfig.server.json`. 3. **Update your type checking scripts** to use the build flag for project references: ```diff - "typecheck": "nuxt prepare && vue-tsc --noEmit" + "typecheck": "nuxt prepare && vue-tsc -b --noEmit" ``` 4. **Move all type augmentations into their appropriate context**: - If you are augmenting types for the app context, move the files to the `app/` directory. - If you are augmenting types for the server context, move the files to the `server/` directory. - If you are augmenting types that are **shared between the app and server**, move the files to the `shared/` directory. ::warning Augmenting types from outside the `app/`, `server/`, or `shared/` directories will not work with the new project references setup. :: 5. **Configure TypeScript options** if needed: ```ts export default defineNuxtConfig({ typescript: { // customize tsconfig.app.json tsConfig: { // ... }, // customize tsconfig.shared.json sharedTsConfig: { // ... }, // customize tsconfig.node.json nodeTsConfig: { // ... }, }, nitro: { typescript: { // customize tsconfig.server.json tsConfig: { // ... }, }, }, }) ``` 6. **Update any CI/build scripts** that run TypeScript checking to ensure they use the new project references approach. The new configuration provides better type safety and IntelliSense for projects that opt in, while maintaining full backward compatibility for existing setups. ### Removal of Experimental Features 🚦 **Impact Level**: Minimal #### What Changed Four experimental features are no longer configurable in Nuxt 4: - `experimental.treeshakeClientOnly` will be `true` (default since v3.0) - `experimental.configSchema` will be `true` (default since v3.3) - `experimental.polyfillVueUseHead` will be `false` (default since v3.4) - `experimental.respectNoSSRHeader` will be `false` (default since v3.4) - `vite.devBundler` is no longer configurable - it will use `vite-node` by default #### Reasons for Change These options have been set to their current values for some time and we do not have a reason to believe that they need to remain configurable. #### Migration Steps - `polyfillVueUseHead` is implementable in user-land with [this plugin](https://github.com/nuxt/nuxt/blob/f209158352b09d1986aa320e29ff36353b91c358/packages/nuxt/src/head/runtime/plugins/vueuse-head-polyfill.ts#L10-L11) - `respectNoSSRHeader`is implementable in user-land with [server middleware](https://github.com/nuxt/nuxt/blob/c660b39447f0d5b8790c0826092638d321cd6821/packages/nuxt/src/core/runtime/nitro/no-ssr.ts#L8-L9) ### Removal of Top-Level `generate` Configuration 🚦 **Impact Level**: Minimal #### What Changed The top-level `generate` configuration option is no longer available in Nuxt 4. This includes all of its properties: - `generate.exclude` - for excluding routes from prerendering - `generate.routes` - for specifying routes to prerender #### Reasons for Change The top level `generate` configuration was a holdover from Nuxt 2. We've supported `nitro.prerender` for a while now, and it is the preferred way to configure prerendering in Nuxt 3+. #### Migration Steps Replace `generate` configuration with the corresponding `nitro.prerender` options: ```diff export default defineNuxtConfig({ - generate: { - exclude: ['/admin', '/private'], - routes: ['/sitemap.xml', '/robots.txt'] - } + nitro: { + prerender: { + ignore: ['/admin', '/private'], + routes: ['/sitemap.xml', '/robots.txt'] + } + } }) ``` ::read-more{to="https://nitro.build/config#prerender"} Read more about Nitro's prerender configuration options. :: ## Nuxt 2 vs. Nuxt 3+ In the table below, there is a quick comparison between 3 versions of Nuxt: | Feature / Version | Nuxt 2 | Nuxt Bridge | Nuxt 3+ | | ----------------------- | ---------- | ----------- | ---------- | | Vue | 2 | 2 | 3 | | Stability | 😊 Stable | 😊 Stable | 😊 Stable | | Performance | 🏎 Fast | ✈️ Faster | 🚀 Fastest | | Nitro Engine | ❌ | ✅ | ✅ | | ESM support | 🌙 Partial | 👍 Better | ✅ | | TypeScript | ☑️ Opt-in | 🚧 Partial | ✅ | | Composition API | ❌ | 🚧 Partial | ✅ | | Options API | ✅ | ✅ | ✅ | | Components Auto Import | ✅ | ✅ | ✅ | | ` ``` **Solution**: You can use [`useCookie`](https://nuxt.com/docs/3.x/api/composables/use-cookie): ```html ``` ### Inconsistent Data **Problem**: Different data between server and client. ```html ``` **Solution**: Use SSR-friendly state: ```html ``` ### Conditional Rendering Based on Client State **Problem**: Using client-only conditions during SSR. ```html ``` **Solution**: Use media queries or handle it client-side: ```html ``` ### Third-party Libraries with Side Effects **Problem**: Libraries that modify the DOM or have browser dependencies (this happens a LOT with tag managers). ```html ``` **Solution**: Initialise libraries after hydration has completed: ```html ``` ### Dynamic Content Based on Time **Problem**: Content that changes based on current time. ```html ``` **Solution**: Use [`NuxtTime`](https://nuxt.com/docs/3.x/api/components/nuxt-time) component or handle it client-side: ```html ``` ```html ``` ## In summary 1. **Use SSR-friendly composables**: [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch), [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data), [`useState`](https://nuxt.com/docs/3.x/api/composables/use-state) 2. **Wrap client-only code**: Use [`ClientOnly`](https://nuxt.com/docs/3.x/api/components/client-only) component for browser-specific content 3. **Consistent data sources**: Ensure server and client uses the same data 4. **Avoid side effects in setup**: Move browser-dependent code to `onMounted` ::tip You can read the [Vue documentation on SSR hydration mismatch](https://vuejs.org/guide/scaling-up/ssr#hydration-mismatch) for a better understanding of hydration. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt performance" description: "Best practices for improving performance of Nuxt apps." canonical_url: "https://nuxt.com/docs/3.x/guide/best-practices/performance" --- # Nuxt performance > Best practices for improving performance of Nuxt apps. Nuxt comes with built-in features designed to improve your application's performance and contribute to better [Core Web Vitals](https://web.dev/articles/vitals). There are also multiple Nuxt core modules that assist in improving performance in specific areas. This guide outlines best practices to optimize performance of your Nuxt application. ## Built-in Features Nuxt offers several built-in features that help you optimize performance of your website. Understanding how these features work is crucial for achieving blazingly-fast performance. ### Links [``](https://nuxt.com/docs/3.x/api/components/nuxt-link) is a drop-in replacement for both Vue Router's `` component and HTML's `` tag. It intelligently determines whether the link is internal or external and renders it accordingly with available optimizations (prefetching, default attributes, etc.) ```html About page ``` Nuxt automatically includes smart prefetching. That means it detects when a link is visible (by default), either in the viewport or when scrolling and prefetches the JavaScript for those pages so that they are ready when the user clicks the link. You can also opt for prefetching on interaction instead: ```ts export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { prefetchOn: { interaction: true, visibility: false, }, }, }, }, }) ``` ::read-more{title="NuxtLink" to="https://nuxt.com/docs/3.x/api/components/nuxt-link"} :: ### Hybrid Rendering In more complex applications, we may need a full control over how our application is rendered to support cases where some pages could be generated at build time, while others should be client-side rendered Hybrid rendering allows different caching rules per route using Route Rules and decides how the server should respond to a new request on a given URL: ```ts export default defineNuxtConfig({ routeRules: { '/': { prerender: true, }, '/products/**': { swr: 3600, }, '/blog': { isr: 3600, }, '/admin/**': { ssr: false, }, }, }) ``` Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using Nitro caching layer. ::read-more{title="Hybrid rendering" to="https://nuxt.com/docs/3.x/guide/concepts/rendering#hybrid-rendering"} :: ### Lazy Loading Components To dynamically import a component (also known as lazy-loading a component) all you need to do is add the Lazy prefix to the component's name. This is useful if the component is not always needed. ```html ``` By using the Lazy prefix you can delay loading the component code until the right moment, which can be helpful for optimizing your JavaScript bundle size. ::read-more{title="Lazy loading components" to="https://nuxt.com/docs/3.x/directory-structure/components#dynamic-imports"} :: ### Lazy Hydration It is not always necessary to hydrate (or make interactive) all the components of your site on the initial load. Using lazy hydration, you can control when components can have their code loaded, which can improve the time-to-interactive metric for your app. Nuxt allows you to control when components become interactive with lazy hydration (added in Nuxt v3.16). ```html ``` To optimize your app, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks. ::read-more{title="Lazy hydration" to="https://nuxt.com/docs/3.x/directory-structure/components#delayed-or-lazy-hydration"} :: ### Fetching data To avoid fetching same data twice (once on the server and once on client) Nuxt provides [`useFetch`](https://nuxt.com/docs/3.x/api/composables/use-fetch) and [`useAsyncData`](https://nuxt.com/docs/3.x/api/composables/use-async-data). They ensure that if an API call is made on the server, the data is forwarded to the client in the payload instead of being fetched again. ::read-more{title="Data fetching" to="https://nuxt.com/docs/3.x/getting-started/data-fetching"} :: ## Core Nuxt Modules Apart from Nuxt's built-in features, there are also core modules maintained by the Nuxt team which help improve performance even further. These modules help handle assets such as images, custom fonts, or third party scripts. ### Images Unoptimized images can have a significant negative impact on your website performance, specifically the [Largest Contentful Paint (LCP)](https://web.dev/articles/lcp) score. In Nuxt we can use [Nuxt Image](https://image.nuxt.com/) module that is a plug-and-play image optimization for Nuxt apps. It allows resizing and transforming your images using built-in optimizer or your favorite images CDN. ::video-accordion{title="Watch the video by LearnVue about Nuxt Image" videoId="_UBff2eqGY0"} :: [``](https://nuxt.com/docs/3.x/api/components/nuxt-img) is a drop-in replacement for the native `` tag that comes with following enhancements: - Uses built-in provider to optimize local and remote images - Converts `src` to provider optimized URLs with modern formats such as WebP or Avif - Automatically resizes images based on `width` and `height` - Generates responsive `sizes` when providing sizes option - Supports native `lazy loading` as well as other `` attributes Images in your website can usually be separated by importance; the ones that are needed to be delivered first at initial load (i.e. `Largest Contentful Paint`), and the ones that can be loaded later or when specifically needed. For that, we could use the following optimizations: ```html ``` ::read-more{title="Nuxt Image" to="https://image.nuxt.com/usage/nuxt-img"} :: ### Fonts [Nuxt Fonts](https://fonts.nuxt.com/) will automatically optimize your fonts (including custom fonts) and remove external network requests for improved privacy and performance. It includes built-in automatic self-hosting for any font file which means you can optimally load web fonts with reduced layout shift, thanks to the underlying package [fontaine](https://github.com/unjs/fontaine). ::video-accordion{title="Watch the talk by Daniel Roe about the idea behind Nuxt Fonts" videoId="D3F683UViBY"} :: Nuxt Fonts processes all your CSS and does the following things automatically when it encounters a font-family declaration. 1. **Resolves fonts** – Looks for font files in public/, then checks web providers like Google, Bunny, and Fontshare. 2. **Generates @font-face rules** – Injects CSS rules to load fonts from the correct sources. 3. **Proxies & caches fonts** – Rewrites URLs to `/_fonts`, downloads and caches fonts locally. 4. **Creates fallback metrics** – Adjusts local system fonts to match web fonts, reducing layout shift ([CLS](https://web.dev/articles/cls)). 5. **Includes fonts in build** – Bundles fonts with your project, hashing file names and setting long-lived cache headers. It supports multiple providers that are designed to be pluggable and extensible, so no matter your setup you should be able to use an existing provider or write your own. ### Scripts Third-party resources like analytics tools, video embeds, maps, and social media integrations enhance website functionality but can significantly degrade user experience and negatively impact [Interaction to Next Paint (INP)](https://web.dev/articles/inp) and Largest Contentful Paint (LCP) scores. [Nuxt Scripts](https://scripts.nuxt.com/) lets you load third-party scripts with better performance, privacy, security and DX. ::video-accordion{title="Watch the video by Alex Lichter about Nuxt Scripts" videoId="sjMqUUvH9AE"} :: Nuxt Scripts provides an abstraction layer on top of third-party scripts, providing SSR support and type-safety and while still giving you full low-level control over how a script is loaded. ```ts const { onLoaded, proxy } = useScriptGoogleAnalytics( { id: 'G-1234567', scriptOptions: { trigger: 'manual', }, }, ) // queue events to be sent when ga loads proxy.gtag('config', 'UA-123456789-1') // or wait until ga is loaded onLoaded((gtag) => { // script loaded }) ``` ::read-more{title="Nuxt Scripts" to="https://scripts.nuxt.com/scripts"} :: ## Profiling Tools To improve performance, we need to first know how to measure it, starting with measuring performance during development - on local environment, and then moving to auditing application that are deployed on production. ### Nuxi Analyze [This](https://nuxt.com/docs/3.x/api/commands/analyze) command of `nuxi` allows to analyze the production bundle or your Nuxt application. It leverages `vite-bundle-visualizer` (similar to `webpack-bundle-analyzer`) to generate a visual representation of your application's bundle, making it easier to identify which components take up the most space. When you see a large block in the visualization, it often signals an opportunity for optimization—whether by splitting it into smaller parts, implementing lazy loading, or replacing it with a more efficient alternative, especially for third-party libraries. Large blocks containing multiple elements can often be reduced by importing only the necessary components rather than entire modules while large standalone blocks may be better suited for lazy loading rather than being included in the main bundle. ### Nuxt DevTools The [Nuxt DevTools](https://nuxt.com/modules/devtools) gives you insights and transparency about your Nuxt App to identify performance gaps and seamlessly manage your app configurations. ![Nuxt DevTools example](https://user-images.githubusercontent.com/11247099/217670806-fb39aeff-3881-44e5-b9c8-6c757f5925fc.png) It comes with several features we can use to measure performance of Nuxt apps: 1. **Timeline** – Tracks time spent on rendering, updating, and initializing components to identify performance bottlenecks. 2. **Assets** – Displays file sizes (e.g., images) without transformations. 3. **Render Tree** – Shows connections between Vue components, scripts, and styles to optimize dynamic loading. 4. **Inspect** – Lists all files used in the Vue app with their size and evaluation time. ### Chrome DevTools Chrome DevTools come with two useful tabs for measuring performance; `Performance` and `Lighthouse`. When you open the [Performance](https://developer.chrome.com/docs/devtools/performance/overview) panel, it instantly shows your local **Largest Contentful Paint (LCP)** and **Cumulative Layout Shift (CLS)** scores (good, needs improvement, or bad). If you interact with the page, it also captures **Interaction to Next Paint (INP)**, giving you a full view of your Core Web Vitals based on your device and network. ![Chrome DevTools Performance Panel](https://developer.chrome.com/static/docs/devtools/performance/image/cpu-throttling_856.png) [Lighthouse](https://developer.chrome.com/docs/devtools/lighthouse) audits performance, accessibility, SEO, progressive web apps, and best practices. It runs tests on your page and generates a report. Use failing audits as a guide to improve your site. ![Lighthouse](https://developer.chrome.com/static/docs/lighthouse/images/lighthouse-overview_720.png) Each audit has a reference document explaining why the audit is important, as well as how to fix it. ### PageSpeed Insights [PageSpeed Insights (PSI)](https://developers.google.com/speed/docs/insights/v5/about) reports on the user experience of a page on both mobile and desktop devices, and provides suggestions on how that page may be improved. It provides both lab and field data about a page. Lab data is useful for debugging issues, as it is collected in a controlled environment while field data is useful for capturing true, real-world user experience. ### Web Page Test [WebPageTest](https://www.webpagetest.org/) is a web performance tool providing deep diagnostic information about how a page performs under a variety of conditions. Each test can be run from different locations around the world, on real browsers, over any number of customizable network conditions. ## Common problems When building more complex Nuxt applications, you will probably encounter some of the problems listed below. Understanding these problems and fixing them will help you improve performance of your website. ### Overusing plugins **Problem**: A large number of plugins can cause performance issues, especially if they require expensive computations or take too long to initialize. Since plugins run during the hydration phase, inefficient setups can block rendering and degrade the user experience. **Solution**: Inspect your plugins and see if some of them could be implemented rather as a composable or utility function instead. ### Unused code / dependencies **Problem**: With the development of the project, there can be a case where there will be some unused code or a dependency. This additional functionality may not be used or needed while it will be increase the bundle size of our project. **Solution**: Inspect your `package.json` for unused dependencies and analyze your code for unused utils/composables/functions. ### Not using Vue Performance tips **Problem**: [Vue documentation](https://vuejs.org/guide/best-practices/performance) lists several Performance improvements we can use in our Nuxt projects as well but as they are part of Vue documentation, developers tend to forget about it and focus on Nuxt specific improvements only - while Nuxt application is still a Vue project. **Solution**: Use concepts such as `shallowRef`, `v-memo`, `v-once`, etc to improve performance. ### Not following patterns **Problem**: The more people are currently working on the project, the more difficult it will be to maintain the stable codebase. Developers have a tendency to introduce new concepts they've seen in another project which can cause conflicts and problems with performance. **Solution**: Establish rules and patterns in the project such as [Good practices and Design Patterns for Vue Composables](https://dev.to/jacobandrewsky/good-practices-and-design-patterns-for-vue-composables-24lk) ### Trying to load everything at the same time **Problem**: When a page is loaded and it is not correctly instructed about the order of loading elements it will result in fetching everything at the same time - which can be slow and result in bad User Experience. **Solution**: Use concepts such as Progressive Enhancement where core webpage content is set first, then more nuanced and technically rigorous layers of presentation and features are added on top as the browser/internet connection allow. ## Useful Resources To learn more about various techniques for improving performance, take a look at the following resources: 1. [Apply instant loading with the PRPL pattern](https://web.dev/articles/apply-instant-loading-with-prpl) 2. [Perceived performance](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Performance/Perceived_performance) 3. [Understanding Critical Rendering Path](https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Critical_rendering_path) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt Plugins" description: "Best practices when using Nuxt plugins." canonical_url: "https://nuxt.com/docs/3.x/guide/best-practices/plugins" --- # Nuxt Plugins > Best practices when using Nuxt plugins. Plugins in Nuxt allow you to extend your application with additional functionality. However, improper use can lead to performance bottlenecks. This guide outlines best practices to optimize your Nuxt plugins. ## Avoid costly plugin setup A large number of plugins can cause performance issues, especially if they require expensive computations or take too long to initialize. Since plugins run during the hydration phase, inefficient setups can block rendering and degrade the user experience. ## Use Composition whenever possible Whenever possible, favor composition over plugins. Just like in Vue, many utilities and composables can be used directly without the need for a plugin. This keeps your project lightweight and improves maintainability. ## If `async`, enable `parallel` By default, all plugins loads synchronously. When defining asynchronous plugins, setting `parallel: true` allows multiple plugins to load concurrently, improving performance by preventing blocking operations. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Auto-imports" description: "Nuxt auto-imports components, composables, helper functions and Vue APIs." canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/auto-imports" --- # Auto-imports > Nuxt auto-imports components, composables, helper functions and Vue APIs. Nuxt auto-imports components, composables and [Vue.js APIs](https://vuejs.org/api/) to use across your application without explicitly importing them. ```vue twoslash [app.vue] ``` Thanks to its opinionated directory structure, Nuxt can auto-import your [`components/`](https://nuxt.com/docs/3.x/directory-structure/components), [`composables/`](https://nuxt.com/docs/3.x/directory-structure/composables) and [`utils/`](https://nuxt.com/docs/3.x/directory-structure/utils). Contrary to a classic global declaration, Nuxt preserves typings, IDEs completions and hints, and **only includes what is used in your production code**. ::note In the docs, every function that is not explicitly imported is auto-imported by Nuxt and can be used as-is in your code. You can find a reference for auto-imported components, composables and utilities in the [API section](https://nuxt.com/docs/3.x/api). :: ::note In the [`server`](https://nuxt.com/docs/3.x/directory-structure/server) directory, Nuxt auto-imports exported functions and variables from `server/utils/`. :: ::note You can also auto-import functions exported from custom folders or third-party packages by configuring the [`imports`](https://nuxt.com/docs/3.x/api/nuxt-config#imports) section of your `nuxt.config` file. :: ## Built-in Auto-imports Nuxt auto-imports functions and composables to perform [data fetching](https://nuxt.com/docs/3.x/getting-started/data-fetching), get access to the [app context](https://nuxt.com/docs/3.x/api/composables/use-nuxt-app) and [runtime config](https://nuxt.com/docs/3.x/guide/going-further/runtime-config), manage [state](https://nuxt.com/docs/3.x/getting-started/state-management) or define components and plugins. ```vue twoslash ``` Vue exposes Reactivity APIs like `ref` or `computed`, as well as lifecycle hooks and helpers that are auto-imported by Nuxt. ```vue twoslash ``` ### Vue and Nuxt Composables When you are using the built-in Composition API composables provided by Vue and Nuxt, be aware that many of them rely on being called in the right *context*. During a component lifecycle, Vue tracks the temporary instance of the current component (and similarly, Nuxt tracks a temporary instance of `nuxtApp`) via a global variable, and then unsets it in the same tick. This is essential when server rendering, both to avoid cross-request state pollution (leaking a shared reference between two users) and to avoid leakage between different components. That means that (with very few exceptions) you cannot use them outside a Nuxt plugin, Nuxt route middleware or Vue setup function. On top of that, you must use them synchronously - that is, you cannot use `await` before calling a composable, except within ` ``` ### Disabling Auto-imports If you want to disable auto-importing composables and utilities, you can set `imports.autoImport` to `false` in the `nuxt.config` file. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ imports: { autoImport: false, }, }) ``` This will disable auto-imports completely but it's still possible to use [explicit imports](https://nuxt.com/docs/3.x/guide/concepts/auto-imports#explicit-imports) from `#imports`. ### Partially Disabling Auto-imports If you want framework-specific functions like `ref` to remain auto-imported but wish to disable auto-imports for your own code (e.g., custom composables), you can set the `imports.scan` option to `false` in your `nuxt.config.ts` file: ```ts export default defineNuxtConfig({ imports: { scan: false, }, }) ``` With this configuration: - Framework functions like `ref`, `computed`, or `watch` will still work without needing manual imports. - Custom code, such as composables, will need to be manually imported in your files. ::warning **Caution:** This setup has certain limitations: - If you structure your project with layers, you will need to explicitly import the composables from each layer, rather than relying on auto-imports. - This breaks the layer system’s override feature. If you use `imports.scan: false`, ensure you understand this side-effect and adjust your architecture accordingly. :: ## Auto-imported Components Nuxt also automatically imports components from your `~/components` directory, although this is configured separately from auto-importing composables and utility functions. ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/components"} :: To disable auto-importing components from your own `~/components` directory, you can set `components.dirs` to an empty array (though note that this will not affect components added by modules). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: { dirs: [], }, }) ``` ## Auto-import from Third-Party Packages Nuxt also allows auto-importing from third-party packages. ::tip If you are using the Nuxt module for that package, it is likely that the module has already configured auto-imports for that package. :: For example, you could enable the auto-import of the `useI18n` composable from the `vue-i18n` package like this: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ imports: { presets: [ { from: 'vue-i18n', imports: ['useI18n'], }, ], }, }) ``` ::video-accordion{title="Watch a video from Alexander Lichter on how to easily set up custom auto imports" videoId="FT2LQJ2NvVI"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Code Style" description: "Nuxt supports ESLint out of the box" canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/code-style" --- # Code Style > Nuxt supports ESLint out of the box ## ESLint The recommended approach for Nuxt is to enable ESLint support using the [`@nuxt/eslint`](https://eslint.nuxt.com/packages/module) module, that will setup project-aware ESLint configuration for you. ::callout{icon="i-lucide-lightbulb"} The module is designed for the [new ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files) which is the [default format since ESLint v9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/). If you are using the legacy `.eslintrc` config, you will need to [configure manually with `@nuxt/eslint-config`](https://eslint.nuxt.com/packages/config#customizing-the-config). We highly recommend you to migrate over the flat config to be future-proof. :: ## Quick Setup ```bash npx nuxt module add eslint ``` Start your Nuxt app, a `eslint.config.mjs` file will be generated under your project root. You can customize it as needed. You can learn more about the module and customizations in [Nuxt ESLint's documentation](https://eslint.nuxt.com/packages/module). ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "ES Modules" description: "Nuxt uses native ES modules." canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/esm" --- # ES Modules > Nuxt uses native ES modules. This guide helps explain what ES Modules are and how to make a Nuxt app (or upstream library) compatible with ESM. ## Background ### CommonJS Modules CommonJS (CJS) is a format introduced by Node.js that allows sharing functionality between isolated JavaScript modules ([read more](https://nodejs.org/api/modules.html)). You might be already familiar with this syntax: ```js const a = require('./a') module.exports.a = a ``` Bundlers like webpack and Rollup support this syntax and allow you to use modules written in CommonJS in the browser. ### ESM Syntax Most of the time, when people talk about ESM vs. CJS, they are talking about a different syntax for writing [modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). ```js import a from './a' export { a } ``` Before ECMAScript Modules (ESM) became a standard (it took more than 10 years!), tooling like [webpack](https://webpack.js.org/guides/ecma-script-modules/) and even languages like TypeScript started supporting so-called **ESM syntax**. However, there are some key differences with actual spec; here's [a helpful explainer](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/). ### What is 'Native' ESM? You may have been writing your app using ESM syntax for a long time. After all, it's natively supported by the browser, and in Nuxt 2 we compiled all the code you wrote to the appropriate format (CJS for server, ESM for browser). When adding modules to your package, things were a little different. A sample library might expose both CJS and ESM versions, and let us pick which one we wanted: ```json { "name": "sample-library", "main": "dist/sample-library.cjs.js", "module": "dist/sample-library.esm.js" } ``` So in Nuxt 2, the bundler (webpack) would pull in the CJS file ('main') for the server build and use the ESM file ('module') for the client build. ::note The `module` field is a convention used by bundlers like webpack and Rollup, but is not recognized by Node.js itself. Node.js only uses the [`exports`](https://nodejs.org/api/packages.html#exports) and [`main`](https://nodejs.org/api/packages.html#main) fields for module resolution. :: However, in recent Node.js LTS releases, it is now possible to [use native ESM module](https://nodejs.org/api/esm.html) within Node.js. That means that Node.js itself can process JavaScript using ESM syntax, although it doesn't do it by default. The two most common ways to enable ESM syntax are: - set `"type": "module"` within your `package.json` and keep using `.js` extension - use the `.mjs` file extensions (recommended) This is what we do for Nuxt Nitro; we output a `.output/server/index.mjs` file. That tells Node.js to treat this file as a native ES module. ### What Are Valid Imports in a Node.js Context? When you `import` a module rather than `require` it, Node.js resolves it differently. For example, when you import `sample-library`, Node.js will look for the `exports` entry in that library's `package.json`, or fall back to the `main` entry if `exports` is not defined. This is also true of dynamic imports, like `const b = await import('sample-library')`. Node supports the following kinds of imports (see [docs](https://nodejs.org/api/packages.html#determining-module-system)): 1. files ending in `.mjs` - these are expected to use ESM syntax 2. files ending in `.cjs` - these are expected to use CJS syntax 3. files ending in `.js` - these are expected to use CJS syntax unless their `package.json` has `"type": "module"` ### What Kinds of Problems Can There Be? For a long time module authors have been producing ESM-syntax builds but using conventions like `.esm.js` or `.es.js`, which they have added to the `module` field in their `package.json`. This hasn't been a problem until now because they have only been used by bundlers like webpack, which don't especially care about the file extension. However, if you try to import a package with an `.esm.js` file in a Node.js ESM context, it won't work, and you'll get an error like: ```bash [Terminal] (node:22145) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. /path/to/index.js:1 export default {} ^^^^^^ SyntaxError: Unexpected token 'export' at wrapSafe (internal/modules/cjs/loader.js:1001:16) at Module._compile (internal/modules/cjs/loader.js:1049:27) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10) .... at async Object.loadESM (internal/process/esm_loader.js:68:5) ``` You might also get this error if you have a named import from an ESM-syntax build that Node.js thinks is CJS: ```bash [Terminal] file:///path/to/index.mjs:5 import { named } from 'sample-library' ^^^^^ SyntaxError: Named export 'named' not found. The requested module 'sample-library' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export, for example using: import pkg from 'sample-library'; const { named } = pkg; at ModuleJob._instantiate (internal/modules/esm/module_job.js:120:21) at async ModuleJob.run (internal/modules/esm/module_job.js:165:5) at async Loader.import (internal/modules/esm/loader.js:177:24) at async Object.loadESM (internal/process/esm_loader.js:68:5) ``` ## Troubleshooting ESM Issues If you encounter these errors, the issue is almost certainly with the upstream library. They need to [fix their library](https://nuxt.com/docs/3.x/guide/concepts/esm#library-author-guide) to support being imported by Node. ### Transpiling Libraries In the meantime, you can tell Nuxt not to try to import these libraries by adding them to `build.transpile`: ```ts twoslash export default defineNuxtConfig({ build: { transpile: ['sample-library'], }, }) ``` You may find that you *also* need to add other packages that are being imported by these libraries. ### Aliasing Libraries In some cases, you may also need to manually alias the library to the CJS version, for example: ```ts twoslash export default defineNuxtConfig({ alias: { 'sample-library': 'sample-library/dist/sample-library.cjs.js', }, }) ``` ### Default Exports A dependency with CommonJS format, can use `module.exports` or `exports` to provide a default export: ```js [node_modules/cjs-pkg/index.js] module.exports = { test: 123 } // or exports.test = 123 ``` This normally works well if we `require` such dependency: ```js [test.cjs] const pkg = require('cjs-pkg') console.log(pkg) // { test: 123 } ``` [Node.js in native ESM mode](https://nodejs.org/api/esm.html#interoperability-with-commonjs), [typescript with `esModuleInterop` enabled](https://www.typescriptlang.org/tsconfig/#esModuleInterop) and bundlers such as webpack, provide a compatibility mechanism so that we can default import such library. This mechanism is often referred to as "interop require default": ```js import pkg from 'cjs-pkg' console.log(pkg) // { test: 123 } ``` However, because of the complexities of syntax detection and different bundle formats, there is always a chance that the interop default fails and we end up with something like this: ```js import pkg from 'cjs-pkg' console.log(pkg) // { default: { test: 123 } } ``` Also when using dynamic import syntax (in both CJS and ESM files), we always have this situation: ```js import('cjs-pkg').then(console.log) // [Module: null prototype] { default: { test: '123' } } ``` In this case, we need to manually interop the default export: ```js // Static import import { default as pkg } from 'cjs-pkg' // Dynamic import import('cjs-pkg').then(m => m.default || m).then(console.log) ``` For handling more complex situations and more safety, we recommend and internally use [mlly](https://github.com/unjs/mlly) in Nuxt that can preserve named exports. ```js import { interopDefault } from 'mlly' // Assuming the shape is { default: { foo: 'bar' }, baz: 'qux' } import myModule from 'my-module' console.log(interopDefault(myModule)) // { foo: 'bar', baz: 'qux' } ``` ## Library Author Guide The good news is that it's relatively simple to fix issues of ESM compatibility. There are two main options: 1. **You can rename your ESM files to end with `.mjs`.** *This is the recommended and simplest approach.* You may have to sort out issues with your library's dependencies and possibly with your build system, but in most cases, this should fix the problem for you. It's also recommended to rename your CJS files to end with `.cjs`, for the greatest explicitness. 2. **You can opt to make your entire library ESM-only**. This would mean setting `"type": "module"` in your `package.json` and ensuring that your built library uses ESM syntax. However, you may face issues with your dependencies - and this approach means your library can *only* be consumed in an ESM context. ### Migration The initial step from CJS to ESM is updating any usage of `require` to use `import` instead: ::code-group ```ts [Before] module.exports = function () { /* ... */ } exports.hello = 'world' ``` ```ts [After] export default function () { /* ... */ } export const hello = 'world' ``` :: ::code-group ```js [Before] const myLib = require('my-lib') ``` ```js [After] import myLib from 'my-lib' // or const dynamicMyLib = await import('my-lib').then(lib => lib.default || lib) ``` :: In ESM Modules, unlike CJS, `require`, `require.resolve`, `__filename` and `__dirname` globals are not available and should be replaced with `import()` and `import.meta.filename`. ::code-group ```js [Before] const { join } = require('node:path') const newDir = join(__dirname, 'new-dir') ``` ```js [After] import { fileURLToPath } from 'node:url' const newDir = fileURLToPath(new URL('./new-dir', import.meta.url)) ``` :: ::code-group ```js [Before] const someFile = require.resolve('./lib/foo.js') ``` ```js [After] import { resolvePath } from 'mlly' const someFile = await resolvePath('my-lib', { url: import.meta.url }) ``` :: ### Best Practices - Prefer named exports rather than default export. This helps reduce CJS conflicts. (see [Default exports](https://nuxt.com/docs/3.x/guide/concepts/esm#default-exports) section) - Avoid depending on Node.js built-ins and CommonJS or Node.js-only dependencies as much as possible to make your library usable in Browsers and Edge Workers without needing Nitro polyfills. - Use new `exports` field with conditional exports. ([read more](https://nodejs.org/api/packages.html#conditional-exports)). ```json { "exports": { ".": { "import": "./dist/mymodule.mjs" } } } ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Modules" description: "Nuxt provides a module system to extend the framework core and simplify integrations." canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/modules" --- # Modules > Nuxt provides a module system to extend the framework core and simplify integrations. ## Exploring Nuxt Modules When developing production-grade applications with Nuxt you might find that the framework's core functionality is not enough. Nuxt can be extended with configuration options and plugins, but maintaining these customizations across multiple projects can be tedious, repetitive and time-consuming. On the other hand, supporting every project's needs out of the box would make Nuxt very complex and hard to use. This is one of the reasons why Nuxt provides a module system that makes it possible to extend the core. Nuxt modules are async functions that sequentially run when starting Nuxt in development mode using [`nuxt dev`](https://nuxt.com/docs/3.x/api/commands/dev) or building a project for production with [`nuxt build`](https://nuxt.com/docs/3.x/api/commands/build). They can override templates, configure webpack loaders, add CSS libraries, and perform many other useful tasks. Best of all, Nuxt modules can be distributed in npm packages. This makes it possible for them to be reused across projects and shared with the community, helping create an ecosystem of high-quality add-ons. ::read-more{to="https://nuxt.com/modules"} Explore Nuxt Modules :: ## Add Nuxt Modules Once you have installed the modules you can add them to your [`nuxt.config.ts`](https://nuxt.com/docs/3.x/directory-structure/nuxt-config) file under the `modules` property. Module developers usually provide additional steps and details for usage. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ modules: [ // Using package name (recommended usage) '@nuxtjs/example', // Load a local module './modules/example', // Add module with inline-options ['./modules/example', { token: '123' }], // Inline module definition async (inlineOptions, nuxt) => { }, ], }) ``` ::warning Nuxt modules are now build-time-only, and the `buildModules` property used in Nuxt 2 is deprecated in favor of `modules`. :: ## Disabling Modules You can disable a module by setting its config key to `false` in your Nuxt config. This is particularly useful when you want to disable modules inherited from layers. ```ts [nuxt.config.ts] export default defineNuxtConfig({ // Disable `@nuxt/image` module image: false, }) ``` ::read-more{to="https://nuxt.com/docs/3.x/guide/going-further/layers#disabling-modules-from-layers"} :: ## Create a Nuxt Module Everyone has the opportunity to develop modules and we cannot wait to see what you will build. ::read-more{to="https://nuxt.com/docs/3.x/guide/modules" title="Module Author Guide"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt Lifecycle" description: "Understanding the lifecycle of Nuxt applications can help you gain deeper insights into how the framework operates, especially for both server-side and client-side rendering." canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/nuxt-lifecycle" --- # Nuxt Lifecycle > Understanding the lifecycle of Nuxt applications can help you gain deeper insights into how the framework operates, especially for both server-side and client-side rendering. The goal of this chapter is to provide a high-level overview of the different parts of the framework, their execution order, and how they work together. ## Server lifecycle On the server, the following steps are executed for every initial request to your application: ::steps ### Server plugins :badge[once]{color="info" .align-middle} Nuxt is powered by [Nitro](https://nitro.build/), a modern server engine. When Nitro starts, it initializes and executes the plugins under the [`/server/plugins`](https://nuxt.com/docs/3.x/directory-structure/server#server-plugins) directory. These plugins can: - Capture and handle application-wide errors. - Register hooks that execute when Nitro shuts down. - Register hooks for request lifecycle events, such as modifying responses. :::callout{icon="i-lucide-lightbulb"} Nitro plugins are executed only once when the server starts. In a serverless environment, the server boots on each incoming request, and so do the Nitro plugins. However, they are not awaited. ::: :::read-more{to="https://nuxt.com/docs/3.x/directory-structure/server#server-plugins"} ::: ### Server middleware After initializing the Nitro server, middleware under `server/middleware/` is executed for every request. Middleware can be used for tasks such as authentication, logging, or request transformation. :::warning Returning a value from middleware will terminate the request and send the returned value as the response. This behavior should generally be avoided to ensure proper request handling! ::: :::read-more{to="https://nuxt.com/docs/3.x/directory-structure/server#server-middleware"} ::: ### App plugins The Vue and Nuxt instances are created first. Afterward, Nuxt executes its app plugins. This includes: - Built-in plugins, such as Vue Router and `unhead`. - Custom plugins located in the `plugins/` directory, including those without a suffix (e.g., `myPlugin.ts`) and those with the `.server` suffix (e.g., `myServerPlugin.server.ts`). Plugins execute in a specific order and may have dependencies on one another. For more details, including execution order and parallelism, refer to the [Plugins documentation](https://nuxt.com/docs/3.x/directory-structure/plugins). :::callout{icon="i-lucide-lightbulb"} After this step, Nuxt calls the [`app:created`](https://nuxt.com/docs/3.x/api/advanced/hooks#app-hooks-runtime) hook, which can be used to execute additional logic. ::: :::read-more{to="https://nuxt.com/docs/3.x/directory-structure/plugins"} ::: ### Route validation After initializing plugins and before executing middleware, Nuxt calls the `validate` method if it is defined in the `definePageMeta` function. The `validate` method, which can be synchronous or asynchronous, is often used to validate dynamic route parameters. - The `validate` function should return `true` if the parameters are valid. - If validation fails, it should return `false` or an object containing a `status` and/or `statusText` to terminate the request. For more information, see the [Route Validation documentation](https://nuxt.com/docs/3.x/getting-started/routing#route-validation). :::read-more{to="https://nuxt.com/docs/3.x/getting-started/routing#route-validation"} ::: ### App middleware Middleware allows you to run code before navigating to a particular route. It is often used for tasks such as authentication, redirection, or logging. In Nuxt, there are three types of middleware: - **Global route middleware** - **Named route middleware** - **Anonymous (or inline) route middleware** Nuxt executes all global middleware on the initial page load (both on server and client) and then again before any client-side navigation. Named and anonymous middleware are executed only on the routes specified in the middleware property of the page(route) meta defined in the corresponding page components. For details about each type and examples, see the [Middleware documentation](https://nuxt.com/docs/3.x/directory-structure/middleware). Any redirection on the server will result in a `Location:` header being sent to the browser; the browser then makes a fresh request to this new location. All application state will be reset when this happens, unless persisted in a cookie. :::read-more{to="https://nuxt.com/docs/3.x/directory-structure/middleware"} ::: ### Page and components Nuxt renders the page and its components and fetches any required data with `useFetch` and `useAsyncData` during this step. Since there are no dynamic updates and no DOM operations occur on the server, Vue lifecycle hooks such as `onBeforeMount`, `onMounted`, and subsequent hooks are **NOT** executed during SSR. By default, Vue pauses dependency tracking during SSR for better performance. :::callout{icon="i-lucide-lightbulb"} There is no reactivity on the server side because Vue SSR renders the app top-down as static HTML, making it impossible to go back and modify content that has already been rendered. ::: :::important You should avoid code that produces side effects that need cleanup in root scope of ` ``` On the initial request, the `counter` ref is initialized in the server since it is rendered inside the `

` tag. The contents of `handleClick` is never executed here. During hydration in the browser, the `counter` ref is re-initialized. The `handleClick` finally binds itself to the button; Therefore it is reasonable to deduce that the body of `handleClick` will always run in a browser environment. [Middlewares](https://nuxt.com/docs/3.x/directory-structure/middleware) and [pages](https://nuxt.com/docs/3.x/directory-structure/pages) run in the server and on the client during hydration. [Plugins](https://nuxt.com/docs/3.x/directory-structure/plugins) can be rendered on the server or client or both. [Components](https://nuxt.com/docs/3.x/directory-structure/components) can be forced to run on the client only as well. [Composables](https://nuxt.com/docs/3.x/directory-structure/composables) and [utilities](https://nuxt.com/docs/3.x/directory-structure/utils) are rendered based on the context of their usage. **Benefits of server-side rendering:** - **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application during the hydration process. - **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly. **Downsides of server-side rendering:** - **Development constraints:** Server and browser environments don't provide the same APIs, and it can be tricky to write code that can run on both sides seamlessly. Fortunately, Nuxt provides guidelines and specific variables to help you determine where a piece of code is executed. - **Cost:** A server needs to be running in order to render pages on the fly. This adds a monthly cost like any traditional server. However, the server calls are highly reduced thanks to universal rendering with the browser taking over on client-side navigation. A cost reduction is possible by leveraging [edge-side-rendering](https://nuxt.com/docs/3.x/guide/concepts/rendering#edge-side-rendering). Universal rendering is very versatile and can fit almost any use case, and is especially appropriate for any content-oriented websites: **blogs, marketing websites, portfolios, e-commerce sites, and marketplaces.** ::tip For more examples about writing Vue code without hydration mismatch, see [the Vue docs](https://vuejs.org/guide/scaling-up/ssr#hydration-mismatch). :: ::important When importing a library that relies on browser APIs and has side effects, make sure the component importing it is only called client-side. Bundlers do not treeshake imports of modules containing side effects. :: ## Client-Side Rendering Out of the box, a traditional Vue.js application is rendered in the browser (or **client**). Then, Vue.js generates HTML elements after the browser downloads and parses all the JavaScript code containing the instructions to create the current interface. ![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](https://nuxt.com/assets/docs/concepts/rendering/csr.svg) **Benefits of client-side rendering:** - **Development speed**: When working entirely on the client-side, we don't have to worry about the server compatibility of the code, for example, by using browser-only APIs like the `window` object. - **Cheaper:** Running a server adds a cost of infrastructure as you would need to run on a platform that supports JavaScript. We can host client-only applications on any static server with HTML, CSS, and JavaScript files. - **Offline:** Because code entirely runs in the browser, it can nicely keep working while the internet is unavailable. **Downsides of client-side rendering:** - **Performance**: The user has to wait for the browser to download, parse and run JavaScript files. Depending on the network for the download part and the user's device for the parsing and execution, this can take some time and impact the user's experience. - **Search Engine Optimization**: Indexing and updating the content delivered via client-side rendering takes more time than with a server-rendered HTML document. This is related to the performance drawback we discussed, as search engine crawlers won't wait for the interface to be fully rendered on their first try to index the page. Your content will take more time to show and update in search results pages with pure client-side rendering. Client-side rendering is a good choice for heavily interactive **web applications** that don't need indexing or whose users visit frequently. It can leverage browser caching to skip the download phase on subsequent visits, such as **SaaS, back-office applications, or online games**. You can enable client-side only rendering with Nuxt in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ ssr: false, }) ``` ::note If you do use `ssr: false`, you should also place an HTML file in `~/app/spa-loading-template.html` with some HTML you would like to use to render a loading screen that will be rendered until your app is hydrated. :read-more{title="SPA Loading Template" to="https://nuxt.com/docs/3.x/api/configuration/nuxt-config#spaloadingtemplate"} :: ::video-accordion{title="Watch a video from Alexander Lichter about Building a plain SPA with Nuxt" videoId="7Lr0QTP1Ro8"} :: ### Deploying a Static Client-Rendered App If you deploy your app to [static hosting](https://nuxt.com/docs/3.x/getting-started/deployment#static-hosting) with the `nuxt generate` or `nuxt build --prerender` commands, then by default, Nuxt will render every page as a separate static HTML file. ::warning If you prerender your app with the `nuxt generate` or `nuxt build --prerender` commands, then you will not be able to use any server endpoints as no server will be included in your output folder. If you need server functionality, use `nuxt build` instead. :: If you are using purely client-side rendering, then this might be unnecessary. You might only need a single `index.html` file, plus `200.html` and `404.html` fallbacks, which you can tell your static web host to serve up for all requests. In order to achieve this we can change how the routes are prerendered. Just add this to [your hooks](https://nuxt.com/docs/3.x/api/advanced/hooks#nuxt-hooks-build-time) in your `nuxt.config.ts`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ hooks: { 'prerender:routes' ({ routes }) { routes.clear() // Do not generate any routes (except the defaults) }, }, }) ``` This will produce three files: - `index.html` - `200.html` - `404.html` The `200.html` and `404.html` might be useful for the hosting provider you are using. #### Skipping Client Fallback Generation When prerendering a client-rendered app, Nuxt will generate `index.html`, `200.html` and `404.html` files by default. However, if you need to prevent any (or all) of these files from being generated in your build, you can use the `'prerender:generate'` hook from [Nitro](https://nuxt.com/docs/3.x/getting-started/prerendering#prerendergenerate-nitro-hook). ```ts twoslash [nuxt.config.ts] // @errors: 2353 7006 export default defineNuxtConfig({ ssr: false, nitro: { hooks: { 'prerender:generate' (route) { const routesToSkip = ['/index.html', '/200.html', '/404.html'] if (routesToSkip.includes(route.route)) { route.skip = true } }, }, }, }) ``` ## Hybrid Rendering Hybrid rendering allows different caching rules per route using **Route Rules** and decides how the server should respond to a new request on a given URL. Previously every route/page of a Nuxt application and server must use the same rendering mode, universal or client-side. In various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application. Nuxt includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route! Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [Nitro caching layer](https://nitro.build/guide/cache). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Homepage pre-rendered at build time '/': { prerender: true }, // Products page generated on demand, revalidates in background, cached until API response changes '/products': { swr: true }, // Product pages generated on demand, revalidates in background, cached for 1 hour (3600 seconds) '/products/**': { swr: 3600 }, // Blog posts page generated on demand, revalidates in background, cached on CDN for 1 hour (3600 seconds) '/blog': { isr: 3600 }, // Blog post page generated on demand once until next deployment, cached on CDN '/blog/**': { isr: true }, // Admin dashboard renders only on client-side '/admin/**': { ssr: false }, // Add cors headers on API routes '/api/**': { cors: true }, // Redirects legacy urls '/old-page': { redirect: '/new-page' }, }, }) ``` ### Route Rules The different properties you can use are the following: - `redirect: string`{lang="ts"} - Define server-side redirects. - `ssr: boolean`{lang="ts"} - Disables server-side rendering of the HTML for sections of your app and make them render only in the browser with `ssr: false` - `cors: boolean`{lang="ts"} - Automatically adds cors headers with `cors: true` - you can customize the output by overriding with `headers` - `headers: object`{lang="ts"} - Add specific headers to sections of your site - for example, your assets - `swr: number | boolean`{lang="ts"} - Add cache headers to the server response and cache it on the server or reverse proxy for a configurable TTL (time to live). The `node-server` preset of Nitro is able to cache the full response. When the TTL expired, the cached response will be sent while the page will be regenerated in the background. If true is used, a `stale-while-revalidate` header is added without a MaxAge. - `isr: number | boolean`{lang="ts"} - The behavior is the same as `swr` except that we are able to add the response to the CDN cache on platforms that support this (currently Netlify or Vercel). If `true` is used, the content persists until the next deploy inside the CDN. - `prerender: boolean`{lang="ts"} - Prerenders routes at build time and includes them in your build as static assets - `noScripts: boolean`{lang="ts"} - Disables rendering of Nuxt scripts and JS resource hints for sections of your site. - `appMiddleware: string | string[] | Record`{lang="ts"} - Allows you to define middleware that should or should not run for page paths within the Vue app part of your application (that is, not your Nitro routes) ::note Routes using `isr` or `swr` also generate `_payload.json` files alongside HTML. Client-side navigation loads these cached payloads instead of re-fetching data. Configure dynamic routes like `pages/[...slug].vue` with glob patterns: `'/**': { isr: true }`. :: Whenever possible, route rules will be automatically applied to the deployment platform's native rules for optimal performances (Netlify and Vercel are currently supported). ::important Note that Hybrid Rendering is not available when using [`nuxt generate`](https://nuxt.com/docs/3.x/api/commands/generate). :: **Examples:** ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: Nuxt Vercel ISR to: https://github.com/danielroe/nuxt-vercel-isr target: _blank ui.icon.base: text-black dark:text-white ``` Example of a Nuxt application with hybrid rendering deployed on Vercel. ::: :: ## Edge-Side Rendering Edge-Side Rendering (ESR) is a powerful feature introduced in Nuxt that allows the rendering of your Nuxt application closer to your users via edge servers of a Content Delivery Network (CDN). By leveraging ESR, you can ensure improved performance and reduced latency, thereby providing an enhanced user experience. With ESR, the rendering process is pushed to the 'edge' of the network - the CDN's edge servers. Note that ESR is more a deployment target than an actual rendering mode. When a request for a page is made, instead of going all the way to the original server, it's intercepted by the nearest edge server. This server generates the HTML for the page and sends it back to the user. This process minimizes the physical distance the data has to travel, **reducing latency and loading the page faster**. Edge-side rendering is possible thanks to [Nitro](https://nitro.build/), the [server engine](https://nuxt.com/docs/3.x/guide/concepts/server-engine) that powers Nuxt. It offers cross-platform support for Node.js, Deno, Cloudflare Workers, and more. The current platforms where you can leverage ESR are: - [Cloudflare Pages](https://pages.cloudflare.com) with zero configuration using the git integration and the `nuxt build` command - [Vercel Cloud](https://vercel.com/home) using the `nuxt build` command and `NITRO_PRESET=vercel-edge` environment variable - [Netlify Edge Functions](https://www.netlify.com/platform/#netlify-edge-functions) using the `nuxt build` command and `NITRO_PRESET=netlify-edge` environment variable Note that **Hybrid Rendering** can be used when using Edge-Side Rendering with route rules. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Server Engine" description: "Nuxt is powered by a new server engine: Nitro." canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/server-engine" --- # Server Engine > Nuxt is powered by a new server engine: Nitro. While building Nuxt, we created a new server engine: [Nitro](https://nitro.build/). It is shipped with many features: - Cross-platform support for Node.js, browsers, service workers and more. - Serverless support out-of-the-box. - API routes support. - Automatic code-splitting and async-loaded chunks. - Hybrid mode for static + serverless sites. - Development server with hot module reloading. ## API Layer Server [API endpoints](https://nuxt.com/docs/3.x/directory-structure/server#server-routes) and [Middleware](https://nuxt.com/docs/3.x/directory-structure/server#server-middleware) are added by Nitro that internally uses [h3](https://github.com/h3js/h3). Key features include: - Handlers can directly return objects/arrays for an automatically-handled JSON response - Handlers can return promises, which will be awaited (`res.end()` and `next()` are also supported) - Helper functions for body parsing, cookie handling, redirects, headers and more Check out [the h3 docs](https://github.com/h3js/h3) for more information. ::read-more{to="https://nuxt.com/docs/3.x/directory-structure/server#server-routes"} Learn more about the API layer in the `server/` directory. :: ## Direct API Calls Nitro allows 'direct' calling of routes via the globally-available [`$fetch`](https://nuxt.com/docs/3.x/api/utils/dollarfetch) helper. This will make an API call to the server if run on the browser, but will directly call the relevant function if run on the server, **saving an additional API call**. [`$fetch`](https://nuxt.com/docs/3.x/api/utils/dollarfetch) API is using [ofetch](https://github.com/unjs/ofetch), with key features including: - Automatic parsing of JSON responses (with access to raw response if needed) - Request body and params are automatically handled, with correct `Content-Type` headers For more information on `$fetch` features, check out [ofetch](https://github.com/unjs/ofetch). ## Typed API Routes When using API routes (or middleware), Nitro will generate typings for these routes as long as you are returning a value instead of using `res.end()` to send a response. You can access these types when using [`$fetch()`](https://nuxt.com/docs/3.x/api/utils/dollarfetch) or [`useFetch()`](https://nuxt.com/docs/3.x/api/composables/use-fetch). ## Standalone Server Nitro produces a standalone server dist that is independent of `node_modules`. The server in Nuxt 2 is not standalone and requires part of Nuxt core to be involved by running `nuxt start` (with the [`nuxt-start`](https://www.npmjs.com/package/nuxt-start) or [`nuxt`](https://www.npmjs.com/package/nuxt) distributions) or custom programmatic usage, which is fragile and prone to breakage and not suitable for serverless and service worker environments. Nuxt generates this dist when running `nuxt build` into a [`.output`](https://nuxt.com/docs/3.x/directory-structure/output) directory. The output contains runtime code to run your Nuxt server in any environment (including experimental browser service workers!) and serve your static files, making it a true hybrid framework for the JAMstack. In addition, Nuxt implements a native storage layer, supporting multi-source drivers and local assets. ::read-more{icon="i-simple-icons-github" to="https://github.com/nitrojs/nitro" target="_blank"} Read more about Nitro engine on GitHub. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "TypeScript" description: "Nuxt is fully typed and provides helpful shortcuts to ensure you have access to accurate type information when you are coding." canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/typescript" --- # TypeScript > Nuxt is fully typed and provides helpful shortcuts to ensure you have access to accurate type information when you are coding. ## Type-checking By default, Nuxt doesn't check types when you run [`nuxt dev`](https://nuxt.com/docs/3.x/api/commands/dev) or [`nuxt build`](https://nuxt.com/docs/3.x/api/commands/build), for performance reasons. To enable type-checking at build or development time, install `vue-tsc` and `typescript` as development dependency: ::code-group{sync="pm"} ```bash [npm] npm install --save-dev vue-tsc typescript ``` ```bash [yarn] yarn add --dev vue-tsc typescript ``` ```bash [pnpm] pnpm add -D vue-tsc typescript ``` ```bash [bun] bun add -D vue-tsc typescript ``` ```bash [deno] deno add -D npm:vue-tsc npm:typescript ``` :: Then, run [`nuxt typecheck`](https://nuxt.com/docs/3.x/api/commands/typecheck) command to check your types: ```bash [Terminal] npx nuxt typecheck ``` To enable type-checking at build or development time, you can also use the [`typescript.typeCheck`](https://nuxt.com/docs/3.x/api/nuxt-config#typecheck) option in your `nuxt.config` file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ typescript: { typeCheck: true, }, }) ``` ## Auto-generated Types When you run `nuxt dev` or `nuxt build`, Nuxt generates the following files for IDE type support (and type checking): ### `.nuxt/nuxt.d.ts` This file contains the types of any modules you are using, as well as the key types that Nuxt requires. Your IDE should recognize these types automatically. Some of the references in the file are to files that are only generated within your `buildDir` (`.nuxt`) and therefore for full typings, you will need to run `nuxt dev` or `nuxt build`. ### `.nuxt/tsconfig.json` This file contains the recommended basic TypeScript configuration for your project, including resolved aliases injected by Nuxt or modules you are using, so you can get full type support and path auto-complete for aliases like `~/file` or `#build/file`. ::note Consider using the `imports` section of [nuxt.config](https://nuxt.com/docs/3.x/api/nuxt-config#imports) to include directories beyond the default ones. This can be useful for auto-importing types which you're using across your app. :: ::warning Nuxt relies on this configuration, and [Nuxt modules](https://nuxt.com/docs/3.x/guide/modules) can extend it as well. For this reason, it is not recommended to modify your `tsconfig.json` file directly, as doing so could overwrite important settings. Instead, extend it via `nuxt.config.ts`. [Learn more about extending the configuration here](https://nuxt.com/docs/3.x/directory-structure/tsconfig). :: [Read more about how to extend this configuration](https://nuxt.com/docs/3.x/directory-structure/tsconfig). ::tip{icon="i-lucide-video" to="https://youtu.be/umLI7SlPygY" target="_blank"} Watch a video from Daniel Roe explaining built-in Nuxt aliases. :: ::note Nitro also [auto-generates types](https://nuxt.com/docs/3.x/guide/concepts/server-engine#typed-api-routes) for API routes. Plus, Nuxt also generates types for globally available components and [auto-imports from your composables](https://nuxt.com/docs/3.x/directory-structure/composables), plus other core functionality. :: ::note Keep in mind that all options extended from `./.nuxt/tsconfig.json` will be overwritten by the options defined in your `tsconfig.json`. Overwriting options such as `"compilerOptions.paths"` with your own configuration will lead TypeScript to not factor in the module resolutions from `./.nuxt/tsconfig.json`. This can lead to module resolutions such as `#imports` not being recognized. In case you need to extend options provided by `./.nuxt/tsconfig.json` further, you can use the [`alias` property](https://nuxt.com/docs/3.x/api/nuxt-config#alias) within your `nuxt.config`. Nuxt will pick them up and extend `./.nuxt/tsconfig.json` accordingly. :: ## Strict Checks TypeScript comes with certain checks to give you more safety and analysis of your program. [Strict checks](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) are enabled by default in Nuxt when the [`typescript.typeCheck`](https://nuxt.com/docs/3.x/guide/concepts/typescript#type-checking) option is enabled to give you greater type safety. If you are currently converting your codebase to TypeScript, you may want to temporarily disable strict checks by setting `strict` to `false` in your `nuxt.config`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ typescript: { strict: false, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Vue.js Development" description: "Nuxt uses Vue.js and adds features such as component auto-imports, file-based routing and composables for an SSR-friendly usage." canonical_url: "https://nuxt.com/docs/3.x/guide/concepts/vuejs-development" --- # Vue.js Development > Nuxt uses Vue.js and adds features such as component auto-imports, file-based routing and composables for an SSR-friendly usage. Nuxt integrates Vue 3, the new major release of Vue that enables new patterns for Nuxt users. ::note While an in-depth knowledge of Vue is not required to use Nuxt, we recommend that you read the documentation and go through some of the examples on [vuejs.org](https://vuejs.org). :: Nuxt has always used Vue as a frontend framework. We chose to build Nuxt on top of Vue for these reasons: - The reactivity model of Vue, where a change in data automatically triggers a change in the interface. - The component-based templating, while keeping HTML as the common language of the web, enables intuitive patterns to keep your interface consistent, yet powerful. - From small projects to large web applications, Vue keeps performing well at scale to ensure that your application keeps delivering value to your users. ## Vue with Nuxt ### Single File Components [Vue’s single-file components](https://vuejs.org/guide/scaling-up/sfc) (SFC or `*.vue` files) encapsulate the markup (` ``` It's suggested to wrap your components in either a `` or `` component as tags will be deduped more intuitively. ::warning If you need to duplicate tags across client-server boundaries, apply a `key` attribute on the `` component. :: ## Types Below are the non-reactive types used for [`useHead`](https://nuxt.com/docs/5.x/api/composables/use-head), [`app.head`](https://nuxt.com/docs/5.x/api/nuxt-config#head) and components. ```ts [Types] interface MetaObject { title?: string titleTemplate?: string | ((title?: string) => string) templateParams?: Record> base?: Base link?: Link[] meta?: Meta[] style?: Style[] script?: Script[] noscript?: Noscript[] htmlAttrs?: HtmlAttributes bodyAttrs?: BodyAttributes } ``` See [@unhead/vue](https://github.com/unjs/unhead/blob/main/packages/vue/src/types/schema.ts) for more detailed types. ## Features ### Reactivity Reactivity is supported on all properties, by providing a computed value, a getter, or a reactive object. ::code-group ```vue twoslash [useHead] ``` ```vue twoslash [useSeoMeta] ``` ```vue [app/Components] ``` :: ### Title Template You can use the `titleTemplate` option to provide a dynamic template for customizing the title of your site. For example, you could add the name of your site to the title of every page. The `titleTemplate` can either be a string, where `%s` is replaced with the title, or a function. If you want to use a function (for full control), then this cannot be set in your `nuxt.config`. It is recommended instead to set it within your `app.vue` file where it will apply to all pages on your site: ::code-group ```vue twoslash [useHead] ``` :: Now, if you set the title to `My Page` with [`useHead`](https://nuxt.com/docs/5.x/api/composables/use-head) on another page of your site, the title would appear as 'My Page - Site Title' in the browser tab. You could also pass `null` to default to 'Site Title'. ### Template Parameters You can use `templateParams` to provide additional placeholders in your `titleTemplate` besides the default `%s`. This allows for more dynamic title generation. ::code-group ```vue twoslash [useHead] ``` :: ### Body Tags You can use the `tagPosition: 'bodyClose'` option on applicable tags to append them to the end of the `` tag. For example: ```vue twoslash ``` ## Example ### With `definePageMeta` Within your [`app/pages/` directory](https://nuxt.com/docs/5.x/directory-structure/app/pages), you can use `definePageMeta` along with [`useHead`](https://nuxt.com/docs/5.x/api/composables/use-head) to set metadata based on the current route. For example, you can first set the current page title (this is extracted at build time via a macro, so it can't be set dynamically): ```vue twoslash [pages/some-page.vue] ``` And then in your layout file, you might use the route's metadata you have previously set: ```vue twoslash [layouts/default.vue] ``` ::link-example{to="https://nuxt.com/docs/examples/features/meta-tags"} :: ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/pages/#page-metadata"} :: ### Dynamic Title In the example below, `titleTemplate` is set either as a string with the `%s` placeholder or as a `function`, which allows greater flexibility in setting the page title dynamically for each route of your Nuxt app: ```vue twoslash [app/app.vue] ``` ```vue twoslash [app/app.vue] ``` `nuxt.config` is also used as an alternative way of setting the page title. However, `nuxt.config` does not allow the page title to be dynamic. Therefore, it is recommended to use `titleTemplate` in the `app.vue` file to add a dynamic title, which is then applied to all routes of your Nuxt app. ### External CSS The example below shows how you might enable Google Fonts using either the `link` property of the [`useHead`](https://nuxt.com/docs/5.x/api/composables/use-head) composable or using the `` component: ::code-group ```vue twoslash [useHead] ``` ```vue [app/Components] ``` :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Server" description: "Build full-stack applications with Nuxt's server framework. You can fetch data from your database or another server, create APIs, or even generate static server-side content like a sitemap or a RSS feed - all from a single codebase." canonical_url: "https://nuxt.com/docs/5.x/getting-started/server" --- # Server > Build full-stack applications with Nuxt's server framework. You can fetch data from your database or another server, create APIs, or even generate static server-side content like a sitemap or a RSS feed - all from a single codebase. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/server"} :: ## Powered by Nitro ![Server engine](https://nuxt.com/assets/docs/getting-started/server.svg) Nuxt's server is [Nitro](https://github.com/nitrojs/nitro). It was originally created for Nuxt but is now part of [UnJS](https://unjs.io) and open for other frameworks - and can even be used on its own. Using Nitro gives Nuxt superpowers: - Full control of the server-side part of your app - Universal deployment on any provider (many zero-config) - Hybrid rendering Nitro is internally using [h3](https://github.com/h3js/h3), a minimal HTTP framework built for high performance and portability. ::video-accordion{title="Watch a video from Alexander Lichter to understand the responsibilities of Nuxt and Nitro in your application" videoId="DkvgJa-X31k"} :: ## Server Endpoints & Middleware You can easily manage the server-only part of your Nuxt app, from API endpoints to middleware. Both endpoints and middleware can be defined like this: ```ts twoslash [server/api/test.ts] import { defineEventHandler } from 'nuxt/server' export default defineEventHandler(async (event) => { // ... Do whatever you want here }) ``` And you can directly return `text`, `json`, `html` or even a `stream`. Out-of-the-box, it supports **hot module replacement** and **auto-import** like the other parts of your Nuxt application. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/server"} :: ## Universal Deployment Nitro offers the ability to deploy your Nuxt app anywhere, from a bare metal server to the edge network, with a start time of just a few milliseconds. That's fast! ::read-more{to="https://nuxt.com/blog/nuxt-on-the-edge"} :: There are more than 15 presets to build your Nuxt app for different cloud providers and servers, including: - [Cloudflare Workers](https://workers.cloudflare.com) - [Netlify Functions](https://www.netlify.com/platform/core/functions/) - [Vercel Cloud](https://vercel.com/home) Or for other runtimes: ::card-group :::card ```yaml [props] icon: i-logos-deno title: Deno to: https://deno.com target: _blank ``` ::: :::card ```yaml [props] icon: i-logos-bun title: Bun to: https://bun.com target: _blank ``` ::: :: ::read-more{to="https://nuxt.com/docs/5.x/getting-started/deployment"} :: ## Hybrid Rendering Nuxt has a powerful feature called `routeRules` which allows you to define a set of rules to customize how each route of your Nuxt app is rendered (and more). Nuxt compiles the rules, and the default server builder (Nitro) applies the server-side ones. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Generated at build time for SEO purpose '/': { prerender: true }, // Cached for 1 hour '/api/*': { cache: { maxAge: 60 * 60 } }, // Redirection to avoid 404 '/old-page': { redirect: { to: '/new-page', status: 302 }, }, // ... }, }) ``` ::read-more{to="https://nuxt.com/docs/5.x/guide/concepts/rendering#hybrid-rendering"} Learn about all the route rules available to customize the rendering mode of your routes. :: In addition, there are some route rules (for example, `ssr`, `appMiddleware`, and `noScripts`) that are Nuxt specific to change the behavior when rendering your pages to HTML. Some route rules (`appMiddleware`, `redirect` and `prerender`) also affect client-side behavior. Nitro is used to build the app for server side rendering, as well as pre-rendering. ::read-more{to="https://nuxt.com/docs/5.x/guide/concepts/rendering"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "State Management" description: "Nuxt provides powerful state management libraries and the useState composable to create a reactive and SSR-friendly shared state." canonical_url: "https://nuxt.com/docs/5.x/getting-started/state-management" --- # State Management > Nuxt provides powerful state management libraries and the useState composable to create a reactive and SSR-friendly shared state. Nuxt provides the [`useState`](https://nuxt.com/docs/5.x/api/composables/use-state) composable to create a reactive and SSR-friendly shared state across components. [`useState`](https://nuxt.com/docs/5.x/api/composables/use-state) is an SSR-friendly [`ref`](https://vuejs.org/api/reactivity-core#ref) replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key. ::video-accordion{title="Watch a video from Alexander Lichter about why and when to use useState" videoId="mv0WcBABcIk"} :: ::important Because the data inside [`useState`](https://nuxt.com/docs/5.x/api/composables/use-state) will be serialized to JSON, it is important that it does not contain anything that cannot be serialized, such as classes, functions or symbols. :: ::read-more{to="https://nuxt.com/docs/5.x/api/composables/use-state"} Read more about `useState` composable. :: ## Best Practices ::warning Never define `const state = ref()` outside of ` ``` ::link-example{to="https://nuxt.com/docs/examples/features/state-management"} :: ::note To globally invalidate cached state, see [`clearNuxtState`](https://nuxt.com/docs/5.x/api/utils/clear-nuxt-state) util. :: ### Initializing State Most of the time, you will want to initialize your state with data that resolves asynchronously. You can use the [`app.vue`](https://nuxt.com/docs/5.x/directory-structure/app/app) component with the [`callOnce`](https://nuxt.com/docs/5.x/api/utils/call-once) util to do so. ```vue twoslash [app/app.vue] ``` ::tip This is similar to the [`nuxtServerInit` action](https://nuxt.com/docs/2.x/directory-structure/store#the-nuxtserverinit-action) in Nuxt 2, which allows filling the initial state of your store server-side before rendering the page. :: ::read-more{to="https://nuxt.com/docs/5.x/api/utils/call-once"} :: ### Usage with Pinia In this example, we leverage the [Pinia module](https://nuxt.com/modules/pinia) to create a global store and use it across the app. ::important Make sure to install the Pinia module with `npx nuxt module add pinia` or follow the [module's installation steps](https://pinia.vuejs.org/ssr/nuxt.html#Installation). :: ::code-group ```ts [app/stores/website.ts] export const useWebsiteStore = defineStore('websiteStore', { state: () => ({ name: '', description: '', }), actions: { async fetch () { const infos = await $fetch('https://api.nuxt.com/modules/pinia') this.name = infos.name this.description = infos.description }, }, }) ``` ```vue [app/app.vue] ``` :: ## Advanced Usage ::code-group ```ts [app/composables/locale.ts] import type { Ref } from 'vue' export const useLocale = () => { return useState('locale', () => useDefaultLocale().value) } export const useDefaultLocale = (fallback = 'en-US') => { const locale = ref(fallback) if (import.meta.server) { const reqLocale = useRequestHeaders()['accept-language']?.split(',')[0] if (reqLocale) { locale.value = reqLocale } } else if (import.meta.client) { const navLang = navigator.language if (navLang) { locale.value = navLang } } return locale } export const useLocales = () => { const locale = useLocale() const locales = ref([ 'en-US', 'en-GB', // ..., 'ja-JP-u-ca-japanese', ]) if (!locales.value.includes(locale.value)) { locales.value.unshift(locale.value) } return locales } export const useLocaleDate = (date: Ref | Date, locale = useLocale()) => { return computed(() => new Intl.DateTimeFormat(locale.value, { dateStyle: 'full' }).format(unref(date))) } ``` ```vue [app/app.vue] ``` :: ::link-example{to="https://nuxt.com/docs/examples/advanced/locale"} :: ## Shared State By using [auto-imported composables](https://nuxt.com/docs/5.x/directory-structure/app/composables) we can define global type-safe states and import them across the app. ```ts twoslash [composables/states.ts] export const useColor = () => useState('color', () => 'pink') ``` ```vue [app/app.vue] ``` ::video-accordion{title="Watch a video from Daniel Roe on how to deal with global state and SSR in Nuxt" videoId="dZSNW07sO-A"} :: ## Using Third-Party Libraries Nuxt **used to rely** on the Vuex library to provide global state management. If you are migrating from Nuxt 2, please head to [the migration guide](https://nuxt.com/docs/5.x/migration/configuration#vuex). Nuxt is not opinionated about state management, so feel free to choose the right solution for your needs. There are multiple integrations with the most popular state management libraries, including: - [Pinia](https://nuxt.com/modules/pinia) - the official Vue recommendation - [Harlem](https://nuxt.com/modules/harlem) - immutable global state management - [XState](https://nuxt.com/modules/xstate) - state machine approach with tools for visualizing and testing your state logic ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Styling" description: "Learn how to style your Nuxt application." canonical_url: "https://nuxt.com/docs/5.x/getting-started/styling" --- # Styling > Learn how to style your Nuxt application. Nuxt is highly flexible when it comes to styling. Write your own styles, or reference local and external stylesheets. You can use CSS preprocessors, CSS frameworks, UI libraries and Nuxt modules to style your application. ## Local Stylesheets If you're writing local stylesheets, the natural place to put them is the [`app/assets/` directory](https://nuxt.com/docs/5.x/directory-structure/app/assets). ### Importing Within Components You can import stylesheets in your pages, layouts and components directly. You can use a JavaScript import, or a CSS [`@import` statement](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/At-rules/@import). ```vue [app/pages/index.vue] ``` ::tip The stylesheets will be inlined in the HTML rendered by Nuxt. :: ### The CSS Property You can also use the `css` property in the Nuxt configuration. The natural place for your stylesheets is the [`app/assets/` directory](https://nuxt.com/docs/5.x/directory-structure/app/assets). You can then reference its path and Nuxt will include it to all the pages of your application. ```ts [nuxt.config.ts] export default defineNuxtConfig({ css: ['~/assets/css/main.css'], }) ``` ::tip The stylesheets will be inlined in the HTML rendered by Nuxt, injected globally and present in all pages. :: ### Working With Fonts Place your local fonts files in your `public/` directory, for example in `public/fonts`. You can then reference them in your stylesheets using `url()`. ```css [assets/css/main.css] @font-face { font-family: 'FarAwayGalaxy'; src: url('/fonts/FarAwayGalaxy.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap; } ``` Then reference your fonts by name in your stylesheets, pages or components: ```vue ``` ### Stylesheets Distributed Through NPM You can also reference stylesheets that are distributed through npm. Let's use the popular `animate.css` library as an example. ::code-group{sync="pm"} ```bash [npm] npm install animate.css ``` ```bash [yarn] yarn add animate.css ``` ```bash [pnpm] pnpm install animate.css ``` ```bash [bun] bun install animate.css ``` ```bash [deno] deno install npm:animate.css ``` :: Then you can reference it directly in your pages, layouts and components: ```vue [app/app.vue] ``` The package can also be referenced as a string in the css property of your Nuxt configuration. ```ts [nuxt.config.ts] export default defineNuxtConfig({ css: ['animate.css'], }) ``` ## External Stylesheets You can include external stylesheets in your application by adding a link element in the head section of your nuxt.config file. You can achieve this result using different methods. Note that local stylesheets can also be included this way. You can manipulate the head with the [`app.head`](https://nuxt.com/docs/5.x/api/nuxt-config#head) property of your Nuxt configuration: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }], }, }, }) ``` ### Dynamically Adding Stylesheets You can use the useHead composable to dynamically set a value in your head in your code. ::read-more{to="https://nuxt.com/docs/5.x/api/composables/use-head"} :: ```ts twoslash useHead({ link: [{ rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css' }], }) ``` Nuxt uses `unhead` under the hood, and you can refer to [its full documentation](https://unhead.unjs.io). ### Modifying The Rendered Head With A Nitro Plugin If you need more advanced control, you can intercept the rendered html with a hook and modify the head programmatically. Create a plugin in `~~/server/plugins/my-plugin.ts` like this: ```ts [server/plugins/my-plugin.ts] import { definePlugin } from 'nitro' export default definePlugin((nitro) => { nitro.hooks.hook('render:html', (html) => { html.head.push('') }) }) ``` External stylesheets are render-blocking resources: they must be loaded and processed before the browser renders the page. Web pages that contain unnecessarily large styles take longer to render. You can read more about it on [web.dev](https://web.dev/articles/defer-non-critical-css). ## Using Preprocessors To use a preprocessor like SCSS, Sass, Less or Stylus, install it first. ::code-group ```bash [Sass & SCSS] npm install -D sass ``` ```bash [Less] npm install -D less ``` ```bash [Stylus] npm install -D stylus ``` :: The natural place to write your stylesheets is the `app/assets` directory. You can then import your source files in your `app.vue` (or layouts files) using your preprocessor's syntax. ```vue [app/pages/app.vue] ``` Alternatively, you can use the `css` property of your Nuxt configuration. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ css: ['~/assets/scss/main.scss'], }) ``` ::tip In both cases, the compiled stylesheets will be inlined in the HTML rendered by Nuxt. :: If you need to inject code in pre-processed files, like a [Sass partial](https://sass-lang.com/documentation/at-rules/use/#partials) with color variables, you can do so with the Vite [preprocessors options](https://vite.dev/config/shared-options#css-preprocessoroptions). Create some partials in your `app/assets` directory: ::code-group{sync="preprocessor"} ```scss [assets/_colors.scss] $primary: #49240F; $secondary: #E4A79D; ``` ```sass [assets/_colors.sass] $primary: #49240F $secondary: #E4A79D ``` :: Then in your `nuxt.config` : ::code-group ```ts twoslash [SCSS] export default defineNuxtConfig({ vite: { css: { preprocessorOptions: { scss: { additionalData: '@use "~/assets/_colors.scss" as *;', }, }, }, }, }) ``` ```ts twoslash [SASS] export default defineNuxtConfig({ vite: { css: { preprocessorOptions: { sass: { additionalData: '@use "~/assets/_colors.sass" as *\n', }, }, }, }, }) ``` :: Nuxt uses Vite by default. If you wish to use webpack instead, refer to each preprocessor loader [documentation](https://webpack.js.org/loaders/sass-loader/). ### Preprocessor Workers (Experimental) Vite has made available an [experimental option](https://vite.dev/config/shared-options#css-preprocessormaxworkers) which can speed up using preprocessors. You can enable this in your `nuxt.config`: ```ts export default defineNuxtConfig({ vite: { css: { preprocessorMaxWorkers: true, // number of CPUs minus 1 }, }, }) ``` ::note This is an experimental option and you should refer to the Vite documentation and [provide feedback](https://github.com/vitejs/vite/discussions/15835). :: ## Single File Components (SFC) Styling One of the best things about Vue and SFC is how great it is at naturally dealing with styling. You can directly write CSS or preprocessor code in the style block of your components file, therefore you will have a fantastic developer experience without having to use something like CSS-in-JS. However if you wish to use CSS-in-JS, you can find 3rd party libraries and modules that support it, such as [pinceau](https://github.com/Tahul/pinceau). You can refer to the [Vue docs](https://vuejs.org/api/sfc-css-features) for a comprehensive reference about styling components in SFC. ### Class And Style Bindings You can leverage Vue SFC features to style your components with class and style attributes. ::code-group ```vue [Ref and Reactive] ``` ```vue [Computed] ``` ```vue [Array] ``` ```vue [Style] ``` :: Refer to the [Vue docs](https://vuejs.org/guide/essentials/class-and-style) for more information. ### Dynamic Styles With `v-bind` You can reference JavaScript variables and expressions within your style blocks with the v-bind function. The binding will be dynamic, meaning that if the variable value changes, the style will be updated. ```vue ``` ### Scoped Styles The scoped attribute allows you to style components in isolation. The styles declared with this attribute will only apply to this component. ```vue ``` ### CSS Modules You can use [CSS Modules](https://github.com/css-modules/css-modules) with the module attribute. Access it with the injected `$style` variable. ```vue ``` ### Preprocessors Support SFC style blocks support preprocessor syntax. Vite comes with built-in support for .scss, .sass, .less, .styl and .stylus files without configuration. You just need to install them first, and they will be available directly in SFC with the lang attribute. ::code-group ```vue [SCSS] ``` ```vue [Sass] ``` ```vue [LESS] ``` ```vue [Stylus] ``` :: You can refer to the [Vite CSS docs](https://vite.dev/guide/features#css) and the [@vitejs/plugin-vue docs](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue). For webpack users, refer to the [vue loader docs](https://vue-loader.vuejs.org). ## Using PostCSS Nuxt comes with postcss built-in. You can configure it in your `nuxt.config` file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ postcss: { plugins: { 'postcss-nested': {}, 'postcss-custom-media': {}, }, }, }) ``` For proper syntax highlighting in SFC, you can use the postcss lang attribute. ```vue ``` With the default Vite builder, no PostCSS plugins are configured out of the box. Vite resolves `@import` and `url()` itself, and minifies CSS as part of the build. [autoprefixer](https://github.com/postcss/autoprefixer) and [cssnano](https://cssnano.github.io/cssnano/) are optional peer dependencies. To use either, install it and name it in `postcss.plugins`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ postcss: { plugins: { autoprefixer: {}, cssnano: {}, }, }, }) ``` With the webpack builder, [postcss-import](https://github.com/postcss/postcss-import), [postcss-url](https://github.com/postcss/postcss-url), `autoprefixer` and `cssnano` are still configured for you. ## Leveraging Layouts For Multiple Styles If you need to style different parts of your application completely differently, you can use layouts. Use different styles for different layouts. ```vue ``` ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/layouts"} :: ## Third Party Libraries And Modules Nuxt isn't opinionated when it comes to styling and provides you with a wide variety of options. You can use any styling tool that you want, such as popular libraries like [UnoCSS](https://unocss.dev) or [Tailwind CSS](https://tailwindcss.com). The community and the Nuxt team have developed plenty of Nuxt modules to make the integration easier. You can discover them on the [modules section](https://nuxt.com/modules) of the website. Here are a few modules to help you get started: - [UnoCSS](https://nuxt.com/modules/unocss): Instant on-demand atomic CSS engine - [Tailwind CSS](https://nuxt.com/modules/tailwindcss): Utility-first CSS framework - [Fontaine](https://github.com/nuxt-modules/fontaine): Font metric fallback - [Pinceau](https://github.com/Tahul/pinceau): Adaptable styling framework - [Nuxt UI](https://ui.nuxt.com): A UI Library for Modern Web Apps - [Panda CSS](https://panda-css.com/docs/installation/nuxt): CSS-in-JS engine that generates atomic CSS at build time Nuxt modules provide you with a good developer experience out of the box, but remember that if your favorite tool doesn't have a module, it doesn't mean that you can't use it with Nuxt! You can configure it yourself for your own project. Depending on the tool, you might need to use a [Nuxt plugin](https://nuxt.com/docs/5.x/directory-structure/app/plugins) and/or [make your own module](https://nuxt.com/docs/5.x/guide/modules). Share them with the [community](https://nuxt.com/modules) if you do! ### Easily Load Webfonts You can use [the Nuxt Google Fonts module](https://github.com/nuxt-modules/google-fonts) to load Google Fonts. If you are using [UnoCSS](https://unocss.dev/integrations/nuxt), note that it comes with a [web fonts presets](https://unocss.dev/presets/web-fonts) to conveniently load fonts from common providers, including Google Fonts and more. ## Advanced ### Transitions Nuxt comes with the same `` element that Vue has, and also has support for the experimental [View Transitions API](https://nuxt.com/docs/5.x/getting-started/transitions#view-transitions-api-experimental). ::read-more{to="https://nuxt.com/docs/5.x/getting-started/transitions"} :: ### Font Advanced Optimization We would recommend using [Fontaine](https://github.com/nuxt-modules/fontaine) to reduce your [CLS](https://web.dev/articles/cls). If you need something more advanced, consider creating a Nuxt module to extend the build process or the Nuxt runtime. ::tip Always remember to take advantage of the various tools and techniques available in the Web ecosystem at large to make styling your application easier and more efficient. Whether you're using native CSS, a preprocessor, postcss, a UI library or a module, Nuxt has got you covered. Happy styling! :: ### LCP Advanced Optimizations You can do the following to speed-up the download of your global CSS files: - Use a CDN so the files are physically closer to your users - Compress your assets, ideally using Brotli - Use HTTP2/HTTP3 for delivery - Host your assets on the same domain (do not use a different subdomain) Most of these things should be done for you automatically if you're using modern platforms like Cloudflare, Netlify or Vercel. You can find an LCP optimization guide on [web.dev](https://web.dev/articles/optimize-lcp). If all of your CSS is inlined by Nuxt, you can (experimentally) completely stop external CSS files from being referenced in your rendered HTML. You can achieve that with a hook, that you can place in a module, or in your Nuxt configuration file. ```ts [nuxt.config.ts] export default defineNuxtConfig({ hooks: { 'build:manifest': (manifest) => { // find the app entry, css list const css = Object.values(manifest).find(options => options.isEntry)?.css if (css) { // start from the end of the array and go to the beginning for (let i = css.length - 1; i >= 0; i--) { // if it starts with 'entry', remove it from the list if (css[i].startsWith('entry')) { css.splice(i, 1) } } } }, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Testing" description: "How to test your Nuxt application." canonical_url: "https://nuxt.com/docs/5.x/getting-started/testing" --- # Testing > How to test your Nuxt application. ::tip If you are a module author, you can find more specific information in the [Module Author's guide](https://nuxt.com/docs/5.x/guide/modules/testing). :: Nuxt offers first-class support for end-to-end and unit testing of your Nuxt application via `@nuxt/test-utils`, a library of test utilities and configuration that currently powers the [tests we use on Nuxt itself](https://github.com/nuxt/nuxt/tree/main/test) and tests throughout the module ecosystem. ::video-accordion{title="Watch a video from Alexander Lichter about getting started with @nuxt/test-utils" videoId="yGzwk9xi9gU"} :: ## Installation In order to allow you to manage your other testing dependencies, `@nuxt/test-utils` ships with various optional peer dependencies. For example: - you can choose between `happy-dom` and `jsdom` for a runtime Nuxt environment - you can choose between `vitest`, `cucumber`, `jest` and `playwright` for end-to-end test runners - `playwright-core` is only required if you wish to use the built-in browser testing utilities (and are not using `@playwright/test` as your test runner) ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [yarn] yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [pnpm] pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` ```bash [bun] bun add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core ``` :: ## Test Environments `@nuxt/test-utils` provides helpers for two different test environments: - **Nuxt environment**: tests run inside a Nuxt app in a [`happy-dom`](https://github.com/capricorn86/happy-dom) or [`jsdom`](https://github.com/jsdom/jsdom) environment, with access to auto-imports, plugins and injections. Helpers from `@nuxt/test-utils/runtime` (such as `mountSuspended` and `mockNuxtImport`) require this environment, which is currently only available with Vitest. - **Node environment**: tests run in plain Node. End-to-end tests using `@nuxt/test-utils/e2e` require this environment, as they test your Nuxt app from the outside, over HTTP or through a browser. Unit tests that do not need Nuxt can also run here. A test file can only run in one of these environments, so `@nuxt/test-utils/runtime` and `@nuxt/test-utils/e2e` cannot be used in the same file. With Vitest, you can use [projects](https://vitest.dev/guide/projects.html#test-projects) to make sure that each test file runs in the right environment, as shown in the [setup](#setup) below. ## Unit Testing We currently ship an environment for unit testing code that needs a [Nuxt](https://nuxt.com) runtime environment. It currently *only has support for `vitest`* (although contribution to add other runtimes would be welcome). ### Setup 1. Add `@nuxt/test-utils/module` to your `nuxt.config` file (optional). It adds a Vitest integration to your Nuxt DevTools which supports running your unit tests in development. ```ts twoslash export default defineNuxtConfig({ modules: [ '@nuxt/test-utils/module', ], }) ``` 2. Create a `vitest.config.ts` with the following content: ```ts twoslash import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' export default defineConfig({ test: { projects: [ { test: { name: 'unit', include: ['test/unit/*.{test,spec}.ts'], environment: 'node', }, }, { test: { name: 'e2e', include: ['test/e2e/*.{test,spec}.ts'], environment: 'node', }, }, await defineVitestProject({ test: { name: 'nuxt', include: ['test/nuxt/*.{test,spec}.ts'], environment: 'nuxt', }, }), ], }, }) ``` ::note `defineVitestProject` is only for Nuxt-environment tests. End-to-end tests should be configured as a regular `test.environment: 'node'` project. :: 3. If your Nuxt-environment tests live outside `test/nuxt/`, see [TypeScript Support in Tests](#typescript-support-in-tests) to add them to the TypeScript context. ::tip When importing `@nuxt/test-utils` in your vitest config, it is necessary to have `"type": "module"` specified in your `package.json` or rename your vitest config file appropriately. > i.e., `vitest.config.m{ts,js}`. :: ::tip It is possible to set environment variables for testing by using the `.env.test` file. :: ### Nuxt Environment Options You can pass Nuxt-specific options to a `defineVitestProject` project with `test.environmentOptions.nuxt`: ```ts twoslash import { fileURLToPath } from 'node:url' import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' export default defineConfig({ test: { projects: [ await defineVitestProject({ test: { name: 'nuxt', include: ['test/nuxt/*.{test,spec}.ts'], environmentOptions: { nuxt: { rootDir: fileURLToPath(new URL('./playground', import.meta.url)), domEnvironment: 'happy-dom', // 'happy-dom' (default) or 'jsdom' overrides: { // other Nuxt config you want to pass }, }, }, }, }), ], }, }) ``` ### Organizing Your Tests With the project-based setup, each project's `include` glob determines which environment a test file runs in: - **Unit tests** in `test/unit/` run in the Node environment. - **Nuxt tests** in `test/nuxt/` run in the Nuxt environment. - **End-to-end tests** in `test/e2e/` run in the Node environment. ```bash [Directory structure] test/ ├── e2e/ │ └── ssr.test.ts ├── nuxt/ │ ├── components.test.ts │ └── composables.test.ts ├── unit/ │ └── utils.test.ts ``` If you prefer to keep tests next to your source files, you can use file suffixes such as `*.unit.spec.ts`, `*.nuxt.spec.ts` and `*.e2e.spec.ts` instead, with matching `include` globs (for example, `include: ['**/*.nuxt.{test,spec}.ts']`). See the [co-location example](https://github.com/nuxt/test-utils/tree/main/examples/app-vitest-co-location) for a full configuration. You can of course opt for any test structure, but each test file should match exactly one project. In particular, keeping Nuxt-environment tests separate from end-to-end tests is important for test stability. ::warning Use your projects' `include` globs to choose the environment for a test file, rather than a `// @vitest-environment` comment. The comment changes the environment without adding or removing the Nuxt Vite plugins configured by `defineVitestProject`. :: #### TypeScript Support in Tests By default, test files in `test/nuxt/` and `tests/nuxt/` directories are included in the [Nuxt app TypeScript context](https://nuxt.com/docs/5.x/guide/concepts/typescript#how-nuxt-uses-project-references). That means they will recognize Nuxt aliases (like `~/`, `@/`, `#imports`) and TypeScript will be aware of auto-imports that work in your Nuxt app. ::tip This matches the recommended structure where only tests that need the Nuxt runtime environment are placed in these directories. Unit tests in other directories like `test/unit/` can be added manually if needed. :: ##### Adding other test directories If you have tests in other directories that you will be running in the Nuxt Vitest environment, you can include them in the Nuxt app TypeScript context by adding them to your configuration: ```ts [nuxt.config.ts] export default defineNuxtConfig({ typescript: { tsConfig: { include: [ // this path is relative to the generated .nuxt/tsconfig.json '../test/other-nuxt-context/**/*', ], }, }, }) ``` ::important Unit tests should not depend on Nuxt runtime features like auto-imports or composables. Only add TypeScript path alias support if your tests import from your source files (e.g., `~/utils/helpers`), not for Nuxt-specific features. :: #### Running Tests With the project setup, you can run different test suites: ```bash # Run all tests npx vitest # Run only unit tests npx vitest --project unit # Run only Nuxt tests npx vitest --project nuxt # Run only end-to-end tests npx vitest --project e2e # Run tests in watch mode npx vitest --watch ``` ::warning When you run your tests within the Nuxt environment, they will be running in a [`happy-dom`](https://github.com/capricorn86/happy-dom) or [`jsdom`](https://github.com/jsdom/jsdom) environment. Before your tests run, a global Nuxt app will be initialized (including, for example, running any plugins or code you've defined in your `app.vue`). This means you should take particular care not to mutate the global state in your tests (or, if you need to, to reset it afterwards). :: ### 🎭 Built-In Mocks `@nuxt/test-utils` provides some built-in mocks for the DOM environment. These are only available in the Nuxt environment. #### `intersectionObserver` Default `true`, creates a dummy class without any functionality for the IntersectionObserver API #### `indexedDB` Default `false`, uses [`fake-indexeddb`](https://github.com/dumbmatter/fakeIndexedDB) to create a functional mock of the IndexedDB API These can be configured in the [Nuxt environment options](#nuxt-environment-options) of your `vitest.config.ts` file: ```ts twoslash import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' export default defineConfig({ test: { projects: [ await defineVitestProject({ test: { name: 'nuxt', include: ['test/nuxt/*.{test,spec}.ts'], environmentOptions: { nuxt: { mock: { intersectionObserver: true, indexedDb: true, }, }, }, }, }), ], }, }) ``` ### 🛠️ Helpers `@nuxt/test-utils` provides a number of helpers to make testing Nuxt apps easier. #### `mountSuspended` `mountSuspended` allows you to mount any Vue component within the Nuxt environment, allowing async setup and access to injections from your Nuxt plugins. ::note Under the hood, `mountSuspended` wraps `mount` from `@vue/test-utils`, so you can check out [the Vue Test Utils documentation](https://test-utils.vuejs.org/guide/) for more on the options you can pass, and how to use this utility. :: For example: ```ts twoslash [tests/components/SomeComponents.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- import { expect, it } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' it('can mount some component', async () => { const component = await mountSuspended(SomeComponent) expect(component.text()).toMatchInlineSnapshot( '"This is an auto-imported component"', ) }) ``` ```ts twoslash [tests/App.nuxt.spec.ts] import { expect, it } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' import App from '~/app.vue' it('can also mount an app', async () => { const component = await mountSuspended(App, { route: '/test' }) expect(component.html()).toMatchInlineSnapshot(` "

This is an auto-imported component
I am a global component
/
Test link " `) }) ``` If you need to spy on component setup state, you can set the `spy` option to `true` and access the return value of the setup via `setupState`. ```ts twoslash [tests/components/SomeComponents.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- import { expect, it, vi } from 'vitest' import { mountSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' it('can spy on setup state', async () => { const component = await mountSuspended(SomeComponent, { spy: true }) vi.mocked(component.setupState.someMethod).mockImplementation(() => 'mocked') }) ``` The options object accepts `@vue/test-utils` mount options and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). - `spy`: enable spy component setup state (default `false`). The return object includes the `@vue/test-utils` mount result and the following properties: - `setupState`: the return value of the component setup. #### `renderSuspended` `renderSuspended` allows you to render any Vue component within the Nuxt environment using `@testing-library/vue`, allowing async setup and access to injections from your Nuxt plugins. This should be used together with utilities from Testing Library, e.g. `screen` and `fireEvent`. Install [@testing-library/vue](https://testing-library.com/docs/vue-testing-library/intro/) in your project to use these. Additionally, Testing Library also relies on testing globals for cleanup. You should turn these on in your [Vitest config](https://vitest.dev/config/globals). The passed in component will be rendered inside a `
`. Examples: ```ts twoslash [tests/components/SomeComponents.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const SomeComponent: Component } // ---cut--- import { expect, it } from 'vitest' import { renderSuspended } from '@nuxt/test-utils/runtime' import { SomeComponent } from '#components' import { screen } from '@testing-library/vue' it('can render some component', async () => { await renderSuspended(SomeComponent) expect(screen.getByText('This is an auto-imported component')).toBeDefined() }) ``` ~~~ts twoslash [tests/App.nuxt.spec.ts] import { expect, it } from 'vitest' import { renderSuspended } from '@nuxt/test-utils/runtime' import App from '~/app.vue' it('can also render an app', async () => { const html = await renderSuspended(App, { route: '/test' }) expect(html).toMatchInlineSnapshot(` "
This is an auto-imported component
I am a global component
Index page
Test link
" `) }) ~~~ The options object accepts `@testing-library/vue` render options and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). - `spy`: enable spy component setup state (default `false`). See the [`mountSuspended`](#mountsuspended) example above. The return object includes the `@testing-library/vue` render result and the following properties: - `setupState`: the return value of the component setup. #### `mockNuxtImport` `mockNuxtImport` allows you to mock Nuxt's auto import functionality. For example, to mock `useState`, you can do so like this: ```ts twoslash import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', () => { return () => { return { value: 'mocked storage' } } }) // your tests here ``` You can explicitly type the mock for type safety, and use the original implementation passed to the factory function when mocking complex functionality. ```ts twoslash [test/nuxt/import.test.ts] import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', (original) => { return (...args) => { return { ...original('some-key'), value: 'mocked state' } } }) // or specify the target to mock mockNuxtImport(useState, (original) => { return (...args) => { return { ...original('some-key'), value: 'mocked state' } } }) // your tests here ``` ::note `mockNuxtImport` can only be used once per mocked import per test file. It is actually a macro that gets transformed to `vi.mock` and `vi.mock` is hoisted, as described [in the Vitest docs](https://vitest.dev/api/vi#vi-mock). :: If you need to mock a Nuxt import and provide different implementations between tests, you can do it by creating and exposing your mocks using [`vi.hoisted`](https://vitest.dev/api/vi#vi-hoisted), and then use those mocks in `mockNuxtImport`. You then have access to the mocked imports, and can change the implementation between tests. Be careful to [restore mocks](https://vitest.dev/api/mock#mockrestore) before or after each test to undo mock state changes between runs. ```ts twoslash import { vi } from 'vitest' import { mockNuxtImport } from '@nuxt/test-utils/runtime' const { useStateMock } = vi.hoisted(() => { return { useStateMock: vi.fn(() => { return { value: 'mocked storage' } }), } }) mockNuxtImport('useState', () => { return useStateMock }) // Then, inside a test useStateMock.mockImplementation(() => { return { value: 'something else' } }) ``` If you need to mock behavior only inside a test, you can also use the following approach. ```ts twoslash import { beforeEach, vi } from 'vitest' import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport(useRoute, original => vi.fn(original)) beforeEach(() => { vi.resetAllMocks() }) // Then, inside a test const useRouteOriginal = vi.mocked(useRoute).getMockImplementation()! vi.mocked(useRoute).mockImplementation( (...args) => ({ ...useRouteOriginal(...args), path: '/mocked' }), ) ``` #### `mockComponent` `mockComponent` allows you to mock Nuxt's component. The first argument can be the component name in PascalCase, or the relative path of the component. The second argument is a factory function that returns the mocked component. For example, to mock `MyComponent`, you can: ```ts twoslash import { mockComponent } from '@nuxt/test-utils/runtime' mockComponent('MyComponent', { props: { value: String, }, setup (props) { // ... }, }) // relative path or alias also works mockComponent('~/components/my-component.vue', () => { // or a factory function return defineComponent({ setup (props) { // ... }, }) }) // or you can use SFC for redirecting to a mock component mockComponent('MyComponent', () => import('./MockComponent.vue')) // your tests here ``` > **Note**: You can't reference local variables in the factory function since they are hoisted. If you need to access Vue APIs or other variables, you need to import them in your factory function. ```ts twoslash import { mockComponent } from '@nuxt/test-utils/runtime' mockComponent('MyComponent', async () => { const { ref, h } = await import('vue') return defineComponent({ setup (props) { const counter = ref(0) return () => h('div', null, counter.value) }, }) }) ``` #### `registerEndpoint` `registerEndpoint` allows you create Nitro endpoint that returns mocked data. It can come in handy if you want to test a component that makes requests to API to display some data. The first argument is the endpoint name (e.g. `/test/`). The second argument is a factory function that returns the mocked data. For example, to mock `/test/` endpoint, you can do: ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' registerEndpoint('/test/', () => ({ test: 'test-field', })) ``` By default, your request will be made using the `GET` method. You may use another method by setting an object as the second argument instead of a function. ```ts twoslash import { registerEndpoint } from '@nuxt/test-utils/runtime' registerEndpoint('/test/', { method: 'POST', handler: () => ({ test: 'test-field' }), }) ``` This object accepts the following properties: - `handler`: the event handler function - `method`: (optional) HTTP method to match (e.g., 'GET', 'POST') - `once`: (optional) if true, the handler will only be used for the first matching request and then automatically removed > **Note**: If your requests in a component go to an external API, you can use `baseURL` and then make it empty using [Nuxt Environment Override Config](https://nuxt.com/docs/5.x/getting-started/configuration#environment-overrides) (`$test`) so all your requests will go to Nitro server. #### Conflict with End-To-End Testing `@nuxt/test-utils/runtime` and `@nuxt/test-utils/e2e` need to run in different [test environments](#test-environments) and so can't be used in the same file. If you would like to use both the end-to-end and unit testing functionality of `@nuxt/test-utils`, split your tests into separate files and make sure each file matches only one project: Nuxt-environment tests should be matched by a `defineVitestProject` project, and end-to-end tests by a regular `environment: 'node'` project. See [Organizing Your Tests](#organizing-your-tests) for folder- and suffix-based layouts. `app.nuxt.spec.ts` ```ts twoslash import { mockNuxtImport } from '@nuxt/test-utils/runtime' mockNuxtImport('useState', () => { return () => { return { value: 'mocked storage' } } }) ``` `app.e2e.spec.ts` ```ts twoslash import { $fetch, setup } from '@nuxt/test-utils/e2e' await setup({ setupTimeout: 10000, }) // ... ``` ### Using `@vue/test-utils` If you prefer to use `@vue/test-utils` on its own for unit testing in Nuxt, and you are only testing components which do not rely on Nuxt composables, auto-imports or context, you can follow these steps to set it up. 1. Install the needed dependencies ::code-group{sync="pm"} ```bash [npm] npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [yarn] yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [pnpm] pnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` ```bash [bun] bun add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue ``` :: 2. Create a `vitest.config.ts` with the following content: ```ts import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'happy-dom', }, }) ``` 3. Add a new command for test in your `package.json` ```json "scripts": { "build": "nuxt build", "dev": "nuxt dev", ... "test": "vitest" }, ``` 4. Create a simple `` component `app/components/HelloWorld.vue` with the following content: ```vue ``` 5. Create a simple unit test for this newly created component `~/components/HelloWorld.spec.ts` ```ts twoslash import { describe, expect, it } from 'vitest' import { mount } from '@vue/test-utils' import HelloWorld from './HelloWorld.vue' describe('HelloWorld', () => { it('component renders Hello world properly', () => { const wrapper = mount(HelloWorld) expect(wrapper.text()).toContain('Hello world') }) }) ``` 6. Run vitest command ::code-group{sync="pm"} ```bash [npm] npm run test ``` ```bash [yarn] yarn test ``` ```bash [pnpm] pnpm run test ``` ```bash [bun] bun run test ``` :: Congratulations, you're all set to start unit testing with `@vue/test-utils` in Nuxt! Happy testing! ### Vitest Browser Mode `@nuxt/test-utils` provides helpers for testing in [Vitest Browser Mode](https://vitest.dev/guide/browser/) via `@nuxt/test-utils/browser`. #### Setup 1. Install Vitest browser packages and a browser provider: ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @vitest/browser-playwright ``` ```bash [yarn] yarn add --dev @vitest/browser-playwright ``` ```bash [pnpm] pnpm add -D @vitest/browser-playwright ``` ```bash [bun] bun add --dev @vitest/browser-playwright ``` :: 2. Configure browser mode in your `vitest.config.ts`: ```ts twoslash // @errors: 2307 // ---cut--- import { defineConfig } from 'vitest/config' import { defineVitestProject } from '@nuxt/test-utils/config' import { playwright } from '@vitest/browser-playwright' export default defineConfig({ test: { projects: [ await defineVitestProject({ test: { name: 'browser', include: ['test/browser/**/*.{test,spec}.ts'], browser: { enabled: true, provider: playwright(), instances: [{ browser: 'chromium' }], }, // If you want to enable `page.render` and automatic cleanup, add this setup file setupFiles: ['@nuxt/test-utils/browser'], }, }), ], }, }) ``` ::note When adding `@nuxt/test-utils/browser` to `setupFiles`, if TypeScript does not automatically recognize the types for `page.render`, you can add `@nuxt/test-utils/browser` via `typescript.tsConfig.compilerOptions.types` in your `nuxt.config.ts`. :: ::note If your Vitest browser mode tests live outside `test/nuxt/` (such as in `test/browser/`), see [Adding other test directories](#adding-other-test-directories) to add them to the TypeScript context. :: #### Usage You can use `page.render` from `vitest/browser`: ```ts twoslash [test/browser/components/MyCounter.nuxt.spec.ts] // @noErrors import type { Component } from 'vue' declare module '#components' { export const MyCounter: Component } // ---cut--- import { expect, it } from 'vitest' import { page } from 'vitest/browser' // If you added the setup file, the following import is not needed import '@nuxt/test-utils/browser' import { MyCounter } from '#components' it('counter button increments the count', async () => { const screen = await page.render(MyCounter) await screen.getByRole('button', { name: 'Increment' }).click() await expect.element(screen.getByText('Count: 1')).toBeVisible() }) ``` You can also import `render` directly from `@nuxt/test-utils/browser`: ```ts twoslash // @noErrors import type { Component } from 'vue' declare module '#components' { export const MyCounter: Component } // ---cut--- import { expect, it } from 'vitest' import { render } from '@nuxt/test-utils/browser' import { MyCounter } from '#components' it('can render using the render helper', async () => { const screen = await render(MyCounter) await screen.getByRole('button', { name: 'Increment' }).click() await expect.element(screen.getByText('Count: 1')).toBeVisible() }) ``` The options object accepts `@vue/test-utils` mount options (use `container` instead of `attachTo`) and the following properties: - `route`: the initial route, or `false` to skip the initial route change (default `/`). - `spy`: enable spy component setup state (default `false`). See the [`mountSuspended`](#mountsuspended) example above. - `container`: custom `HTMLElement` container to render into (use this option instead of `@vue/test-utils` `attachTo`). - `baseElement`: custom base `HTMLElement` (default `document.body`). The return object includes the following properties: - `container`: the container `HTMLElement` the component is rendered into. - `baseElement`: the base `HTMLElement` (default `document.body`). - `locator`: root element `Locator`. - `setupState`: the return value of the component setup, mocked when the `spy` option is enabled. - `debug()`: print formatted DOM to console. - `unmount()`: unmount the component. Also records a `nuxt.unmount` trace mark. - `emitted()`: get emitted events. - `rerender(props)`: re-render the component with new props. Also records a `nuxt.rerender` trace mark. ## End-To-End Testing For end-to-end testing, we support [Vitest](https://github.com/vitest-dev/vitest), [Jest](https://jestjs.io), [Cucumber](https://cucumber.io/) and [Playwright](https://playwright.dev/) as test runners. ### Setup In each `describe` block where you are taking advantage of the `@nuxt/test-utils/e2e` helper methods, you will need to set up the test context before beginning. ```ts twoslash [test/my-test.spec.ts] import { describe, test } from 'vitest' import { $fetch, setup } from '@nuxt/test-utils/e2e' describe('My test', async () => { await setup({ // test context options }) test('my test', () => { // ... }) }) ``` Behind the scenes, `setup` performs a number of tasks in `beforeAll`, `beforeEach`, `afterEach` and `afterAll` to set up the Nuxt test environment correctly. Please use the options below for the `setup` method. #### Nuxt Config - `rootDir`: Path to a directory with a Nuxt app to be put under test. - Type: `string` - Default: `'.'` - `configFile`: Name of the configuration file. - Type: `string` - Default: `'nuxt.config'` #### Timings - `setupTimeout`: The amount of time (in milliseconds) to allow for `setupTest` to complete its work (which could include building or generating files for a Nuxt application, depending on the options that are passed). - Type: `number` - Default: `120000` or `240000` on windows - `teardownTimeout`: The amount of time (in milliseconds) to allow tearing down the test environment, such as closing the browser. - Type: `number` - Default: `30000` #### Features - `build`: Whether to run a separate build step. - Type: `boolean` - Default: `true` (`false` if `browser` or `server` is disabled, or if a `host` is provided) - `server`: Whether to launch a server to respond to requests in the test suite. - Type: `boolean` - Default: `true` (`false` if a `host` is provided) - `port`: If provided, set the launched test server port to the value. - Type: `number | undefined` - Default: `undefined` - `host`: If provided, a URL to use as the test target instead of building and running a new server. Useful for running "real" end-to-end tests against a deployed version of your application, or against an already running local server (which may provide a significant reduction in test execution timings). See the [target host end-to-end example below](https://nuxt.com/docs/5.x/getting-started/testing#target-host-end-to-end-example). - Type: `string` - Default: `undefined` - `browser`: Under the hood, Nuxt test utils uses [`playwright`](https://playwright.dev) to carry out browser testing. If this option is set, a browser will be launched and can be controlled in the subsequent test suite. - Type: `boolean` - Default: `false` - `browserOptions` - Type: `object` with the following properties - `type`: The type of browser to launch - either `chromium`, `firefox` or `webkit` - `launch`: `object` of options that will be passed to playwright when launching the browser. See [full API reference](https://playwright.dev/docs/api/class-browsertype#browser-type-launch). - `runner`: Specify the runner for the test suite. Currently, [Vitest](https://vitest.dev) is recommended. - Type: `'vitest' | 'jest' | 'cucumber'` - Default: `'vitest'` - `logLevel`: Overrides the consola log level for the server subprocess. (use NUXT_TEST_LOG_LEVEL environment variable to override) - Type: `number` - Default: `1` - `captureServerLogs`: Whether to capture server process output instead of inheriting stdio. When `true` (default), server stdout/stderr is suppressed from the console and accessible via `getServerLogs()`. Set to `false` to restore the old inherit-stdio behavior (useful when debugging a test locally). - Type: `boolean` - Default: `true` ##### Target `host` end-to-end example A common use-case for end-to-end testing is running the tests against a deployed application running in the same environment typically used for Production. For local development or automated deploy pipelines, testing against a separate local server can be more efficient and is typically faster than allowing the test framework to rebuild between tests. To utilize a separate target host for end-to-end tests, simply provide the `host` property of the `setup` function with the desired URL. ```ts import { createPage, setup } from '@nuxt/test-utils/e2e' import { describe, expect, it } from 'vitest' describe('login page', async () => { await setup({ host: 'http://localhost:8787', }) it('displays the email and password fields', async () => { const page = await createPage('/login') expect(await page.getByTestId('email').isVisible()).toBe(true) expect(await page.getByTestId('password').isVisible()).toBe(true) }) }) ``` ### APIs #### `$fetch(url)` Get the HTML of a server-rendered page. ```ts twoslash import { $fetch } from '@nuxt/test-utils/e2e' const html = await $fetch('/') ``` #### `fetch(url)` Get the response of a server-rendered page. ```ts twoslash import { fetch } from '@nuxt/test-utils/e2e' const res = await fetch('/') const { body, headers } = res ``` #### `url(path)` Get the full URL for a given page (including the port the test server is running on.) ```ts twoslash import { url } from '@nuxt/test-utils/e2e' const pageUrl = url('/page') // 'http://localhost:6840/page' ``` #### `getServerLogs()` Returns the lines captured from the server subprocess's stdout/stderr since the last `startServer()` call (or `clearServerLogs()`). Only populated when `captureServerLogs` is `true` (the default). ```ts twoslash import { expect, it, vi } from 'vitest' import { $fetch, clearServerLogs, getServerLogs } from '@nuxt/test-utils/e2e' it('captures console.log output from a server route', async () => { clearServerLogs() await $fetch('/api/log-test') await vi.waitFor(() => { expect(getServerLogs().some(line => line.includes('[test] server-log-marker'))).toBe(true) }) }) ``` #### `clearServerLogs()` Clears the captured server log lines. Useful between requests when you want to assert only on the logs produced by a specific operation. ### Testing in a Browser We provide built-in support using Playwright within `@nuxt/test-utils`, either programmatically or via the Playwright test runner. #### `createPage(url)` Within `vitest`, `jest` or `cucumber`, you can create a configured Playwright browser instance with `createPage`, and (optionally) point it at a path from the running server. You can find out more about the API methods available in the [Playwright documentation](https://playwright.dev/docs/api/class-page). When you pass a path, `createPage` waits for your Nuxt app to finish hydrating before it resolves. On later navigations, pass `waitUntil: 'hydration'` to `page.goto` to wait for hydration again. ```ts twoslash import { createPage, url } from '@nuxt/test-utils/e2e' const page = await createPage('/page') // you can access all the Playwright APIs from the `page` variable await page.goto(url('/other-page'), { waitUntil: 'hydration' }) ``` ::tip Interacting with a page before it has hydrated (for example, clicking a button before its event handler is attached) can make tests flaky. :: #### Testing with Playwright Test Runner We also provide first-class support for testing Nuxt within [the Playwright test runner](https://playwright.dev/docs/intro). ::code-group{sync="pm"} ```bash [npm] npm i --save-dev @playwright/test @nuxt/test-utils ``` ```bash [yarn] yarn add --dev @playwright/test @nuxt/test-utils ``` ```bash [pnpm] pnpm add -D @playwright/test @nuxt/test-utils ``` ```bash [bun] bun add --dev @playwright/test @nuxt/test-utils ``` ```bash [deno] deno add --dev npm:@playwright/test npm:@nuxt/test-utils ``` :: You can provide global Nuxt configuration, with the same configuration details as the `setup()` function mentioned earlier in this section. ```ts [playwright.config.ts] import { fileURLToPath } from 'node:url' import { defineConfig, devices } from '@playwright/test' import type { ConfigOptions } from '@nuxt/test-utils/playwright' export default defineConfig({ use: { nuxt: { rootDir: fileURLToPath(new URL('.', import.meta.url)), }, }, // ... }) ``` ::read-more{title="See full example config" to="https://github.com/nuxt/test-utils/blob/main/examples/app-playwright/playwright.config.ts" target="_blank"} :: Your test file should then use `expect` and `test` directly from `@nuxt/test-utils/playwright`: ```ts [tests/example.test.ts] import { expect, test } from '@nuxt/test-utils/playwright' test('test', async ({ page, goto }) => { await goto('/', { waitUntil: 'hydration' }) await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') }) ``` You can alternatively configure your Nuxt server directly within your test file: ```ts [tests/example.test.ts] import { expect, test } from '@nuxt/test-utils/playwright' test.use({ nuxt: { rootDir: fileURLToPath(new URL('..', import.meta.url)), }, }) test('test', async ({ page, goto }) => { await goto('/', { waitUntil: 'hydration' }) await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!') }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Transitions" description: "Apply transitions between pages and layouts with Vue or native browser View Transitions." canonical_url: "https://nuxt.com/docs/5.x/getting-started/transitions" --- # Transitions > Apply transitions between pages and layouts with Vue or native browser View Transitions. ::note Nuxt leverages Vue's [``](https://vuejs.org/guide/built-ins/transition#the-transition-component) component to apply transitions between pages and layouts. :: ::important Because Nuxt uses Vue's `` component, a page or layout you want to animate must have a **single root element**. A page or layout with multiple root elements (a fragment) cannot be animated, so the transition will not run and navigating between routes may error. Nuxt warns about this in development. Wrap the template in a single root element (for example a `
`). :: ## Page Transitions You can enable page transitions to apply an automatic transition for all your [pages](https://nuxt.com/docs/5.x/directory-structure/app/pages). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: { name: 'page', mode: 'out-in' }, }, }) ``` ::note If you are changing layouts as well as pages, the page transition you set here will not run. Instead, you should set a [layout transition](https://nuxt.com/docs/5.x/getting-started/transitions#layout-transitions). :: To start adding transition between your pages, add the following CSS to your [`app.vue`](https://nuxt.com/docs/5.x/directory-structure/app/app): ::code-group ```vue [app/app.vue] ``` ```vue [app/pages/index.vue] ``` ```vue [app/pages/about.vue] ``` :: This produces the following result when navigating between pages: To set a different transition for a page, set the `pageTransition` key in [`definePageMeta`](https://nuxt.com/docs/5.x/api/utils/define-page-meta) of the page: ::code-group ```vue twoslash [pages/about.vue] ``` ```vue [app/app.vue] ``` :: Moving to the about page will add the 3d rotation effect: ## Layout Transitions You can enable layout transitions to apply an automatic transition for all your [layouts](https://nuxt.com/docs/5.x/directory-structure/app/layouts). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { layoutTransition: { name: 'layout', mode: 'out-in' }, }, }) ``` To start adding transition between your pages and layouts, add the following CSS to your [`app.vue`](https://nuxt.com/docs/5.x/directory-structure/app/app): ::code-group ```vue [app/app.vue] ``` ```vue [app/layouts/default.vue] ``` ```vue [app/layouts/orange.vue] ``` ```vue [app/pages/index.vue] ``` ```vue [app/pages/about.vue] ``` :: This produces the following result when navigating between pages: Similar to `pageTransition`, you can apply a custom `layoutTransition` to the page component using `definePageMeta`: ```vue twoslash [pages/about.vue] ``` ## Global Settings You can customize these default transition names globally using `nuxt.config`. Both `pageTransition` and `layoutTransition` keys accept [`TransitionProps`](https://vuejs.org/api/built-in-components#transition) as JSON serializable values where you can pass the `name`, `mode` and other valid transition-props of the custom CSS transition. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: { name: 'fade', mode: 'out-in', // default }, layoutTransition: { name: 'slide', mode: 'out-in', // default }, }, }) ``` ::warning If you change the `name` property, you also have to rename the CSS classes accordingly. :: To override the global transition property, use the `definePageMeta` to define page or layout transitions for a single Nuxt page and override any page or layout transitions that are defined globally in `nuxt.config` file. ```vue twoslash [pages/some-page.vue] ``` ## Disable Transitions `pageTransition` and `layoutTransition` can be disabled for a specific route: ```vue twoslash [pages/some-page.vue] ``` Or globally in the `nuxt.config`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { pageTransition: false, layoutTransition: false, }, }) ``` ## JavaScript Hooks For advanced use-cases, you can use JavaScript hooks to create highly dynamic and custom transitions for your Nuxt pages. This way presents perfect use-cases for JavaScript animation libraries such as [GSAP](https://gsap.com). ```vue twoslash [pages/some-page.vue] ``` ::tip Learn more about additional [JavaScript hooks](https://vuejs.org/guide/built-ins/transition#javascript-hooks) available in the `Transition` component. :: ## Dynamic Transitions To apply dynamic transitions using conditional logic, you can leverage inline [middleware](https://nuxt.com/docs/5.x/directory-structure/app/middleware) to assign a different transition name to `to.meta.pageTransition`. ::code-group ```vue twoslash [pages/[id\\].vue] ``` ```vue [app/layouts/default.vue] ``` :: The page now applies the `slide-left` transition when going to the next id and `slide-right` for the previous one: ## Transition with NuxtPage When `` is used in `app.vue`, transitions can be configured with the `transition` prop to activate transitions globally. ```vue [app/app.vue] ``` ::note Remember, this page transition cannot be overridden with `definePageMeta` on individual pages. :: ## View Transitions API (Experimental) Nuxt ships with an experimental implementation of the [**View Transitions API**](https://developer.chrome.com/docs/web-platform/view-transitions) (see [MDN](https://developer.mozilla.org/en-US/docs/Web/API/View_Transition_API)). This is an exciting new way to implement native browser transitions which (among other things) have the ability to transition between unrelated elements on different pages. You can check a demo [on StackBlitz](https://stackblitz.com/edit/nuxt-view-transitions). The Nuxt integration can be enabled with the `experimental.viewTransition` option in your configuration file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { viewTransition: true, }, }) ``` The possible values are: `false`, `true`, or `'always'`. If set to true, Nuxt will not apply transitions if the user's browser matches `prefers-reduced-motion: reduce` (recommended). If set to `always`, Nuxt will always apply the transition and it is up to you to respect the user's preference. By default, view transitions are enabled for all [pages](https://nuxt.com/docs/5.x/directory-structure/app/pages), but you can set a different global default. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { // Disable view transitions globally, and opt-in on a per page basis viewTransition: false, }, }) ``` It is possible to override the default `viewTransition` value for a page by setting the `viewTransition` key in [`definePageMeta`](https://nuxt.com/docs/5.x/api/utils/define-page-meta) of the page: ```vue twoslash [pages/about.vue] ``` ::warning Overriding view transitions on a per-page basis will only have an effect if you have enabled the `experimental.viewTransition` option. :: ### View Transition Types :badge[v4.4]{color="info" size="xs" .align-middle} [View transition types](https://developer.chrome.com/blog/view-transitions-update-io24#view-transition-types) allow you to apply different CSS animations depending on the type of navigation. This is useful for creating asymmetric transitions (e.g., a different animation when navigating forward vs. backward). Types are set on the [`ViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) and can be targeted in CSS using the [`:active-view-transition-type()`](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/:active-view-transition-type) pseudo-class selector. You can set default types globally in your `nuxt.config.ts`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ app: { viewTransition: { enabled: true, types: ['slide'], }, }, }) ``` Or configure types per-page using `definePageMeta`. Per-page types support both static arrays and functions for dynamic behavior: ```vue twoslash [pages/detail.vue] ``` You can also use functions for `types`, `toTypes`, and `fromTypes` in `definePageMeta` to determine types dynamically based on the route: ```vue twoslash [pages/[id].vue] ``` Then target these types in your CSS: ```css /* Default crossfade */ ::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.3s; } /* Slide left animation */ html:active-view-transition-type(slide-left) { &::view-transition-old(root) { animation: slide-out-left 0.3s ease-in-out; } &::view-transition-new(root) { animation: slide-in-right 0.3s ease-in-out; } } /* Slide right animation */ html:active-view-transition-type(slide-right) { &::view-transition-old(root) { animation: slide-out-right 0.3s ease-in-out; } &::view-transition-new(root) { animation: slide-in-left 0.3s ease-in-out; } } ``` ::note Function values for `types`, `toTypes`, and `fromTypes` only work in `definePageMeta`, not in `nuxt.config.ts` (where only static `string[]` is supported). :: The `page:view-transition:start` hook provides access to the [`ViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition) object, which includes a [`types`](https://developer.mozilla.org/en-US/docs/Web/API/ViewTransition/types) property (`ViewTransitionTypeSet`) that can be read or modified at runtime: ```ts [plugins/view-transition.client.ts] export default defineNuxtPlugin((nuxtApp) => { nuxtApp.hook('page:view-transition:start', (transition) => { // Read or modify types at runtime console.log([...transition.types]) }) }) ``` If you are also using Vue transitions like `pageTransition` and `layoutTransition` (see above) to achieve the same result as the new View Transitions API, then you may wish to *disable* Vue transitions if the user's browser supports the newer, native web API. You can do this by creating `~/middleware/disable-vue-transitions.global.ts` with the following contents: ```ts export default defineNuxtRouteMiddleware((to) => { if (import.meta.server || !document.startViewTransition) { return } // Disable built-in Vue transitions to.meta.pageTransition = false to.meta.layoutTransition = false }) ``` ### Known Issues - If you perform data fetching within your page setup functions, you may wish to reconsider using this feature for the moment. (By design, View Transitions completely freeze DOM updates while they are taking place.) We're looking at restricting the View Transition to the final moments before `` resolves, but in the interim you may want to consider carefully whether to adopt this feature if this describes you. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Upgrade Guide" description: "Learn how to upgrade to the latest Nuxt version." canonical_url: "https://nuxt.com/docs/5.x/getting-started/upgrade" --- # Upgrade Guide > Learn how to upgrade to the latest Nuxt version. ## Upgrading Nuxt ### Latest release To upgrade Nuxt to the [latest release](https://github.com/nuxt/nuxt/releases), use the `nuxt upgrade` command. ::code-group{sync="pm"} ```bash [npm] npx nuxt upgrade ``` ```bash [yarn] yarn nuxt upgrade ``` ```bash [pnpm] pnpm nuxt upgrade ``` ```bash [bun] bun x nuxt upgrade ``` ```bash [deno] deno x nuxt upgrade ``` :: ### Nightly Release Channel To use the latest Nuxt build and test features before their release, read about the [nightly release channel](https://nuxt.com/docs/5.x/guide/going-further/nightly-release-channel) guide. ## Migrating to Nuxt 5 Nuxt 5 includes significant improvements and changes. This guide covers what changed and how to migrate an existing Nuxt 4 application. First, upgrade Nuxt using the [instructions above](#upgrading-nuxt). Most Nuxt 5 behavior is the new default. Where a change can still be configured, the section describing it names the option that restores the Nuxt 4 behavior; where it cannot, the section says so. The changes are: - **Nitro v3**: The server engine is [rewritten on h3 v2 and srvx](https://nuxt.com/docs/5.x/getting-started/upgrade#migration-to-nitro-v3), with a [compatibility layer](https://nuxt.com/docs/5.x/getting-started/upgrade#nitro-v2-compatibility) for server code you have not migrated yet - **Server imports**: Server utilities come from [`nuxt/server`](https://nuxt.com/docs/5.x/getting-started/upgrade#server-imports-move-to-nuxtserver), including [session helpers](https://nuxt.com/docs/5.x/getting-started/upgrade#sessions-come-from-nuxtserver) - **Vite Environment API**: Vite plugins are [registered and configured per environment](https://nuxt.com/docs/5.x/getting-started/upgrade#migration-to-vite-environment-api) - **Vite 8**: The bundler is [Rolldown rather than esbuild and Rollup](https://nuxt.com/docs/5.x/getting-started/upgrade#migration-to-vite-8) - **Nitro as a Vite environment**: With `@nuxt/vite-builder`, server and client are [built by one Vite build](https://nuxt.com/docs/5.x/getting-started/upgrade#nitro-builds-as-a-vite-environment) - **Typed `$fetch`**: `$fetch` and `useFetch` are typed from [generated route types](https://nuxt.com/docs/5.x/getting-started/upgrade#typed-fetch-rebuilt-on-generated-route-types) rather than nitro's `InternalApi` - **`jiti` is no longer bundled**: `nuxt.config`, `modules/` and layer configs are [loaded by the runtime](https://nuxt.com/docs/5.x/getting-started/upgrade#jiti-is-no-longer-bundled) - **`process.*` type augmentation removed**: TypeScript no longer exposes [deprecated `process.*` flags](https://nuxt.com/docs/5.x/getting-started/upgrade#process-type-augmentation-removed) on `NodeJS.Process` - **Case-sensitive routing**: Page routes [match URL casing exactly](https://nuxt.com/docs/5.x/getting-started/upgrade#case-sensitive-routing), consistent with Nitro - **Normalized page names**: Page component names [match their route names](https://nuxt.com/docs/5.x/getting-started/upgrade#normalized-page-component-names) for consistent `` behavior - **Serializable page meta**: Statically analysable `definePageMeta` values are [written into the generated route record](https://nuxt.com/docs/5.x/getting-started/upgrade#serializable-page-meta-extracted-at-build-time) - **Client-side payload extraction**: [`experimental.payloadExtraction`](https://nuxt.com/docs/5.x/getting-started/upgrade#payload-extraction-defaults-to-client) is `'client'`, inlining the payload in the HTML of a prerendered page - **`clearNuxtState` resets to defaults**: `clearNuxtState` [resets state to its initial value](https://nuxt.com/docs/5.x/getting-started/upgrade#respect-defaults-when-clearing-usestate) instead of setting it to `undefined` - **Early return from `navigateTo`**: A successful top-level `await navigateTo()` [stops the rest of your `setup()` running](https://nuxt.com/docs/5.x/getting-started/upgrade#early-return-from-navigateto) - **Non-async `callHook`**: [`callHook` may return `void`](https://nuxt.com/docs/5.x/getting-started/upgrade#non-async-callhook) instead of always returning a `Promise` - **Comment node placeholders**: Client-only components use [comment nodes instead of `
`](https://nuxt.com/docs/5.x/getting-started/upgrade#client-only-comment-placeholders) as SSR placeholders, fixing a scoped styles hydration issue - **Server auto-imports off**: Server code [imports Nitro's helpers explicitly](https://nuxt.com/docs/5.x/getting-started/upgrade#server-auto-imports-are-now-opt-in) rather than relying on the auto-import layer - **Top-level `server/utils/` only**: Files in [subdirectories of `server/utils/`](https://nuxt.com/docs/5.x/getting-started/upgrade#nested-serverutils-files-are-no-longer-auto-imported) are no longer auto-imported - **Parsed `error.data`**: [`experimental.parseErrorData`](https://nuxt.com/docs/5.x/getting-started/upgrade#experimentalparseerrordata-is-no-longer-configurable) is forced on, so `error.data` reaches the error page unstringified - **Error response format**: Page errors return the error page and server route errors return JSON, [regardless of the request headers](https://nuxt.com/docs/5.x/getting-started/upgrade#error-format-depends-on-where-the-error-is-thrown) - **Builder file watcher**: Nuxt [reuses the builder's own watcher](https://nuxt.com/docs/5.x/getting-started/upgrade#builder-file-watcher-in-development) in development instead of starting a second one - **Server-only head composables**: `useServerHead`, `useServerHeadSafe` and `useServerSeoMeta` are [no longer auto-imported](https://nuxt.com/docs/5.x/getting-started/upgrade#server-only-head-composables-are-no-longer-auto-imported), and `unhead.legacy` is ignored - **Vue Options API disabled**: The [Options API is compiled out of the client bundle](https://nuxt.com/docs/5.x/getting-started/upgrade#vue-options-api-disabled-by-default) to reduce its size - **Typed pages**: [`experimental.typedPages`](https://nuxt.com/docs/5.x/getting-started/upgrade#typed-pages-enabled-by-default) is enabled by default for type-checked routing - **Node-resolution config types**: The generated `node` tsconfig describes [how the runtime loads `nuxt.config` and modules](https://nuxt.com/docs/5.x/getting-started/upgrade#jiti-is-no-longer-bundled) - **Stricter side-effect imports**: The generated `tsconfig.json` enables [`noUncheckedSideEffectImports`](https://nuxt.com/docs/5.x/getting-started/upgrade#stricter-side-effect-imports) to match the TypeScript 7 default - **TypeScript `baseUrl` ignored**: Generated TypeScript configurations [no longer use `compilerOptions.baseUrl`](https://nuxt.com/docs/5.x/getting-started/upgrade#typescript-baseurl-is-ignored) to resolve Nuxt aliases - **PostCSS plugins**: `autoprefixer` and `cssnano` are [no longer installed or configured by default](https://nuxt.com/docs/5.x/getting-started/upgrade#postcss-plugins-are-no-longer-installed-by-default) - **Optional dependencies**: [`@vitejs/plugin-vue-jsx`](https://nuxt.com/docs/5.x/getting-started/upgrade#vitejsplugin-vue-jsx-is-now-optional) and [`giget`](https://nuxt.com/docs/5.x/getting-started/upgrade#giget-is-now-optional-remote-layers) are installed on demand - **Removed options**: [Several experimental options](https://nuxt.com/docs/5.x/getting-started/upgrade#removed-configuration-options) are gone, because the behavior they controlled is now unconditional ::note Nuxt 5 is still in development, so this page changes as further work lands. Check back here regularly while you are migrating. :: Breaking or significant changes are documented below along with migration steps and the configuration available for each. ### `process.*` Type Augmentation Removed 🚦 **Impact Level**: Minimal #### What Changed Nuxt no longer augments `NodeJS.Process` with `browser`, `client`, `dev`, `server`, and `test`. Build-time defines for those flags remain for compatibility, but TypeScript will not treat them as known properties. Prefer [`import.meta.*`](https://nuxt.com/docs/5.x/api/advanced/import-meta). Those flags are replaced at build time and stay tree-shakeable. #### Migration Steps Replace legacy checks in your app code, modules, and libraries: ```ts // Before // eslint-disable-next-line nuxt/prefer-import-meta if (process.server) { /* ... */ } // After if (import.meta.server) { /* ... */ } ``` The [`nuxt/prefer-import-meta`](https://eslint.nuxt.com/packages/plugin#nuxtprefer-import-meta) ESLint rule flags remaining `process.*` usage. ### Case-Sensitive Routing 🚦 **Impact Level**: Minimal #### What Changed `router.options.sensitive` now defaults to `true`, so page routes match URLs case-sensitively, consistent with Nitro. For example, `/About` no longer matches `pages/about.vue`. `sensitive` is now read only from `nuxt.config`, because the server matches route rules with the same setting. The build fails if `app/router.options.ts` sets it. #### Migration Steps Update links to use the same casing as their page routes. To keep case-insensitive matching, set `sensitive` in `nuxt.config` (and remove it from `app/router.options.ts` if it is there): ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ router: { options: { sensitive: false, }, }, }) ``` If you opt out, regex constraints in your route rules must be written for [lower-case letters](https://nuxt.com/docs/5.x/guide/recipes/custom-routing#route-rules-with-case-insensitive-routing). ### Normalized Page Component Names 🚦 **Impact Level**: Minimal #### What Changed `experimental.normalizePageNames` is now enabled by default, so page components are named after their route rather than after their filename. `pages/foo/index.vue` and `pages/bar/index.vue` are named `foo` and `bar` instead of both being named `index`. #### Migration Steps If you filter `` by component name, update `include`/`exclude` to the route names. To keep the filename-based names: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { normalizePageNames: false, }, }) ``` ### Serializable Page Meta Extracted at Build Time 🚦 **Impact Level**: Minimal #### What Changed `experimental.extractSerializablePageMeta` is now enabled by default. Every JSON-serializable `definePageMeta()` property is written into the generated route record, rather than only a fixed list of keys (plus `experimental.extraPageMetaExtractionKeys`) with the rest resolved at runtime from a per-page meta module. Properties whose values cannot be serialized, such as functions, variable references and spreads, are still resolved at runtime. #### Migration Steps If you read page meta from the route record at build time (in a `pages:extend` hook, for example), expect to see the extracted values there. To keep the previous behavior: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { extractSerializablePageMeta: false, }, }) ``` ### Payload Extraction Defaults to `'client'` 🚦 **Impact Level**: Minimal #### What Changed `experimental.payloadExtraction` now defaults to `'client'` rather than `true`. The payload of a prerendered or cached page is inlined in the HTML for the initial render, and the `_payload.json` file is only fetched for client-side navigation. It is still forced to `false` when `ssr` is disabled. #### Migration Steps No change is needed for most applications. If you fetch `_payload.json` for the initial render yourself, or measure HTML size in a test, set the option explicitly: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { payloadExtraction: true, }, }) ``` ::read-more{to="https://nuxt.com/docs/5.x/getting-started/prerendering#payload-extraction"} Read more about payload extraction and the consequences of each mode. :: ### Respect Defaults When Clearing `useState` 🚦 **Impact Level**: Minimal #### What Changed `experimental.defaults.useState.resetOnClear` is now enabled by default, so [`clearNuxtState`](https://nuxt.com/docs/5.x/api/utils/clear-nuxt-state) resets state to the value produced by the `init` function of [`useState`](https://nuxt.com/docs/5.x/api/composables/use-state) instead of setting it to `undefined`. #### Migration Steps If your code checks for `undefined` after clearing state, check for the initial value instead. To keep the previous behavior: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { defaults: { useState: { resetOnClear: false, }, }, }, }) ``` ### Early Return From `navigateTo` 🚦 **Impact Level**: Minimal #### What Changed `experimental.navigateToEarlyReturn` is now enabled by default. A top-level `await navigateTo()` in ` ``` ### Error Format Depends on Where the Error Is Thrown 🚦 **Impact Level**: Moderate #### What Changed An error thrown while rendering a page returns the HTML error page. An error thrown in a server route or server middleware returns JSON. The `Accept`, `Sec-Fetch-Mode`, and `User-Agent` request headers and the `/api/` path prefix no longer affect the format. Compared with Nuxt 4: - **Page errors** return the HTML error page with the error's status code, even for requests that accept JSON. This includes `fetch()` and `$fetch()` calls in the browser, `curl`, and requests with an `Accept: application/json` header. - **Server route and server middleware errors** return JSON, even for a browser navigation. This includes server middleware that throws for a page route, such as an authentication check. - **Payload errors** for `_payload.json` requests still return JSON. #### Reasons for Change In Nuxt 4, the request headers determined the error format, so one URL could return a different error response to each kind of client. Caches had to vary on those headers. A `fetch()` call to a page also returned JSON, while navigating to the same page returned HTML. Each URL now has a single error response. #### Migration Steps If you read the JSON body of a failed page request, such as `error.data` from `$fetch('/some-page')`, read the response status instead. To keep structured error data, move it into a server route. If server middleware throws for page routes to show the error page, move the check into [route middleware](https://nuxt.com/docs/5.x/directory-structure/app/middleware). Route middleware runs during the page render, so an error it throws returns the error page: ```ts [app/middleware/auth.global.ts] export default defineNuxtRouteMiddleware((to) => { if (to.path.startsWith('/admin') && !useCookie('session').value) { throw createError({ status: 401, statusText: 'Unauthorized' }) } }) ``` To restore the Nuxt 4 behavior, disable `experimental.inlineErrorRendering`. This also restores the Nuxt 4 way of rendering the error page, through an internal request to `/__nuxt_error`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { inlineErrorRendering: false, }, }) ``` ### Removed Configuration Options 🚦 **Impact Level**: Minimal #### What Changed Three experimental options are removed, because the behavior they controlled is now unconditional in Nuxt 5: - `experimental.viteEnvironmentApi`. See [Migration to Vite Environment API](#migration-to-vite-environment-api). - `experimental.routeTypedFetch`. See [Typed `$fetch` Rebuilt on Generated Route Types](#typed-fetch-rebuilt-on-generated-route-types). - `experimental.renderJsonPayloads`. Payloads are always rendered and parsed as JSON, with support for revivers. `experimental.externalVue` is removed too, which is covered in [its own section](#removal-of-experimentalexternalvue). #### Migration Steps Remove the options from your configuration: ```diff export default defineNuxtConfig({ experimental: { - viteEnvironmentApi: true, - routeTypedFetch: true, - renderJsonPayloads: true, }, }) ``` ### `@vitejs/plugin-vue-jsx` Is Now Optional 🚦 **Impact Level**: Minimal #### What Changed `@vitejs/plugin-vue-jsx` is no longer installed by default with `@nuxt/vite-builder`. It is now an optional peer dependency that is loaded on demand only when a `.jsx` or `.tsx` file is encountered during the build. If your project uses JSX/TSX components, Nuxt will automatically detect this and prompt you to install the package. #### Reasons for Change The `@vitejs/plugin-vue-jsx` plugin pulls in a significant dependency tree (Babel, `@vue/babel-plugin-jsx`, etc.) that is unnecessary for projects that don't use JSX. Making it optional reduces the default install size and speeds up dependency resolution for the majority of Nuxt projects. #### Migration Steps If your project uses `.jsx` or `.tsx` files, add `@vitejs/plugin-vue-jsx` as a dev dependency: ::code-group{sync="pm"} ```bash [npm] npm install -D @vitejs/plugin-vue-jsx ``` ```bash [yarn] yarn add -D @vitejs/plugin-vue-jsx ``` ```bash [pnpm] pnpm add -D @vitejs/plugin-vue-jsx ``` ```bash [bun] bun add -D @vitejs/plugin-vue-jsx ``` :: Alternatively, Nuxt will prompt you to install it automatically the first time a JSX/TSX file is processed during development. If your project does not use JSX, no changes are needed. ### PostCSS Plugins Are No Longer Installed by Default 🚦 **Impact Level**: Medium #### What Changed `autoprefixer` and `cssnano` are no longer configured by default, and are no longer dependencies of `@nuxt/vite-builder`. They are optional peer dependencies, applied only when you name them in `postcss.plugins`. Vite minifies CSS as part of the build, so `cssnano` is not needed for that. The webpack and rspack builders still configure both, along with `postcss-import` and `postcss-url`. #### Reasons for Change Both packages were installed for every project to run a pass most projects do not configure, and browser support for the properties `autoprefixer` prefixes has moved on since the default was set. #### Migration Steps If you need either plugin, install it and enable it explicitly: ::code-group{sync="pm"} ```bash [npm] npm install -D autoprefixer cssnano ``` ```bash [yarn] yarn add -D autoprefixer cssnano ``` ```bash [pnpm] pnpm add -D autoprefixer cssnano ``` ```bash [bun] bun add -D autoprefixer cssnano ``` :: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ postcss: { plugins: { autoprefixer: {}, cssnano: {}, }, }, }) ``` If a configured plugin is not installed, Nuxt prompts you to install it and reports the command to run. ### `giget` Is Now Optional (Remote Layers) 🚦 **Impact Level**: Minimal #### What Changed `giget` is no longer installed by default. It is now an optional peer dependency of `@nuxt/kit`, needed only to download a layer that `extends` names by remote URL: ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: ['github:my-org/my-theme'], }) ``` Local layers, layers in `~~/layers/`, and layers installed as packages are unaffected. #### Reasons for Change Most projects never extend from a remote source, so the downloader was being installed for everyone to serve a small minority. Downloading a layer at config-resolution time is also the least reproducible way to consume one: the fetch happens outside your package manager, so it is not in your lockfile and, unless you pin it yourself, not pinned to a revision. #### Migration Steps **Preferred: move the layer into `package.json`.** Every major package manager understands git URLs, so a remote layer can be a normal dependency. This puts it in your lockfile, pins it to an exact commit, and installs it alongside everything else: ```json [package.json] { "devDependencies": { "my-theme": "github:my-org/my-theme#4a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b" } } ``` Then extend from it by package name: ```ts [nuxt.config.ts] export default defineNuxtConfig({ extends: ['my-theme'], }) ``` The part after `#` is any commit-ish, so a tag (`#v1.2.0`) or `#semver:^1.2.0` works too; a full commit SHA is the only form that is genuinely immutable. **Otherwise, install `giget`** and keep using a remote `extends` entry: ::code-group{sync="pm"} ```bash [npm] npm install -D giget ``` ```bash [yarn] yarn add -D giget ``` ```bash [pnpm] pnpm add -D giget ``` ```bash [bun] bun add -D giget ``` :: If a remote layer is resolved without `giget` present, Nuxt reports which `extends` entry needs it. ### Removal of Legacy `_renderResponse` Support 🚦 **Impact Level**: Minimal #### What Changed `ssrContext._renderResponse` is no longer checked as a fallback. Only the internal `ssrContext['~renderResponse']` (set by Nuxt's own router composable) is used. #### Reasons for Change The `_renderResponse` property on `ssrContext` was kept as a backward-compatibility fallback after [#33896](https://github.com/nuxt/nuxt/pull/33896) migrated the internal API to `~renderResponse`. The TODO comments indicated it should be removed in Nuxt v5. #### Migration Steps If you were setting `ssrContext._renderResponse` directly (which was never a public API), use `ssrContext['~renderResponse']` instead. The Nuxt router composable already uses the new property, so no changes are needed if you're going through `navigateTo` or route middleware. ### Non-Async `callHook` 🚦 **Impact Level**: Minimal #### What Changed With the upgrade to [hookable v6](https://github.com/unjs/hookable), `callHook` may now return `void` instead of always returning `Promise`. This is a significant performance improvement that avoids unnecessary `Promise` allocations when there are no registered hooks or all hooks are synchronous. Nuxt 4 wrapped `callHook` with `Promise.resolve()` so that existing `.then()` and `.catch()` chaining continued to work. That wrapper is removed, and `experimental.asyncCallHook` now defaults to `false`. ::tip This affects both build-time Nuxt hooks (used by Nuxt modules) and runtime Nuxt hooks (which you might use in your application code). :: #### Reasons for Change Hookable v6's `callHook` is 20-40x faster because it avoids creating a `Promise` when one is not needed. This benefits applications with many hook call sites. #### Migration Steps If you or your modules use `callHook` with `.then()` or `.catch()` chaining, switch to `await`: ```diff - nuxtApp.callHook('my:hook', data).then(() => { ... }) + await nuxtApp.callHook('my:hook', data) ``` ```diff - nuxtApp.hooks.callHook('my:hook', data).catch(err => { ... }) + try { await nuxtApp.hooks.callHook('my:hook', data) } catch (err) { ... } ``` To keep `callHook` always returning a `Promise`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { asyncCallHook: true, }, }) ``` ### Client-Only Comment Placeholders 🚦 **Impact Level**: Minimal #### What Changed Client-only components (`.client.vue` files and `createClientOnly()` wrappers) now render an HTML comment (``) on the server instead of an empty `
` element. #### Reasons for Change When the placeholder `
` and the actual component root share the same tag name, Vue's runtime skips re-applying `setScopeId` during hydration. This causes scoped styles to be missing after the component mounts. Using a comment node avoids the tag name collision entirely. #### Migration Steps If you rely on the placeholder `
` to inherit attributes (`class`, `style`, etc.) for layout purposes (e.g., reserving space to prevent layout shift), wrap the component in `` with a `#fallback` slot instead: ```diff - + + + + ``` To revert to the previous `
` placeholder behavior: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { clientNodePlaceholder: false, }, }) ``` ### Stricter Side-Effect Imports 🚦 **Impact Level**: Minimal #### What Changed Nuxt's generated `tsconfig.json` enables [`noUncheckedSideEffectImports`](https://www.typescriptlang.org/tsconfig/#noUncheckedSideEffectImports). This is a default in TypeScript 7, so adopting it early keeps your project aligned ahead of that upgrade. With this option on, a side-effect-only import (`import './setup'`) that TypeScript cannot resolve to a module is now a type error, whereas it was previously ignored. This only affects type-checking (`nuxt typecheck` and your editor), not runtime behavior. #### Reasons for Change Unresolved side-effect imports were silently ignored, so a typo or a deleted file could pass type-checking. Flagging them catches these mistakes and matches the TypeScript 7 default. #### Migration Steps If type-checking now errors on a side-effect import of a non-code asset (for example `import '~/assets/styles.css'`), add an ambient module declaration so TypeScript knows the import is valid: ```ts [types.d.ts] declare module '*.css' {} ``` ::tip You can revert to the previous behavior by disabling the option in your `nuxt.config`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ typescript: { tsConfig: { compilerOptions: { noUncheckedSideEffectImports: false, }, }, }, }) ``` :: ### Vue Options API Disabled by Default 🚦 **Impact Level**: Minimal #### What Changed Nuxt sets Vue's [`__VUE_OPTIONS_API__`](https://vuejs.org/api/compile-time-flags#VUE_OPTIONS_API) feature flag to `false`, which compiles Vue's Options API runtime out of the client bundle. #### Reasons for Change The Options API runtime ships in every client bundle even though most Nuxt applications are written with the Composition API and ` ``` ::read-more{title="NuxtRouteAnnouncer" to="https://nuxt.com/docs/5.x/api/components/nuxt-route-announcer"} :: For in-page updates that are not navigations, such as form validation or toasts, use [``](https://nuxt.com/docs/5.x/api/components/nuxt-announcer) with [`useAnnouncer`](https://nuxt.com/docs/5.x/api/composables/use-announcer) instead. ## Page Titles Because the route announcer follows the document title, giving every route a distinct title is the single most valuable thing you can do. Set a global template in `app.vue` and let each page fill in its own part: ```vue [app.vue] ``` ```vue [app/pages/about.vue] ``` If your titles come from route metadata rather than from the page itself, you can read [`definePageMeta`](https://nuxt.com/docs/5.x/directory-structure/app/pages#page-metadata) values from [`useRoute`](https://nuxt.com/docs/5.x/api/composables/use-route) in a layout. ::read-more{title="SEO and Meta" to="https://nuxt.com/docs/5.x/getting-started/seo-meta#dynamic-title"} :: ## Links Use [``](https://nuxt.com/docs/5.x/api/components/nuxt-link) for in-app navigation. It renders a real ``, which means it is focusable, appears in the tab order, and works with middle-click and "open in new tab", all of which you would have to reimplement on a `
` with a `@click` handler calling `navigateTo`. ```vue ``` In a menu or a set of breadcrumbs, the link matching the current route already exposes `aria-current="page"`, so assistive technology can tell which item you are on. Where a different token describes the relationship better, such as a step in a multi-page form, set [`ariaCurrentValue`](https://nuxt.com/docs/5.x/api/components/nuxt-link#routerlink): ```vue ``` Links to files in your `public/` directory, or to another app on the same origin, are not routes that Vue Router knows about. Mark them as [`external`](https://nuxt.com/docs/5.x/api/components/nuxt-link#handling-static-file-and-cross-app-links) so the browser performs a real navigation instead of failing to match a route. ::read-more{title="NuxtLink" to="https://nuxt.com/docs/5.x/api/components/nuxt-link"} :: ## Focus Management After a client-side navigation, focus stays where it was, which is usually the link the user just activated. Vue Router does not move it and neither does Nuxt, so a keyboard user can end up tabbing through the whole header again to reach the content that just changed. A skip link as the first tab stop of your app is the conventional fix, and it helps on the initial page load too: ```vue [app.vue] ``` `
` is not focusable on its own, so it needs `tabindex="-1"` to accept focus from the skip link or from a script. Use `-1` rather than a positive value, which would move the element in the tab order and surprise everyone else. If it suits your app, you can go further and move focus to the main region after every navigation from a plugin: ```ts [app/plugins/focus-main.client.ts] export default defineNuxtPlugin(() => { useRouter().afterEach((to, from) => { if (to.path === from.path) { return } nextTick(() => document.getElementById('main')?.focus()) }) }) ``` ::tip Navigate around your app with the keyboard alone. Tabbing from the skip link into `
` after a couple of navigations will surface most focus problems quickly. :: ## Scroll Behavior Nuxt scrolls to the top on a new route, restores the previous position when the user goes back, and scrolls to hash targets. If you need something different, such as smooth scrolling or a different offset, configure [`scrollBehaviorType`](https://nuxt.com/docs/5.x/guide/recipes/custom-routing#scroll-behavior-for-hash-links) or write your own `scrollBehavior` in [`router.options.ts`](https://nuxt.com/docs/5.x/guide/recipes/custom-routing#router-options). Bear in mind that smooth scrolling should respect the user's `prefers-reduced-motion` setting. ::read-more{title="Custom routing" to="https://nuxt.com/docs/5.x/guide/recipes/custom-routing"} :: ## Useful Resources - [Web Accessibility Initiative (WAI)](https://www.w3.org/WAI/) - [MDN: Accessibility](https://developer.mozilla.org/en-US/docs/Web/Accessibility) - [Understanding WCAG 2.1](https://www.w3.org/WAI/WCAG21/Understanding/) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Dev Containers" description: "Set up or open a Nuxt project in a dev container for a consistent development environment." canonical_url: "https://nuxt.com/docs/5.x/guide/best-practices/devcontainers" --- # Dev Containers > Set up or open a Nuxt project in a dev container for a consistent development environment. ## Setting Up a Dev Container If you're starting a new Nuxt project and want to develop inside a dev container, you can add the configuration yourself. ::read-more{to="https://code.visualstudio.com/docs/devcontainers/containers" target="_blank"} Read more about dev containers :: ### Prerequisites - [Visual Studio Code](https://code.visualstudio.com/) with the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers) - [Docker Desktop](https://www.docker.com/products/docker-desktop/) or [Docker Engine](https://docs.docker.com/engine/) ### Create the Configuration Create a `.devcontainer/` folder in your project root with these two files: ```json [devcontainer.json] { "name": "nuxt-devcontainer", "build": { "dockerfile": "Dockerfile", "context": "../" }, "forwardPorts": [3000], "portsAttributes": { "3000": { "label": "Application", "onAutoForward": "openPreview" } }, "mounts": [ "type=volume,target=${containerWorkspaceFolder}/node_modules" ], "postStartCommand": "pnpm install && pnpm dev:prepare" } ``` ```dockerfile [Dockerfile] FROM node:lts WORKDIR /app RUN npm i -g corepack && corepack enable COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./ RUN pnpm install --frozen-lockfile COPY . . ``` This configuration uses Node.js LTS and enables pnpm via corepack. It forwards port 3000 for the Nuxt dev server and persists `node_modules` in a Docker volume to avoid reinstallation on container restarts. ::tip To use a different package manager, replace `corepack enable` with your preferred manager (for example, `npm install -g yarn`) and update the `postStartCommand` accordingly. :: ## Opening an Existing Dev Container If a project already includes a dev container configuration, you can open it using any of these methods: ### 1. VS Code Prompt When you open the project in VS Code, you should see a notification in the bottom right corner: > "Reopen in Dev Containers" Click this button to build and open the project in a dev container. ### 2. Command Palette If you dismiss the prompt or want to manually trigger it: 1. Open the Command Palette (`Cmd+Shift+P` on Mac, `Ctrl+Shift+P` on Windows/Linux) 2. Search for **"Dev Containers: Reopen in Container"** 3. Select it VS Code will build the container and reopen your project. ### 3. Dev Containers CLI For advanced users or CI workflows, you can use the Dev Containers CLI directly: ```bash # Install the CLI (if not already installed) npm install -g @devcontainers/cli # Build and open the project in a container devcontainer up --workspace-folder . # After making changes to .devcontainer, rebuild devcontainer build ``` ## Next Steps Once the container is running: ```bash pnpm dev ``` Your Nuxt app will be available at . ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt and Hydration" description: "Why fixing hydration issues is important" canonical_url: "https://nuxt.com/docs/5.x/guide/best-practices/hydration" --- # Nuxt and Hydration > Why fixing hydration issues is important When developing, you may face hydration issues. Don't ignore those warnings. ## Why Is It Important to Fix Them? Hydration mismatches are not just warnings - they are indicators of serious problems that can break your application: ### Performance Impact - **Increased time to interactive**: Hydration errors force Vue to re-render the entire component tree, which will increase the time for your Nuxt app to become interactive - **Poor user experience**: Users may see content flashing or unexpected layout shifts ### Functionality Issues - **Broken interactivity**: Event listeners may not attach properly, leaving buttons and forms non-functional - **State inconsistencies**: Application state can become out of sync between what the user sees and what the application thinks is rendered - **SEO problems**: Search engines may index different content than what users actually see ## How to Detect Them ### Development Console Warnings Vue will log hydration mismatch warnings in the browser console during development: ![Screenshot of Vue hydration mismatch warning in the browser console](https://nuxt.com/assets/docs/best-practices/vue-console-hydration.png) ## Common Reasons ### Browser-only APIs in Server Context **Problem**: Using browser-specific APIs during server-side rendering. ```html ``` **Solution**: You can use [`useCookie`](https://nuxt.com/docs/5.x/api/composables/use-cookie): ```html ``` ### Inconsistent Data **Problem**: Different data between server and client. ```html ``` **Solution**: Use SSR-friendly state: ```html ``` ### Conditional Rendering Based on Client State **Problem**: Using client-only conditions during SSR. ```html ``` **Solution**: Use media queries or handle it client-side: ```html ``` ### Third-party Libraries with Side Effects **Problem**: Libraries that modify the DOM or have browser dependencies (this happens a LOT with tag managers). ```html ``` **Solution**: Initialise libraries after hydration has completed: ```html ``` ### Dynamic Content Based on Time **Problem**: Content that changes based on current time. ```html ``` **Solution**: Use [`NuxtTime`](https://nuxt.com/docs/5.x/api/components/nuxt-time) component or handle it client-side: ```html ``` ```html ``` ### Phone Numbers on iOS Safari **Problem**: iOS Safari detects phone numbers (and other data such as dates, addresses and email addresses) in the rendered text and wraps them in links. This changes the DOM before Vue hydrates it, causing a hydration mismatch on iOS only. **Solution**: Disable detection with the `format-detection` meta tag in your `nuxt.config`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ app: { head: { meta: [ { name: 'format-detection', content: 'telephone=no, date=no, email=no, address=no' }, ], }, }, }) ``` To keep a phone number tappable, wrap it in an `` element so the link is part of the server-rendered HTML. ## In Summary 1. **Use SSR-friendly composables**: [`useFetch`](https://nuxt.com/docs/5.x/api/composables/use-fetch), [`useAsyncData`](https://nuxt.com/docs/5.x/api/composables/use-async-data), [`useState`](https://nuxt.com/docs/5.x/api/composables/use-state) 2. **Wrap client-only code**: Use [`ClientOnly`](https://nuxt.com/docs/5.x/api/components/client-only) component for browser-specific content 3. **Consistent data sources**: Ensure server and client uses the same data 4. **Avoid side effects in setup**: Move browser-dependent code to `onMounted` ::tip You can read the [Vue documentation on SSR hydration mismatch](https://vuejs.org/guide/scaling-up/ssr#hydration-mismatch) for a better understanding of hydration. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt performance" description: "Best practices for improving performance of Nuxt apps." canonical_url: "https://nuxt.com/docs/5.x/guide/best-practices/performance" --- # Nuxt performance > Best practices for improving performance of Nuxt apps. Nuxt comes with built-in features designed to improve your application's performance and contribute to better [Core Web Vitals](https://web.dev/articles/vitals). There are also multiple Nuxt core modules that assist in improving performance in specific areas. This guide outlines best practices to optimize performance of your Nuxt application. ## Built-in Features Nuxt offers several built-in features that help you optimize performance of your website. Understanding how these features work is crucial for achieving blazingly-fast performance. ### Links [``](https://nuxt.com/docs/5.x/api/components/nuxt-link) is a drop-in replacement for both Vue Router's `` component and HTML's `` tag. It intelligently determines whether the link is internal or external and renders it accordingly with available optimizations (prefetching, default attributes, etc.) ```html About page ``` Nuxt automatically includes smart prefetching. That means it detects when a link is visible (by default), either in the viewport or when scrolling and prefetches the JavaScript for those pages so that they are ready when the user clicks the link. You can also opt for prefetching on interaction instead: ```ts export default defineNuxtConfig({ experimental: { defaults: { nuxtLink: { prefetchOn: { interaction: true, visibility: false, }, }, }, }, }) ``` ::read-more{title="NuxtLink" to="https://nuxt.com/docs/5.x/api/components/nuxt-link"} :: ### Resource Hints To speed up client-side navigation, Nuxt adds resource hints for the JavaScript and CSS chunks a request resolves. `` (or `modulepreload`) marks the chunks the page itself needs, while `` marks the chunks the entry point and the rendered components lazy-import, so they are warm if the user later navigates to them. On a large app the prefetched set can grow past what a page actually needs. A public page and the signed-in app often share an entry chunk, so a prerendered public page can prefetch chunks that only the signed-in app uses. Prefetch hints have low download priority, but in volume they can still compete with the blocking stylesheet and delay first paint on a slow connection. To stop a chunk from being prefetched, set `prefetch` to `false` on its manifest entry in the [`build:manifest`](https://nuxt.com/docs/5.x/api/advanced/hooks#nuxt-hooks-build-time) hook: ```ts [nuxt.config.ts] export default defineNuxtConfig({ hooks: { 'build:manifest': (manifest) => { for (const chunk of Object.values(manifest)) { if (chunk.src?.startsWith('pages/app/')) { chunk.prefetch = false } } }, }, }) ``` This removes the `rel="prefetch"` hint for every chunk whose source lives under `pages/app/`, while leaving the chunks in the bundle and the `preload` and `modulepreload` hints intact. Adjust the `startsWith` prefix to the directory whose chunks your public pages should not prefetch. You can also preload specific components or routes from your own code with [`preloadComponents`](https://nuxt.com/docs/5.x/api/utils/preload-components), [`preloadRouteComponents`](https://nuxt.com/docs/5.x/api/utils/preload-route-components) and [`prefetchComponents`](https://nuxt.com/docs/5.x/api/utils/prefetch-components). ### Hybrid Rendering In more complex applications, we may need a full control over how our application is rendered to support cases where some pages could be generated at build time, while others should be client-side rendered Hybrid rendering allows different caching rules per route using Route Rules and decides how the server should respond to a new request on a given URL: ```ts export default defineNuxtConfig({ routeRules: { '/': { prerender: true, }, '/products/**': { swr: 3600, }, '/blog': { isr: 3600, }, '/admin/**': { ssr: false, }, }, }) ``` Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using Nitro caching layer. ::read-more{title="Hybrid rendering" to="https://nuxt.com/docs/5.x/guide/concepts/rendering#hybrid-rendering"} :: ### Lazy Loading Components To dynamically import a component (also known as lazy-loading a component) all you need to do is add the Lazy prefix to the component's name. This is useful if the component is not always needed. ```html ``` By using the Lazy prefix you can delay loading the component code until the right moment, which can be helpful for optimizing your JavaScript bundle size. ::read-more{title="Lazy loading components" to="https://nuxt.com/docs/5.x/directory-structure/app/components#dynamic-imports"} :: ### Lazy Hydration It is not always necessary to hydrate (or make interactive) all the components of your site on the initial load. Using lazy hydration, you can control when components can have their code loaded, which can improve the time-to-interactive metric for your app. Nuxt allows you to control when components become interactive with lazy hydration (added in Nuxt v3.16). ```html ``` To optimize your app, you may want to delay the hydration of some components until they're visible, or until the browser is done with more important tasks. ::read-more{title="Lazy hydration" to="https://nuxt.com/docs/5.x/directory-structure/app/components#delayed-or-lazy-hydration"} :: For content and marketing sites that need little or no client-side interactivity, you can go further and combine prerendering, the `noScripts` route rule, [server components](https://nuxt.com/docs/5.x/guide/concepts/server-components) and lazy hydration to ship near-zero JavaScript. ::read-more{title="Mostly-static sites" to="https://nuxt.com/docs/5.x/guide/recipes/mostly-static-sites"} :: ### Fetching data To avoid fetching same data twice (once on the server and once on client) Nuxt provides [`useFetch`](https://nuxt.com/docs/5.x/api/composables/use-fetch) and [`useAsyncData`](https://nuxt.com/docs/5.x/api/composables/use-async-data). They ensure that if an API call is made on the server, the data is forwarded to the client in the payload instead of being fetched again. ::read-more{title="Data fetching" to="https://nuxt.com/docs/5.x/getting-started/data-fetching"} :: ## Core Nuxt Modules Apart from Nuxt's built-in features, there are also core modules maintained by the Nuxt team which help improve performance even further. These modules help handle assets such as images, custom fonts, or third party scripts. ### Images Unoptimized images can have a significant negative impact on your website performance, specifically the [Largest Contentful Paint (LCP)](https://web.dev/articles/lcp) score. In Nuxt we can use [Nuxt Image](https://image.nuxt.com/) module that is a plug-and-play image optimization for Nuxt apps. It allows resizing and transforming your images using built-in optimizer or your favorite images CDN. ::video-accordion{title="Watch the video by LearnVue about Nuxt Image" videoId="_UBff2eqGY0"} :: [``](https://nuxt.com/docs/5.x/api/components/nuxt-img) is a drop-in replacement for the native `` tag that comes with following enhancements: - Uses built-in provider to optimize local and remote images - Converts `src` to provider optimized URLs with modern formats such as WebP or Avif - Automatically resizes images based on `width` and `height` - Generates responsive `sizes` when providing sizes option - Supports native `lazy loading` as well as other `` attributes Images in your website can usually be separated by importance; the ones that are needed to be delivered first at initial load (i.e. `Largest Contentful Paint`), and the ones that can be loaded later or when specifically needed. For that, we could use the following optimizations: ```html ``` ::read-more{title="Nuxt Image" to="https://image.nuxt.com/usage/nuxt-img"} :: ### Fonts [Nuxt Fonts](https://fonts.nuxt.com/) will automatically optimize your fonts (including custom fonts) and remove external network requests for improved privacy and performance. It includes built-in automatic self-hosting for any font file which means you can optimally load web fonts with reduced layout shift, thanks to the underlying package [fontaine](https://github.com/unjs/fontaine). ::video-accordion{title="Watch the talk by Daniel Roe about the idea behind Nuxt Fonts" videoId="D3F683UViBY"} :: Nuxt Fonts processes all your CSS and does the following things automatically when it encounters a font-family declaration. 1. **Resolves fonts** – Looks for font files in public/, then checks web providers like Google, Bunny, and Fontshare. 2. **Generates @font-face rules** – Injects CSS rules to load fonts from the correct sources. 3. **Proxies & caches fonts** – Rewrites URLs to `/_fonts`, downloads and caches fonts locally. 4. **Creates fallback metrics** – Adjusts local system fonts to match web fonts, reducing layout shift ([CLS](https://web.dev/articles/cls)). 5. **Includes fonts in build** – Bundles fonts with your project, hashing file names and setting long-lived cache headers. It supports multiple providers that are designed to be pluggable and extensible, so no matter your setup you should be able to use an existing provider or write your own. ### Scripts Third-party resources like analytics tools, video embeds, maps, and social media integrations enhance website functionality but can significantly degrade user experience and negatively impact [Interaction to Next Paint (INP)](https://web.dev/articles/inp) and Largest Contentful Paint (LCP) scores. [Nuxt Scripts](https://scripts.nuxt.com/) lets you load third-party scripts with better performance, privacy, security and DX. ::video-accordion{title="Watch the video by Alex Lichter about Nuxt Scripts" videoId="sjMqUUvH9AE"} :: Nuxt Scripts provides an abstraction layer on top of third-party scripts, providing SSR support and type-safety and while still giving you full low-level control over how a script is loaded. ```ts const { onLoaded, proxy } = useScriptGoogleAnalytics( { id: 'G-1234567', scriptOptions: { trigger: 'manual', }, }, ) // queue events to be sent when ga loads proxy.gtag('config', 'UA-123456789-1') // or wait until ga is loaded onLoaded((gtag) => { // script loaded }) ``` ::read-more{title="Nuxt Scripts" to="https://scripts.nuxt.com/scripts"} :: ## Profiling Tools To improve performance, we need to first know how to measure it, starting with measuring performance during development - on local environment, and then moving to auditing application that are deployed on production. ### Nuxi Analyze [This](https://nuxt.com/docs/5.x/api/commands/analyze) command of `nuxt` allows you to analyze the production bundle of your Nuxt application. It leverages `vite-bundle-visualizer` (similar to `webpack-bundle-analyzer`) to generate a visual representation of your application's bundle, making it easier to identify which components take up the most space. When you see a large block in the visualization, it often signals an opportunity for optimization—whether by splitting it into smaller parts, implementing lazy loading, or replacing it with a more efficient alternative, especially for third-party libraries. Large blocks containing multiple elements can often be reduced by importing only the necessary components rather than entire modules while large standalone blocks may be better suited for lazy loading rather than being included in the main bundle. ### Nuxt DevTools The [Nuxt DevTools](https://nuxt.com/modules/devtools) gives you insights and transparency about your Nuxt App to identify performance gaps and seamlessly manage your app configurations. ![Nuxt DevTools example](https://user-images.githubusercontent.com/11247099/217670806-fb39aeff-3881-44e5-b9c8-6c757f5925fc.png) It comes with several features we can use to measure performance of Nuxt apps: 1. **Timeline** – Tracks time spent on rendering, updating, and initializing components to identify performance bottlenecks. 2. **Assets** – Displays file sizes (e.g., images) without transformations. 3. **Render Tree** – Shows connections between Vue components, scripts, and styles to optimize dynamic loading. 4. **Inspect** – Lists all files used in the Vue app with their size and evaluation time. ### Chrome DevTools Chrome DevTools come with two useful tabs for measuring performance; `Performance` and `Lighthouse`. When you open the [Performance](https://developer.chrome.com/docs/devtools/performance/overview) panel, it instantly shows your local **Largest Contentful Paint (LCP)** and **Cumulative Layout Shift (CLS)** scores (good, needs improvement, or bad). If you interact with the page, it also captures **Interaction to Next Paint (INP)**, giving you a full view of your Core Web Vitals based on your device and network. ![Chrome DevTools Performance Panel](https://developer.chrome.com/static/docs/devtools/performance/image/cpu-throttling_856.png) [Lighthouse](https://developer.chrome.com/docs/devtools/lighthouse) audits performance, accessibility, SEO, progressive web apps, and best practices. It runs tests on your page and generates a report. Use failing audits as a guide to improve your site. ![Lighthouse](https://developer.chrome.com/static/docs/lighthouse/images/lighthouse-overview_720.png) Each audit has a reference document explaining why the audit is important, as well as how to fix it. ### PageSpeed Insights [PageSpeed Insights (PSI)](https://developers.google.com/speed/docs/insights/v5/about) reports on the user experience of a page on both mobile and desktop devices, and provides suggestions on how that page may be improved. It provides both lab and field data about a page. Lab data is useful for debugging issues, as it is collected in a controlled environment while field data is useful for capturing true, real-world user experience. ### Web Page Test [WebPageTest](https://www.webpagetest.org/) is a web performance tool providing deep diagnostic information about how a page performs under a variety of conditions. Each test can be run from different locations around the world, on real browsers, over any number of customizable network conditions. ## Common Problems When building more complex Nuxt applications, you will probably encounter some of the problems listed below. Understanding these problems and fixing them will help you improve performance of your website. ### Overusing plugins **Problem**: A large number of plugins can cause performance issues, especially if they require expensive computations or take too long to initialize. Since plugins run during the hydration phase, inefficient setups can block rendering and degrade the user experience. **Solution**: Inspect your plugins and see if some of them could be implemented rather as a composable or utility function instead. ### Unused code / dependencies **Problem**: With the development of the project, there can be a case where there will be some unused code or a dependency. This additional functionality may not be used or needed while it will be increase the bundle size of our project. **Solution**: Inspect your `package.json` for unused dependencies and analyze your code for unused utils/composables/functions. ### Not using Vue Performance tips **Problem**: [Vue documentation](https://vuejs.org/guide/best-practices/performance) lists several Performance improvements we can use in our Nuxt projects as well but as they are part of Vue documentation, developers tend to forget about it and focus on Nuxt specific improvements only - while Nuxt application is still a Vue project. **Solution**: Use concepts such as `shallowRef`, `v-memo`, `v-once`, etc to improve performance. ### Not following patterns **Problem**: The more people are currently working on the project, the more difficult it will be to maintain the stable codebase. Developers have a tendency to introduce new concepts they've seen in another project which can cause conflicts and problems with performance. **Solution**: Establish rules and patterns in the project such as [Good practices and Design Patterns for Vue Composables](https://dev.to/jacobandrewsky/good-practices-and-design-patterns-for-vue-composables-24lk) ### Trying to load everything at the same time **Problem**: When a page is loaded and it is not correctly instructed about the order of loading elements it will result in fetching everything at the same time - which can be slow and result in bad User Experience. **Solution**: Use concepts such as Progressive Enhancement where core webpage content is set first, then more nuanced and technically rigorous layers of presentation and features are added on top as the browser/internet connection allow. ## Useful Resources To learn more about various techniques for improving performance, take a look at the following resources: 1. [Apply instant loading with the PRPL pattern](https://web.dev/articles/apply-instant-loading-with-prpl) 2. [Perceived performance](https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Performance/Perceived_performance) 3. [Understanding Critical Rendering Path](https://developer.mozilla.org/en-US/docs/Web/Performance/Guides/Critical_rendering_path) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt Plugins" description: "Best practices when using Nuxt plugins." canonical_url: "https://nuxt.com/docs/5.x/guide/best-practices/plugins" --- # Nuxt Plugins > Best practices when using Nuxt plugins. Plugins in Nuxt allow you to extend your application with additional functionality. However, improper use can lead to performance bottlenecks. This guide outlines best practices to optimize your Nuxt plugins. ## Avoid Costly Plugin Setup A large number of plugins can cause performance issues, especially if they require expensive computations or take too long to initialize. Since plugins run during the hydration phase, inefficient setups can block rendering and degrade the user experience. ## Use Composition Whenever Possible Whenever possible, favor composition over plugins. Just like in Vue, many utilities and composables can be used directly without the need for a plugin. This keeps your project lightweight and improves maintainability. ## If `async`, Enable `parallel` By default, all plugins loads synchronously. When defining asynchronous plugins, setting `parallel: true` allows multiple plugins to load concurrently, improving performance by preventing blocking operations. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Auto-imports" description: "Nuxt auto-imports components, composables, helper functions and Vue APIs." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/auto-imports" --- # Auto-imports > Nuxt auto-imports components, composables, helper functions and Vue APIs. Nuxt auto-imports components, composables and [Vue.js APIs](https://vuejs.org/api/) to use across your application without explicitly importing them. ```vue twoslash [app/app.vue] ``` Thanks to its opinionated directory structure, Nuxt can auto-import your [`app/components/`](https://nuxt.com/docs/5.x/directory-structure/app/components), [`app/composables/`](https://nuxt.com/docs/5.x/directory-structure/app/composables) and [`app/utils/`](https://nuxt.com/docs/5.x/directory-structure/app/utils). Contrary to a classic global declaration, Nuxt preserves typings, IDEs completions and hints, and **only includes what is used in your production code**. ::note In the docs, every function that is not explicitly imported is auto-imported by Nuxt and can be used as-is in your code. You can find a reference for auto-imported components, composables and utilities in the [API section](https://nuxt.com/docs/5.x/api). :: ::note In the [`server`](https://nuxt.com/docs/5.x/directory-structure/server) directory, Nuxt auto-imports exported functions and variables from `server/utils/`. :: ::note You can also auto-import functions exported from custom folders or third-party packages by configuring the [`imports`](https://nuxt.com/docs/5.x/api/nuxt-config#imports) section of your `nuxt.config` file. :: ## Built-in Auto-imports Nuxt auto-imports functions and composables to perform [data fetching](https://nuxt.com/docs/5.x/getting-started/data-fetching), get access to the [app context](https://nuxt.com/docs/5.x/api/composables/use-nuxt-app) and [runtime config](https://nuxt.com/docs/5.x/guide/going-further/runtime-config), manage [state](https://nuxt.com/docs/5.x/getting-started/state-management) or define components and plugins. ```vue twoslash ``` Vue exposes Reactivity APIs like `ref` or `computed`, as well as lifecycle hooks and helpers that are auto-imported by Nuxt. ```vue twoslash ``` ### Vue and Nuxt Composables When you are using the built-in Composition API composables provided by Vue and Nuxt, be aware that many of them rely on being called in the right *context*. During a component lifecycle, Vue tracks the temporary instance of the current component (and similarly, Nuxt tracks a temporary instance of `nuxtApp`) via a global variable, and then unsets it in the same tick. This is essential when server rendering, both to avoid cross-request state pollution (leaking a shared reference between two users) and to avoid leakage between different components. That means that (with very few exceptions) you cannot use them outside a Nuxt plugin, Nuxt route middleware or Vue setup function. On top of that, you must use them synchronously - that is, you cannot use `await` before calling a composable, except within ` ``` ### Disabling Auto-imports If you want to disable auto-importing composables and utilities, you can set `imports.autoImport` to `false` in the `nuxt.config` file. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ imports: { autoImport: false, }, }) ``` This will disable auto-imports completely but it's still possible to use [explicit imports](https://nuxt.com/docs/5.x/guide/concepts/auto-imports#explicit-imports) from `#imports`. ### Partially Disabling Auto-imports If you want framework-specific functions like `ref` to remain auto-imported but wish to disable auto-imports for your own code (e.g., custom composables), you can set the `imports.scan` option to `false` in your `nuxt.config.ts` file: ```ts export default defineNuxtConfig({ imports: { scan: false, }, }) ``` With this configuration: - Framework functions like `ref`, `computed`, or `watch` will still work without needing manual imports. - Custom code, such as composables, will need to be manually imported in your files. ::warning **Caution:** This setup has certain limitations: - If you structure your project with layers, you will need to explicitly import the composables from each layer, rather than relying on auto-imports. - This breaks the layer system’s override feature. If you use `imports.scan: false`, ensure you understand this side-effect and adjust your architecture accordingly. :: ## Auto-imported Components Nuxt also automatically imports components from your `~/components` directory, although this is configured separately from auto-importing composables and utility functions. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/components"} :: To disable auto-importing components from your own `~/components` directory, you can set `components.dirs` to an empty array (though note that this will not affect components added by modules). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ components: { dirs: [], }, }) ``` ## Auto-Import from Third-Party Packages Nuxt also allows auto-importing from third-party packages. ::tip If you are using the Nuxt module for that package, it is likely that the module has already configured auto-imports for that package. :: For example, you could enable the auto-import of the `useI18n` composable from the `vue-i18n` package like this: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ imports: { presets: [ { from: 'vue-i18n', imports: ['useI18n'], }, ], }, }) ``` ::video-accordion{title="Watch a video from Alexander Lichter on how to easily set up custom auto imports" videoId="FT2LQJ2NvVI"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Code Style" description: "Nuxt supports ESLint out of the box" canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/code-style" --- # Code Style > Nuxt supports ESLint out of the box ## ESLint The recommended approach for Nuxt is to enable ESLint support using the [`@nuxt/eslint`](https://eslint.nuxt.com/packages/module) module, that will setup project-aware ESLint configuration for you. ::callout{icon="i-lucide-lightbulb"} The module is designed for the [new ESLint flat config format](https://eslint.org/docs/latest/use/configure/configuration-files) which is the [default format since ESLint v9](https://eslint.org/blog/2024/04/eslint-v9.0.0-released/). If you are using the legacy `.eslintrc` config, you will need to [configure manually with `@nuxt/eslint-config`](https://eslint.nuxt.com/packages/config#customizing-the-config). We highly recommend you to migrate over the flat config to be future-proof. :: ## Quick Setup ```bash npx nuxt module add eslint ``` Start your Nuxt app, a `eslint.config.mjs` file will be generated under your project root. You can customize it as needed. You can learn more about the module and customizations in [Nuxt ESLint's documentation](https://eslint.nuxt.com/packages/module). ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "ES Modules" description: "Nuxt uses native ES modules." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/esm" --- # ES Modules > Nuxt uses native ES modules. This guide helps explain what ES Modules are and how to make a Nuxt app (or upstream library) compatible with ESM. ## Background ### CommonJS Modules CommonJS (CJS) is a format introduced by Node.js that allows sharing functionality between isolated JavaScript modules ([read more](https://nodejs.org/api/modules.html)). You might be already familiar with this syntax: ```js const a = require('./a') module.exports.a = a ``` Bundlers like webpack and Rollup support this syntax and allow you to use modules written in CommonJS in the browser. ### ESM Syntax Most of the time, when people talk about ESM vs. CJS, they are talking about a different syntax for writing [modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules). ```js import a from './a' export { a } ``` Before ECMAScript Modules (ESM) became a standard (it took more than 10 years!), tooling like [webpack](https://webpack.js.org/guides/ecma-script-modules/) and even languages like TypeScript started supporting so-called **ESM syntax**. However, there are some key differences with actual spec; here's [a helpful explainer](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/). ### What is 'Native' ESM? You may have been writing your app using ESM syntax for a long time. After all, it's natively supported by the browser, and in Nuxt 2 we compiled all the code you wrote to the appropriate format (CJS for server, ESM for browser). When adding modules to your package, things were a little different. A sample library might expose both CJS and ESM versions, and let us pick which one we wanted: ```json { "name": "sample-library", "main": "dist/sample-library.cjs.js", "module": "dist/sample-library.esm.js" } ``` So in Nuxt 2, the bundler (webpack) would pull in the CJS file ('main') for the server build and use the ESM file ('module') for the client build. ::note The `module` field is a convention used by bundlers like webpack and Rollup, but is not recognized by Node.js itself. Node.js only uses the [`exports`](https://nodejs.org/api/packages.html#exports) and [`main`](https://nodejs.org/api/packages.html#main) fields for module resolution. :: However, in recent Node.js LTS releases, it is now possible to [use native ESM module](https://nodejs.org/api/esm.html) within Node.js. That means that Node.js itself can process JavaScript using ESM syntax, although it doesn't do it by default. The two most common ways to enable ESM syntax are: - set `"type": "module"` within your `package.json` and keep using `.js` extension - use the `.mjs` file extensions (recommended) This is what we do for Nuxt Nitro; we output a `.output/server/index.mjs` file. That tells Node.js to treat this file as a native ES module. ### What Are Valid Imports in a Node.js Context? When you `import` a module rather than `require` it, Node.js resolves it differently. For example, when you import `sample-library`, Node.js will look for the `exports` entry in that library's `package.json`, or fall back to the `main` entry if `exports` is not defined. This is also true of dynamic imports, like `const b = await import('sample-library')`. Node supports the following kinds of imports (see [docs](https://nodejs.org/api/packages.html#determining-module-system)): 1. files ending in `.mjs` - these are expected to use ESM syntax 2. files ending in `.cjs` - these are expected to use CJS syntax 3. files ending in `.js` - these are expected to use CJS syntax unless their `package.json` has `"type": "module"` ### What Kinds of Problems Can There Be? For a long time module authors have been producing ESM-syntax builds but using conventions like `.esm.js` or `.es.js`, which they have added to the `module` field in their `package.json`. This hasn't been a problem until now because they have only been used by bundlers like webpack, which don't especially care about the file extension. However, if you try to import a package with an `.esm.js` file in a Node.js ESM context, it won't work, and you'll get an error like: ```bash [Terminal] (node:22145) Warning: To load an ES module, set "type": "module" in the package.json or use the .mjs extension. /path/to/index.js:1 export default {} ^^^^^^ SyntaxError: Unexpected token 'export' at wrapSafe (internal/modules/cjs/loader.js:1001:16) at Module._compile (internal/modules/cjs/loader.js:1049:27) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10) .... at async Object.loadESM (internal/process/esm_loader.js:68:5) ``` You might also get this error if you have a named import from an ESM-syntax build that Node.js thinks is CJS: ```bash [Terminal] file:///path/to/index.mjs:5 import { named } from 'sample-library' ^^^^^ SyntaxError: Named export 'named' not found. The requested module 'sample-library' is a CommonJS module, which may not support all module.exports as named exports. CommonJS modules can always be imported via the default export, for example using: import pkg from 'sample-library'; const { named } = pkg; at ModuleJob._instantiate (internal/modules/esm/module_job.js:120:21) at async ModuleJob.run (internal/modules/esm/module_job.js:165:5) at async Loader.import (internal/modules/esm/loader.js:177:24) at async Object.loadESM (internal/process/esm_loader.js:68:5) ``` ## Troubleshooting ESM Issues If you encounter these errors, the issue is almost certainly with the upstream library. They need to [fix their library](https://nuxt.com/docs/5.x/guide/concepts/esm#library-author-guide) to support being imported by Node. ### Transpiling Libraries In the meantime, you can tell Nuxt not to try to import these libraries by adding them to `build.transpile`: ```ts twoslash export default defineNuxtConfig({ build: { transpile: ['sample-library'], }, }) ``` You may find that you *also* need to add other packages that are being imported by these libraries. ### Aliasing Libraries In some cases, you may also need to manually alias the library to the CJS version, for example: ```ts twoslash export default defineNuxtConfig({ alias: { 'sample-library': 'sample-library/dist/sample-library.cjs.js', }, }) ``` ### Default Exports A dependency with CommonJS format, can use `module.exports` or `exports` to provide a default export: ```js [node_modules/cjs-pkg/index.js] module.exports = { test: 123 } // or exports.test = 123 ``` This normally works well if we `require` such dependency: ```js [test.cjs] const pkg = require('cjs-pkg') console.log(pkg) // { test: 123 } ``` [Node.js in native ESM mode](https://nodejs.org/api/esm.html#interoperability-with-commonjs), [typescript with `esModuleInterop` enabled](https://www.typescriptlang.org/tsconfig/#esModuleInterop) and bundlers such as webpack, provide a compatibility mechanism so that we can default import such library. This mechanism is often referred to as "interop require default": ```js import pkg from 'cjs-pkg' console.log(pkg) // { test: 123 } ``` However, because of the complexities of syntax detection and different bundle formats, there is always a chance that the interop default fails and we end up with something like this: ```js import pkg from 'cjs-pkg' console.log(pkg) // { default: { test: 123 } } ``` Also when using dynamic import syntax (in both CJS and ESM files), we always have this situation: ```js import('cjs-pkg').then(console.log) // [Module: null prototype] { default: { test: '123' } } ``` In this case, we need to manually interop the default export: ```js // Static import import { default as pkg } from 'cjs-pkg' // Dynamic import import('cjs-pkg').then(m => m.default || m).then(console.log) ``` For handling more complex situations and more safety, we recommend [mlly](https://github.com/unjs/mlly), which can preserve named exports. ```js import { interopDefault } from 'mlly' // Assuming the shape is { default: { foo: 'bar' }, baz: 'qux' } import myModule from 'my-module' console.log(interopDefault(myModule)) // { foo: 'bar', baz: 'qux' } ``` ## Library Author Guide The good news is that it's relatively simple to fix issues of ESM compatibility. There are two main options: 1. **You can rename your ESM files to end with `.mjs`.** *This is the recommended and simplest approach.* You may have to sort out issues with your library's dependencies and possibly with your build system, but in most cases, this should fix the problem for you. It's also recommended to rename your CJS files to end with `.cjs`, for the greatest explicitness. 2. **You can opt to make your entire library ESM-only**. This would mean setting `"type": "module"` in your `package.json` and ensuring that your built library uses ESM syntax. However, you may face issues with your dependencies - and this approach means your library can *only* be consumed in an ESM context. ### Migration The initial step from CJS to ESM is updating any usage of `require` to use `import` instead: ::code-group ```ts [Before] module.exports = function () { /* ... */ } exports.hello = 'world' ``` ```ts [After] export default function () { /* ... */ } export const hello = 'world' ``` :: ::code-group ```js [Before] const myLib = require('my-lib') ``` ```js [After] import myLib from 'my-lib' // or const dynamicMyLib = await import('my-lib').then(lib => lib.default || lib) ``` :: In ESM Modules, unlike CJS, `require`, `require.resolve`, `__filename` and `__dirname` globals are not available and should be replaced with `import()` and `import.meta.filename`. ::code-group ```js [Before] const { join } = require('node:path') const newDir = join(__dirname, 'new-dir') ``` ```js [After] import { fileURLToPath } from 'node:url' const newDir = fileURLToPath(new URL('./new-dir', import.meta.url)) ``` :: ::code-group ```js [Before] const someFile = require.resolve('./lib/foo.js') ``` ```js [After] import { resolveModulePath } from 'exsolve' const someFile = resolveModulePath('my-lib', { from: import.meta.url }) ``` :: ### Best Practices - Prefer named exports rather than default export. This helps reduce CJS conflicts. (see [Default exports](https://nuxt.com/docs/5.x/guide/concepts/esm#default-exports) section) - Avoid depending on Node.js built-ins and CommonJS or Node.js-only dependencies as much as possible to make your library usable in Browsers and Edge Workers without needing Nitro polyfills. - Use new `exports` field with conditional exports. ([read more](https://nodejs.org/api/packages.html#conditional-exports)). ```json { "exports": { ".": { "import": "./dist/mymodule.mjs" } } } ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Modules" description: "Nuxt provides a module system to extend the framework core and simplify integrations." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/modules" --- # Modules > Nuxt provides a module system to extend the framework core and simplify integrations. ## Exploring Nuxt Modules When developing production-grade applications with Nuxt you might find that the framework's core functionality is not enough. Nuxt can be extended with configuration options and plugins, but maintaining these customizations across multiple projects can be tedious, repetitive and time-consuming. On the other hand, supporting every project's needs out of the box would make Nuxt very complex and hard to use. This is one of the reasons why Nuxt provides a module system that makes it possible to extend the core. Nuxt modules are async functions that sequentially run when starting Nuxt in development mode using [`nuxt dev`](https://nuxt.com/docs/5.x/api/commands/dev) or building a project for production with [`nuxt build`](https://nuxt.com/docs/5.x/api/commands/build). They can override templates, configure webpack loaders, add CSS libraries, and perform many other useful tasks. Best of all, Nuxt modules can be distributed in npm packages. This makes it possible for them to be reused across projects and shared with the community, helping create an ecosystem of high-quality add-ons. ::read-more{to="https://nuxt.com/modules"} Explore Nuxt Modules :: ## Add Nuxt Modules Once you have installed the modules you can add them to your [`nuxt.config.ts`](https://nuxt.com/docs/5.x/directory-structure/nuxt-config) file under the `modules` property. Module developers usually provide additional steps and details for usage. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ modules: [ // Using package name (recommended usage) '@nuxtjs/example', // Load a local module './modules/example', // Add module with inline-options ['./modules/example', { token: '123' }], // Inline module definition async (inlineOptions, nuxt) => { }, ], }) ``` ::warning Nuxt modules are now build-time-only, and the `buildModules` property used in Nuxt 2 is deprecated in favor of `modules`. :: ## Disabling Modules :badge[v4.3]{color="info" size="xs" .align-middle} You can disable a module by setting its config key to `false` in your Nuxt config. This is particularly useful when you want to disable modules inherited from layers. ```ts [nuxt.config.ts] export default defineNuxtConfig({ // Disable `@nuxt/image` module image: false, }) ``` ::read-more{to="https://nuxt.com/docs/5.x/guide/going-further/layers#disabling-modules-from-layers"} :: ## Create a Nuxt Module Everyone has the opportunity to develop modules and we cannot wait to see what you will build. ::read-more{to="https://nuxt.com/docs/5.x/guide/modules" title="Module Author Guide"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt Lifecycle" description: "Understanding the lifecycle of Nuxt applications can help you gain deeper insights into how the framework operates, especially for both server-side and client-side rendering." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/nuxt-lifecycle" --- # Nuxt Lifecycle > Understanding the lifecycle of Nuxt applications can help you gain deeper insights into how the framework operates, especially for both server-side and client-side rendering. The goal of this chapter is to provide a high-level overview of the different parts of the framework, their execution order, and how they work together. ## Server Lifecycle On the server, the following steps are executed for every initial request to your application: ::steps ### Server plugins :badge[once]{color="info" .align-middle} Nuxt is powered by [Nitro](https://nitro.build/), a modern server engine. When Nitro starts, it initializes and executes the plugins under the [`/server/plugins`](https://nuxt.com/docs/5.x/directory-structure/server#server-plugins) directory. These plugins can: - Capture and handle application-wide errors. - Register hooks that execute when Nitro shuts down. - Register hooks for request lifecycle events, such as modifying responses. :::callout{icon="i-lucide-lightbulb"} Nitro plugins are executed only once when the server starts. In a serverless environment, the server boots on each incoming request, and so do the Nitro plugins. However, they are not awaited. ::: :::read-more{to="https://nuxt.com/docs/5.x/directory-structure/server#server-plugins"} ::: ### Server middleware After initializing the Nitro server, middleware under `server/middleware/` is executed for every request. Middleware can be used for tasks such as authentication, logging, or request transformation. :::warning Returning a value from middleware will terminate the request and send the returned value as the response. This behavior should generally be avoided to ensure proper request handling! ::: :::read-more{to="https://nuxt.com/docs/5.x/directory-structure/server#server-middleware"} ::: ### App plugins The Vue and Nuxt instances are created first. Afterward, Nuxt executes its app plugins. This includes: - Built-in plugins, such as Vue Router and `unhead`. - Custom plugins located in the `app/plugins/` directory, including those without a suffix (e.g., `myPlugin.ts`) and those with the `.server` suffix (e.g., `myServerPlugin.server.ts`). Plugins execute in a specific order and may have dependencies on one another. For more details, including execution order and parallelism, refer to the [Plugins documentation](https://nuxt.com/docs/5.x/directory-structure/app/plugins). :::callout{icon="i-lucide-lightbulb"} After this step, Nuxt calls the [`app:created`](https://nuxt.com/docs/5.x/api/advanced/hooks#app-hooks-runtime) hook, which can be used to execute additional logic. ::: :::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/plugins"} ::: ### Route validation After initializing plugins and before executing middleware, Nuxt calls the `validate` method if it is defined in the `definePageMeta` function. The `validate` method, which can be synchronous or asynchronous, is often used to validate dynamic route parameters. - The `validate` function should return `true` if the parameters are valid. - If validation fails, it should return `false` or an object containing a `status` and/or `statusText` to terminate the request. For more information, see the [Route Validation documentation](https://nuxt.com/docs/5.x/getting-started/routing#route-validation). :::read-more{to="https://nuxt.com/docs/5.x/getting-started/routing#route-validation"} ::: ### App middleware Middleware allows you to run code before navigating to a particular route. It is often used for tasks such as authentication, redirection, or logging. In Nuxt, there are three types of middleware: - **Global route middleware** - **Named route middleware** - **Anonymous (or inline) route middleware** Nuxt executes all global middleware on the initial page load (both on server and client) and then again before any client-side navigation. Named and anonymous middleware are executed only on the routes specified in the middleware property of the page(route) meta defined in the corresponding page components. For details about each type and examples, see the [Middleware documentation](https://nuxt.com/docs/5.x/directory-structure/app/middleware). Any redirection on the server will result in a `Location:` header being sent to the browser; the browser then makes a fresh request to this new location. All application state will be reset when this happens, unless persisted in a cookie. :::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/middleware"} ::: ### Page and components Nuxt renders the page and its components and fetches any required data with `useFetch` and `useAsyncData` during this step. Since there are no dynamic updates and no DOM operations occur on the server, Vue lifecycle hooks such as `onBeforeMount`, `onMounted`, and subsequent hooks are **NOT** executed during SSR. By default, Vue pauses dependency tracking during SSR for better performance. :::callout{icon="i-lucide-lightbulb"} There is no reactivity on the server side because Vue SSR renders the app top-down as static HTML, making it impossible to go back and modify content that has already been rendered. ::: :::important You should avoid code that produces side effects that need cleanup in root scope of ` ``` On the initial request, the `counter` ref is initialized in the server since it is rendered inside the `

` tag. The contents of `handleClick` is never executed here. During hydration in the browser, the `counter` ref is re-initialized. The `handleClick` finally binds itself to the button; Therefore it is reasonable to deduce that the body of `handleClick` will always run in a browser environment. [Middlewares](https://nuxt.com/docs/5.x/directory-structure/app/middleware) and [pages](https://nuxt.com/docs/5.x/directory-structure/app/pages) run in the server and on the client during hydration. [Plugins](https://nuxt.com/docs/5.x/directory-structure/app/plugins) can be rendered on the server or client or both. [Components](https://nuxt.com/docs/5.x/directory-structure/app/components) can be forced to run on the client only as well. [Composables](https://nuxt.com/docs/5.x/directory-structure/app/composables) and [utilities](https://nuxt.com/docs/5.x/directory-structure/app/utils) are rendered based on the context of their usage. **Benefits of server-side rendering:** - **Performance**: Users can get immediate access to the page's content because browsers can display static content much faster than JavaScript-generated content. At the same time, Nuxt preserves the interactivity of a web application during the hydration process. - **Search Engine Optimization**: Universal rendering delivers the entire HTML content of the page to the browser as a classic server application. Web crawlers can directly index the page's content, which makes Universal rendering a great choice for any content that you want to index quickly. **Downsides of server-side rendering:** - **Development constraints:** Server and browser environments don't provide the same APIs, and it can be tricky to write code that can run on both sides seamlessly. Fortunately, Nuxt provides guidelines and specific variables to help you determine where a piece of code is executed. - **Cost:** A server needs to be running in order to render pages on the fly. This adds a monthly cost like any traditional server. However, the server calls are highly reduced thanks to universal rendering with the browser taking over on client-side navigation. A cost reduction is possible by leveraging [edge-side-rendering](https://nuxt.com/docs/5.x/guide/concepts/rendering#edge-side-rendering). Universal rendering is very versatile and can fit almost any use case, and is especially appropriate for any content-oriented websites: **blogs, marketing websites, portfolios, e-commerce sites, and marketplaces.** ::tip For more examples about writing Vue code without hydration mismatch, see [the Vue docs](https://vuejs.org/guide/scaling-up/ssr#hydration-mismatch). :: ::important When importing a library that relies on browser APIs and has side effects, make sure the component importing it is only called client-side. Bundlers do not treeshake imports of modules containing side effects. :: ## Client-Side Rendering Out of the box, a traditional Vue.js application is rendered in the browser (or **client**). Then, Vue.js generates HTML elements after the browser downloads and parses all the JavaScript code containing the instructions to create the current interface. ![Users have to wait for the browser to download, parse and execute the JavaScript before seeing the page's content](https://nuxt.com/assets/docs/concepts/rendering/csr.svg) **Benefits of client-side rendering:** - **Development speed**: When working entirely on the client-side, we don't have to worry about the server compatibility of the code, for example, by using browser-only APIs like the `window` object. - **Cheaper:** Running a server adds a cost of infrastructure as you would need to run on a platform that supports JavaScript. We can host client-only applications on any static server with HTML, CSS, and JavaScript files. - **Offline:** Because code entirely runs in the browser, it can nicely keep working while the internet is unavailable. **Downsides of client-side rendering:** - **Performance**: The user has to wait for the browser to download, parse and run JavaScript files. Depending on the network for the download part and the user's device for the parsing and execution, this can take some time and impact the user's experience. - **Search Engine Optimization**: Indexing and updating the content delivered via client-side rendering takes more time than with a server-rendered HTML document. This is related to the performance drawback we discussed, as search engine crawlers won't wait for the interface to be fully rendered on their first try to index the page. Your content will take more time to show and update in search results pages with pure client-side rendering. Client-side rendering is a good choice for heavily interactive **web applications** that don't need indexing or whose users visit frequently. It can leverage browser caching to skip the download phase on subsequent visits, such as **SaaS, back-office applications, or online games**. You can enable client-side only rendering with Nuxt in your `nuxt.config.ts`: ```ts [nuxt.config.ts] export default defineNuxtConfig({ ssr: false, }) ``` ::note If you do use `ssr: false`, you should also place an HTML file in `app/spa-loading-template.html` with some HTML you would like to use to render a loading screen until your app is ready. :read-more{title="SPA Loading Template" to="https://nuxt.com/docs/5.x/directory-structure/app/spa-loading-template"} :: ::video-accordion{title="Watch a video from Alexander Lichter about Building a plain SPA with Nuxt" videoId="7Lr0QTP1Ro8"} :: ### Deploying a Static Client-Rendered App If you deploy your app to [static hosting](https://nuxt.com/docs/5.x/getting-started/deployment#static-hosting) with the `nuxt generate` or `nuxt build --prerender` commands, then by default, Nuxt will render every page as a separate static HTML file. ::warning If you prerender your app with the `nuxt generate` or `nuxt build --prerender` commands, then you will not be able to use any server endpoints as no server will be included in your output folder. If you need server functionality, use `nuxt build` instead. :: If you are using purely client-side rendering, then this might be unnecessary. You might only need a single `index.html` file, plus `200.html` and `404.html` fallbacks, which you can tell your static web host to serve up for all requests. In order to achieve this we can change how the routes are prerendered. Just add this to [your hooks](https://nuxt.com/docs/5.x/api/advanced/hooks#nuxt-hooks-build-time) in your `nuxt.config.ts`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ hooks: { 'prerender:routes' ({ routes }) { routes.clear() // Do not generate any routes (except the defaults) }, }, }) ``` This will produce three files: - `index.html` - `200.html` - `404.html` #### What are 200.html and 404.html? Static hosts need an HTML shell for client-side routes and missing paths. Nuxt emits two SPA fallbacks for that: - **`200.html`**. Serve this for unmatched paths when you want the client router to handle the URL. - **`404.html`**. Serve this when the host should keep a 404 status and still load your app. `nuxt generate` and `nuxt build --prerender` write these into `.output/public/`. A plain `nuxt build` without prerender does not. With hybrid route rules, add the fallbacks with route rules or run a prerender build if you need them. Point your host at the file your provider expects. #### Server-rendering the Error Page By default `404.html` is an empty shell, so your `error.vue` (and its layout and data) only appear once the client app has booted. You can prerender it instead: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { prerenderErrorPages: true, }, }) ``` Nuxt then renders `error.vue` at build time with a synthetic 404 error and writes the result to `404.html`, which hydrates in place at whatever URL your host served it from. Pass an array of status codes between 400 and 599 (`[404, 500]`) to emit additional pages, if your host can serve them. Because a single file is served for every missing path, the error page cannot depend on the request: `useRoute()`, `useRequestURL()` and data fetched with the current path will hold the build-time values in the prerendered HTML and be corrected on hydration. Wrap request-specific markup in [``](https://nuxt.com/docs/5.x/api/components/client-only), and skip request-specific data fetching with `import.meta.prerender`: ```vue [error.vue] ``` `import.meta.prerender` is only `true` while the page is being generated, so the same `error.vue` still server-renders its data when the app is deployed with a server. #### Skipping Client Fallback Generation When prerendering a client-rendered app, Nuxt will generate `index.html`, `200.html` and `404.html` files by default. However, if you need to prevent any (or all) of these files from being generated in your build, you can use the `'prerender:generate'` hook from [Nitro](https://nuxt.com/docs/5.x/getting-started/prerendering#prerendergenerate-nitro-hook). ```ts twoslash [nuxt.config.ts] // @errors: 2353 7006 export default defineNuxtConfig({ ssr: false, nitro: { hooks: { 'prerender:generate' (route) { const routesToSkip = ['/index.html', '/200.html', '/404.html'] if (routesToSkip.includes(route.route)) { route.skip = true } }, }, }, }) ``` ## Hybrid Rendering Hybrid rendering allows different caching rules per route using **Route Rules** and decides how the server should respond to a new request on a given URL. Previously every route/page of a Nuxt application and server must use the same rendering mode, universal or client-side. In various cases, some pages could be generated at build time, while others should be client-side rendered. For example, think of a content website with an admin section. Every content page should be primarily static and generated once, but the admin section requires registration and behaves more like a dynamic application. Nuxt includes route rules and hybrid rendering support. Using route rules you can define rules for a group of nuxt routes, change rendering mode or assign a cache strategy based on route! Nuxt server will automatically register corresponding middleware and wrap routes with cache handlers using [Nitro caching layer](https://nitro.build/guide/cache). ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ routeRules: { // Homepage pre-rendered at build time '/': { prerender: true }, // Products page generated on demand, revalidates in background, cached until API response changes '/products': { swr: true }, // Product pages generated on demand, revalidates in background, cached for 1 hour (3600 seconds) '/products/**': { swr: 3600 }, // Blog posts page generated on demand, revalidates in background, cached on CDN for 1 hour (3600 seconds) '/blog': { isr: 3600 }, // Blog post page generated on demand once until next deployment, cached on CDN '/blog/**': { isr: true }, // Admin dashboard renders only on client-side '/admin/**': { ssr: false }, // Add cors headers on API routes '/api/**': { cors: true }, // Redirects legacy urls '/old-page': { redirect: '/new-page' }, '/proxy/**': { proxy: 'https://api.example.com/**' }, }, }) ``` ### Route Rules The different properties you can use are the following: - `redirect: string`{lang="ts"} - Define server-side redirects. - `proxy: string | object`{lang="ts"} - Proxy requests to another URL using a string target or an object with `to` and H3 proxy options. Read more in [Nitro's route rules docs](https://nitro.build/guide/routing#route-rules). - `ssr: boolean`{lang="ts"} - Disables server-side rendering of the HTML for sections of your app and make them render only in the browser with `ssr: false` - `cors: boolean`{lang="ts"} - Automatically adds cors headers with `cors: true` - you can customize the output by overriding with `headers` - `headers: object`{lang="ts"} - Add specific headers to sections of your site - for example, your assets - `swr: number | boolean`{lang="ts"} - Add cache headers to the server response and cache it on the server or reverse proxy for a configurable TTL (time to live). The `node-server` preset of Nitro is able to cache the full response. When the TTL expired, the cached response will be sent while the page will be regenerated in the background. If true is used, a `stale-while-revalidate` header is added without a MaxAge. - `isr: number | boolean`{lang="ts"} - The behavior is the same as `swr` except that we are able to add the response to the CDN cache on platforms that support this (currently Netlify or Vercel). If `true` is used, the content persists until the next deploy inside the CDN. - `prerender: boolean`{lang="ts"} - Prerenders routes at build time and includes them in your build as static assets - `noScripts: boolean`{lang="ts"} - Disables rendering of Nuxt scripts and JS resource hints for sections of your site. Read more about [`noScripts`](https://nuxt.com/docs/5.x/guide/going-further/features#noscripts). - `appMiddleware: string | string[] | Record`{lang="ts"} - Allows you to define middleware that should or should not run for page paths within the Vue app part of your application (that is, not your Nitro routes) ::note Routes using `isr` or `swr` also generate `_payload.json` files alongside HTML. Client-side navigation loads these cached payloads instead of re-fetching data. Read more about [payload extraction](https://nuxt.com/docs/5.x/getting-started/prerendering#payload-extraction). :: ### Server Bundle Size with `ssr: false` A route covered by `ssr: false` is only ever rendered in the browser, so Nuxt excludes its page component from the server bundle. This applies whenever the rules covering every path that reaches the page can be resolved at build time, including dynamic routes such as `pages/products/[id].vue` under a `/products/**` rule. A page is kept in the server bundle when any path that reaches it might still be rendered on the server: - a more specific rule re-enables SSR somewhere below the client-only one (`'/admin/**': { ssr: false }`{lang="ts"} with `'/admin/report': { ssr: true }`{lang="ts"}) - the page has an alias, or a child declared with an absolute path, that falls outside the client-only region - the page is a parent shell rendering a child that is still server-rendered This is a build-time optimization only; it does not change what the server sends to the browser. Whenever possible, route rules will be automatically applied to the deployment platform's native rules for optimal performances (Netlify and Vercel are currently supported). ::important Note that Hybrid Rendering is not available when using [`nuxt generate`](https://nuxt.com/docs/5.x/api/commands/generate). :: **Examples:** ::card-group :::card ```yaml [props] icon: i-simple-icons-github title: Nuxt Vercel ISR to: https://github.com/danielroe/nuxt-vercel-isr target: _blank ui.icon.base: text-black dark:text-white ``` Example of a Nuxt application with hybrid rendering deployed on Vercel. ::: :: ## Edge-Side Rendering Edge-Side Rendering (ESR) is a powerful feature introduced in Nuxt that allows the rendering of your Nuxt application closer to your users via edge servers of a Content Delivery Network (CDN). By leveraging ESR, you can ensure improved performance and reduced latency, thereby providing an enhanced user experience. With ESR, the rendering process is pushed to the 'edge' of the network - the CDN's edge servers. Note that ESR is more a deployment target than an actual rendering mode. When a request for a page is made, instead of going all the way to the original server, it's intercepted by the nearest edge server. This server generates the HTML for the page and sends it back to the user. This process minimizes the physical distance the data has to travel, **reducing latency and loading the page faster**. Edge-side rendering is possible thanks to [Nitro](https://nitro.build/), the [server engine](https://nuxt.com/docs/5.x/guide/concepts/server-engine) that powers Nuxt. It offers cross-platform support for Node.js, Deno, Cloudflare Workers, and more. The current platforms where you can leverage ESR are: - [Cloudflare Pages](https://pages.cloudflare.com) with zero configuration using the git integration and the `nuxt build` command - [Vercel Cloud](https://vercel.com/home) using the `nuxt build` command and `NITRO_PRESET=vercel-edge` environment variable - [Netlify Edge Functions](https://www.netlify.com/platform/#netlify-edge-functions) using the `nuxt build` command and `NITRO_PRESET=netlify-edge` environment variable Note that **Hybrid Rendering** can be used when using Edge-Side Rendering with route rules. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Server Components" description: "Render individual components on the server only, keeping their JavaScript out of your client bundle." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/server-components" --- # Server Components > Render individual components on the server only, keeping their JavaScript out of your client bundle. Nuxt renders your app on the server by default, but then it ships the JavaScript for every component to the browser and hydrates the whole page. For content-heavy components (markdown rendering, syntax highlighting, CMS output) that never change on the client, this is wasted work: the user downloads, parses and executes code whose only job is to reproduce HTML that is already on the page. Server components (also called island components) invert this. A server component is rendered on the server, its HTML is embedded in the page, and none of its JavaScript is sent to the client. Its dependencies (a markdown parser, a highlighting library) stay on the server too. ::tip{icon="i-lucide-newspaper" to="https://roe.dev/blog/nuxt-server-components" target="_blank"} Read Daniel Roe's guide to Nuxt Server Components. :: ::video-accordion{title="Watch Learn Vue video about Nuxt Server Components" videoId="u1yyXe86xJM"} :: ## Enabling Server Components Component islands are controlled by [`experimental.componentIslands`](https://nuxt.com/docs/5.x/guide/going-further/experimental-features#componentislands). The default value is `'auto'`, which enables the feature automatically as soon as your app contains a server component or island, so in most cases you do not need any configuration. Set the option explicitly if you want remote islands or selective client hydration: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { componentIslands: { selectiveClient: true, // or 'deep', to enable `nuxt-client` remoteIsland: false, // allow rendering islands from a remote source }, }, }) ``` ::read-more{icon="i-simple-icons-github" to="https://github.com/nuxt/nuxt/issues/19772" target="_blank"} Server components are still marked experimental. You can follow the roadmap on GitHub. :: ## `.server.vue` Components Add the `.server` suffix to a component to make it a standalone server component: ```bash [Directory Structure] -| app/ ---| components/ -----| HighlightedMarkdown.server.vue ``` Use it like any other component: ```vue [app/pages/example.vue] ``` Components inside `~/components/islands/` are also registered as islands and can be rendered with [``](https://nuxt.com/docs/5.x/api/components/nuxt-island) directly, for example `` for `~/components/islands/MyIsland.vue`. You can also pair a `.server.vue` component with a `.client.vue` component of the same name for [separate server and client implementations](https://nuxt.com/docs/5.x/directory-structure/app/components#paired-with-a-client-component). In that case the component is not an island: the client half hydrates normally. ::warning Server components (and islands) must have a single root element. (HTML comments are considered elements as well.) :: ## How Islands Are Rendered Server components use [``](https://nuxt.com/docs/5.x/api/components/nuxt-island) under the hood. Rendering an island issues a request to a dedicated island endpoint, which: - creates a **new, isolated Vue app** on the server to render just that component - creates an 'island context' that you can access via `nuxtApp.ssrContext.islandContext` inside the island - runs your plugins again, unless they set `env: { islands: false }` (object-syntax plugins) Because the island is isolated from the rest of your app: - you cannot share state (provide/inject, Pinia, `useState`) between the page and the island; pass data via props instead - [`useRoute()`](https://nuxt.com/docs/5.x/api/composables/use-route) inside an island reflects the island's own request, not the page the user is on. If an island needs route information, pass it in explicitly, either as props or via the `context` prop on `` (read inside the island from `nuxtApp.ssrContext.islandContext`) - route middleware does not run when rendering islands Props are serialized and sent as **GET query parameters**. This makes island responses cacheable, but it also means: - props must be JSON-serializable - props are limited by URL length, so avoid passing large amounts of data - props may be visible in server access logs, CDN caches and `Referer` headers ::note Because props come from the request (URL query or body), treat them as untrusted input. Nuxt rejects the props most likely to leak through unintentionally: a top-level `as` that the island does not declare (an undeclared prop falls through as an attribute onto the island's root), and, with `vue.runtimeCompiler` enabled, a `template` anywhere in the props. Beyond that, avoid feeding props you have not validated into dynamic component resolution (``{lang="vue"}, `h()`, `resolveDynamicComponent()`, or a polymorphic `as` / `asChild` prop), since a string can resolve to any registered component or HTML element. Treating props as untrusted includes their size: validate or clamp numeric and array props before using them to drive a `v-for` or other work that grows with their value. Nuxt caps numeric `v-for` sources at 10,000 iterations in island renders, but only as a safety net, not as validation. Props a component does not declare fall through as attributes onto its single root element, so an island whose root is a polymorphic component (e.g. from `reka-ui` / `@nuxt/ui`) can receive attributes you did not bind. Set `defineOptions({ inheritAttrs: false })`{lang="ts"} on such islands, or declare the props you accept. As with any Vue component, fall-through attributes include event handler attributes such as `onclick` or `onfocus`, which the browser runs as JavaScript, so don't spread an object you don't control (such as `route.query`) onto a server component or into the `props` of ``{lang="vue"}. To switch components based on caller input, map a discriminator through an allowlist of imported components rather than passing the raw prop: ```vue ``` :: Changing an island's props triggers a network request that re-renders the component on the server and updates its HTML in place. ::read-more{to="https://nuxt.com/docs/5.x/api/components/nuxt-island"} Read the full `` API documentation, including props, slots, events and known limitations. :: ## Selective Hydration with `nuxt-client` An island is static by default, but you can hydrate individual components inside it by adding the `nuxt-client` attribute. This requires `experimental.componentIslands.selectiveClient` to be enabled. ```vue [app/components/ServerWithClient.server.vue] ``` The component marked with `nuxt-client` is server-rendered as part of the island, then hydrated by the main client app. Only its chunk is shipped to the client; the rest of the island remains static. Setting `selectiveClient: 'deep'` additionally allows passing slots to `nuxt-client` components. Those slots are rendered on the server and are **not interactive** on the client. ::warning Use `nuxt-client` only on local `.vue` SFCs. Built-ins like [``](https://nuxt.com/docs/5.x/api/components/nuxt-link) skip the islands transform. After client navigation you may see `Failed to locate Teleport target`, or the link disappears with no error. Wrap the built-in in your own `.vue` file and put `nuxt-client` on that wrapper. See [#29251](https://github.com/nuxt/nuxt/issues/29251) and [#26002](https://github.com/nuxt/nuxt/issues/26002). :: ## Slots Slots can be passed to an island component if declared in the island. Slot content is provided by the parent, so it belongs to the main client app and **is** interactive (it is wrapped in a `

` with `display: contents;`). `` reserves the `#fallback` slot to specify content rendered before the island loads (when `lazy` is set) or when fetching the island fails. ## The Client Navigation Round Trip On the initial server-rendered page load, islands are rendered inline and there is no extra request. On **client-side navigation**, however, each island on the destination page must be fetched from the server (you can see these requests in the network tab). This has real costs: - islands block on a network round trip during navigation, unless you pass the `lazy` prop (with a `#fallback` slot) to render them non-blockingly - an app with many islands per page makes many requests per navigation Islands work best on pages that are reached by full page loads (content and marketing pages) or when their number per page is small. If a component needs to update frequently on the client, an island is probably the wrong tool. When a `` to a [server-only page](https://nuxt.com/docs/5.x/directory-structure/app/pages#server-only-pages) is prefetched, its island response is prefetched too, so the round trip happens before the user navigates rather than during the navigation. The prefetched response is cached against the island's name, props and context, which is the same key the island uses when it mounts. A failed prefetch is not fatal: the island is fetched again on mount. ::note This applies to the island that renders a whole `.server.vue` page. Islands used within a page are still fetched when they mount. :: ## Prerendering and Caching Islands play well with static and cached rendering: - during prerendering (`nuxt generate` or `prerender` route rules), island responses are cached, so identical islands (same name, props and context) are rendered once and reused - because props travel as GET query parameters, island responses can also be cached by your server or CDN at the island endpoint level - two instances of the same island with the same props share a single server render and payload entry Note that island responses being keyed only on name, props and context is exactly what keeps them cacheable independently of the page they appear on; this is also why they cannot see the current route (see above). If you are building a mostly-static site, islands combine well with `prerender` and `noScripts` route rules. ::read-more{to="https://nuxt.com/docs/5.x/guide/recipes/mostly-static-sites"} See the mostly-static site recipe for combining prerendering, `noScripts`, islands and lazy hydration. :: One interaction to be aware of: island slots and `nuxt-client` components rely on a small inline script to relocate teleported content into place before hydration. On routes rendered with [`noScripts`](https://nuxt.com/docs/5.x/guide/going-further/features#noscripts), that script is omitted, so fully interactive `nuxt-client` components will not hydrate there. Plain static islands are unaffected. ## Current Limitations Server components are experimental, and some rough edges are tracked in open issues: - Most features for server-only and island components, such as slots and `nuxt-client` components, are only available for single file components. - Using islands can significantly increase the number of chunks generated at build time ([#34855](https://github.com/nuxt/nuxt/issues/34855)). - With webpack and Rspack, scoped `:slotted()` styles in server component slots can fail because server and client builds may generate different scope IDs ([#31510](https://github.com/nuxt/nuxt/issues/31510)). - Template refs cannot reference elements inside a server component from the parent ([#31512](https://github.com/nuxt/nuxt/issues/31512)). - `inject`/`provide` does not cross the island boundary, so injecting from the page into a standalone server component does not work ([#22751](https://github.com/nuxt/nuxt/issues/22751)). - Server components rendered via the auto-generated wrapper do not expose load and error events; use `` directly if you need its `error` event and `refresh()` method ([#25744](https://github.com/nuxt/nuxt/issues/25744)). - [`useId`](https://vuejs.org/api/composition-api-helpers#useid) has known limitations inside islands; see the [`` documentation](https://nuxt.com/docs/5.x/api/components/nuxt-island#known-limitations). - Each nested island adds extra overhead, so be careful when nesting islands within other islands. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/components#server-components"} Read more about server component file conventions in the components directory documentation. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Server Engine" description: "Nuxt is powered by a new server engine: Nitro." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/server-engine" --- # Server Engine > Nuxt is powered by a new server engine: Nitro. While building Nuxt, we created a new server engine: [Nitro](https://nitro.build/). It is shipped with many features: - Cross-platform support for Node.js, browsers, service workers and more. - Serverless support out-of-the-box. - API routes support. - Automatic code-splitting and async-loaded chunks. - Hybrid mode for static + serverless sites. - Development server with hot module reloading. ## API Layer Server [API endpoints](https://nuxt.com/docs/5.x/directory-structure/server#server-routes) and [Middleware](https://nuxt.com/docs/5.x/directory-structure/server#server-middleware) are added by Nitro that internally uses [h3](https://github.com/h3js/h3). Key features include: - Handlers can directly return objects/arrays for an automatically-handled JSON response - Handlers can return promises, which will be awaited (`res.end()` and `next()` are also supported) - Helper functions for body parsing, cookie handling, redirects, headers and more Check out [the h3 docs](https://github.com/h3js/h3) for more information. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/server#server-routes"} Learn more about the API layer in the `server/` directory. :: ## Direct API Calls Nitro allows 'direct' calling of routes via the globally-available [`$fetch`](https://nuxt.com/docs/5.x/api/utils/dollarfetch) helper. This will make an API call to the server if run on the browser, but will directly call the relevant function if run on the server, **saving an additional API call**. [`$fetch`](https://nuxt.com/docs/5.x/api/utils/dollarfetch) API is using [ofetch](https://github.com/unjs/ofetch), with key features including: - Automatic parsing of JSON responses (with access to raw response if needed) - Request body and params are automatically handled, with correct `Content-Type` headers For more information on `$fetch` features, check out [ofetch](https://github.com/unjs/ofetch). ## Typed API Routes When using API routes (or middleware), Nitro will generate typings for these routes as long as you are returning a value instead of using `res.end()` to send a response. You can access these types when using [`$fetch()`](https://nuxt.com/docs/5.x/api/utils/dollarfetch) or [`useFetch()`](https://nuxt.com/docs/5.x/api/composables/use-fetch). ## Standalone Server Nitro produces a standalone server dist that is independent of `node_modules`. The server in Nuxt 2 is not standalone and requires part of Nuxt core to be involved by running `nuxt start` (with the [`nuxt-start`](https://www.npmjs.com/package/nuxt-start) or [`nuxt`](https://www.npmjs.com/package/nuxt) distributions) or custom programmatic usage, which is fragile and prone to breakage and not suitable for serverless and service worker environments. Nuxt generates this dist when running `nuxt build` into a [`.output`](https://nuxt.com/docs/5.x/directory-structure/output) directory. The output contains runtime code to run your Nuxt server in any environment (including experimental browser service workers!) and serve your static files, making it a true hybrid framework for the JAMstack. In addition, Nuxt implements a native storage layer, supporting multi-source drivers and local assets. ::read-more{icon="i-simple-icons-github" to="https://github.com/nitrojs/nitro" target="_blank"} Read more about Nitro engine on GitHub. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "TypeScript" description: "Nuxt is fully typed and provides helpful shortcuts to ensure you have access to accurate type information when you are coding." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/typescript" --- # TypeScript > Nuxt is fully typed and provides helpful shortcuts to ensure you have access to accurate type information when you are coding. ## Type-checking By default, Nuxt doesn't check types when you run [`nuxt dev`](https://nuxt.com/docs/5.x/api/commands/dev) or [`nuxt build`](https://nuxt.com/docs/5.x/api/commands/build), for performance reasons. To enable type-checking at build or development time, install `vue-tsc` and `typescript` as development dependency: ::code-group{sync="pm"} ```bash [npm] npm install --save-dev vue-tsc typescript ``` ```bash [yarn] yarn add --dev vue-tsc typescript ``` ```bash [pnpm] pnpm add -D vue-tsc typescript ``` ```bash [bun] bun add -D vue-tsc typescript ``` ```bash [deno] deno add -D npm:vue-tsc npm:typescript ``` :: Then, run [`nuxt typecheck`](https://nuxt.com/docs/5.x/api/commands/typecheck) command to check your types: ```bash [Terminal] npx nuxt typecheck ``` To enable type-checking at build or development time, you can also use the [`typescript.typeCheck`](https://nuxt.com/docs/5.x/api/nuxt-config#typecheck) option in your `nuxt.config` file: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ typescript: { typeCheck: true, }, }) ``` ## Auto-generated Types Nuxt projects rely on auto-generated types to work properly. These types are stored in the [`.nuxt`](https://nuxt.com/docs/5.x/directory-structure/nuxt) directory and are generated when you run the dev server or build your application. You can also generate these files manually by running `nuxt prepare`. The generated `tsconfig.json` files inside the [`.nuxt`](https://nuxt.com/docs/5.x/directory-structure/nuxt) directory include **recommended basic TypeScript configuration** for your project, references to [auto-imports](https://nuxt.com/docs/5.x/guide/concepts/auto-imports), [API route types](https://nuxt.com/docs/5.x/guide/concepts/server-engine#typed-api-routes), path aliases like `#imports`, `~/file`, or `#build/file`, and more. ::warning Nuxt relies on this configuration, and [Nuxt modules](https://nuxt.com/docs/5.x/guide/modules) can extend it as well. For this reason, it is not recommended to modify your `tsconfig.json` file directly, as doing so could overwrite important settings. Instead, extend it via `nuxt.config.ts`. [Learn more about extending the configuration here](https://nuxt.com/docs/5.x/directory-structure/tsconfig). :: ::tip{icon="i-lucide-video" to="https://youtu.be/umLI7SlPygY" target="_blank"} Watch a video from Daniel Roe explaining built-in Nuxt aliases. :: ## Project References Nuxt uses [TypeScript project references](https://www.typescriptlang.org/docs/handbook/project-references.html) to improve type-checking performance and provide better IDE support. This feature allows TypeScript to break up your codebase into smaller, more manageable pieces. ### How Nuxt Uses Project References When you run `nuxt dev`, `nuxt build` or `nuxt prepare`, Nuxt will generate multiple `tsconfig.json` files for different parts of your application. - **`.nuxt/tsconfig.app.json`** - Configuration for your application code within the `app/` directory - **`.nuxt/tsconfig.node.json`** - Configuration for your `nuxt.config.ts` and files outside the other contexts - **`.nuxt/tsconfig.server.json`** - Configuration for server-side code (when applicable) - **`.nuxt/tsconfig.shared.json`** - For code shared between app and server contexts (like types and non-environment specific utilities) Each of these files is configured to reference the appropriate dependencies and provide optimal type-checking for their specific context. ::note For backward compatibility, Nuxt still generates `.nuxt/tsconfig.json`. However, we recommend using [TypeScript project references](https://nuxt.com/docs/5.x/directory-structure/tsconfig) with the new configuration files (`.nuxt/tsconfig.app.json`, `.nuxt/tsconfig.server.json`, etc.) for better type safety and performance. This legacy file will be removed in a future version of Nuxt. :: ### Benefits of Project References - **Faster builds**: TypeScript can skip rebuilding unchanged projects - **Better IDE performance**: Your IDE can provide faster IntelliSense and error checking - **Isolated compilation**: Errors in one part of your application don't prevent compilation of other parts - **Clearer dependency management**: Each project explicitly declares its dependencies ### Augmenting Types with Project References Since the project is divided into **multiple type contexts**, it's important to **augment types within the correct context** to ensure they're properly recognized. TypeScript will not recognize augmentations placed outside these directories unless they are explicitly included in the appropriate context. For example, if you want to augment types for the `app` context, the augmentation file should be placed in the `app/` directory. Similarly: - For the `server` context, place the augmentation file in the `server/` directory. - For types that are **shared between the app and server**, place the file in the `shared/` directory. ::read-more{to="https://nuxt.com/docs/5.x/guide/modules/recipes-advanced#extend-typescript-config"} Read more about augmenting specific type contexts from **files outside those contexts** in the Module Author Guide. :: ## Strict Checks TypeScript comes with certain checks to give you more safety and analysis of your program. [Strict checks](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html#getting-stricter-checks) are enabled by default in Nuxt when the [`typescript.typeCheck`](https://nuxt.com/docs/5.x/guide/concepts/typescript#type-checking) option is enabled to give you greater type safety. If you are currently converting your codebase to TypeScript, you may want to temporarily disable strict checks by setting `strict` to `false` in your `nuxt.config`: ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ typescript: { strict: false, }, }) ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Vue.js Development" description: "Nuxt uses Vue.js and adds features such as component auto-imports, file-based routing and composables for an SSR-friendly usage." canonical_url: "https://nuxt.com/docs/5.x/guide/concepts/vuejs-development" --- # Vue.js Development > Nuxt uses Vue.js and adds features such as component auto-imports, file-based routing and composables for an SSR-friendly usage. Nuxt integrates Vue 3, the new major release of Vue that enables new patterns for Nuxt users. ::note While an in-depth knowledge of Vue is not required to use Nuxt, we recommend that you read the documentation and go through some of the examples on [vuejs.org](https://vuejs.org). :: Nuxt has always used Vue as a frontend framework. We chose to build Nuxt on top of Vue for these reasons: - The reactivity model of Vue, where a change in data automatically triggers a change in the interface. - The component-based templating, while keeping HTML as the common language of the web, enables intuitive patterns to keep your interface consistent, yet powerful. - From small projects to large web applications, Vue keeps performing well at scale to ensure that your application keeps delivering value to your users. ## Vue with Nuxt ### Single File Components [Vue’s single-file components](https://vuejs.org/guide/scaling-up/sfc) (SFC or `*.vue` files) encapsulate the markup (` ``` :: ::important 1. Make sure you use capital letters for these component names to distinguish them from native HTML elements (`` rather than `<title>`). 2. You can place these components anywhere in your template for your page. :: ### Options API ```vue [Nuxt 3 (Options API)] <script> // if using options API `head` method you must use `defineNuxtComponent` export default defineNuxtComponent({ head (nuxtApp) { // `head` receives the nuxt app but cannot access the component instance return { meta: [{ name: 'description', content: 'This is my page description.', }], } }, }) </script> ``` ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Modules" description: "Learn how to migrate from Nuxt 2 to Nuxt 3 modules." canonical_url: "https://nuxt.com/docs/5.x/migration/module-authors" --- # Modules > Learn how to migrate from Nuxt 2 to Nuxt 3 modules. ## Module Compatibility Nuxt 3 has a basic backward compatibility layer for Nuxt 2 modules using `@nuxt/kit` auto wrappers. But there are usually steps to follow to make modules compatible with Nuxt 3 and sometimes, using Nuxt Bridge is required for cross-version compatibility. We have prepared a [Dedicated Guide](https://nuxt.com/docs/5.x/guide/modules) for authoring Nuxt 3 ready modules using `@nuxt/kit`. Currently best migration path is to follow it and rewrite your modules. Rest of this guide includes preparation steps if you prefer to avoid a full rewrite yet making modules compatible with Nuxt 3. ::tip{icon="i-lucide-puzzle" to="https://nuxt.com/modules"} Explore Nuxt 3 compatible modules. :: ### Plugin Compatibility Nuxt 3 plugins are **not** fully backward compatible with Nuxt 2. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/plugins"} :: ### Vue Compatibility Plugins or components using the Composition API need exclusive Vue 2 or Vue 3 support. By using [vue-demi](https://github.com/vueuse/vue-demi) they should be compatible with both Nuxt 2 and 3. ## Module Migration When Nuxt 3 users add your module, you will not have access to the module container (`this.*`) so you will need to use utilities from `@nuxt/kit` to access the container functionality. ### Test with `@nuxt/bridge` Migrating to `@nuxt/bridge` is the first and most important step for supporting Nuxt 3. If you have a fixture or example in your module, add `@nuxt/bridge` package to its config (see [example](https://nuxt.com/docs/5.x/bridge/overview#update-nuxtconfig)) ### Migrate from CommonJS to ESM Nuxt 3 natively supports TypeScript and ECMAScript Modules. Please check [Native ES Modules](https://nuxt.com/docs/5.x/guide/concepts/esm) for more info and upgrading. ### Ensure Plugins Default Export If you inject a Nuxt plugin that does not have `export default` (such as global Vue plugins), ensure you add `export default () => { }` to the end of it. ::code-group ```js [Before] // ~/plugins/vuelidate.js import Vue from 'vue' import Vuelidate from 'vuelidate' Vue.use(Vuelidate) ``` ```js [After] // ~/plugins/vuelidate.js import Vue from 'vue' import Vuelidate from 'vuelidate' Vue.use(Vuelidate) export default () => { } ``` :: ### Avoid Runtime Modules With Nuxt 3, Nuxt is now a build-time-only dependency, which means that modules shouldn't attempt to hook into the Nuxt runtime. Your module should work even if it's only added to [`buildModules`](https://nuxt.com/docs/5.x/api/nuxt-config#runtimeconfig-1) (instead of `modules`). For example: - Avoid updating `process.env` within a Nuxt module and reading by a Nuxt plugin; use [`runtimeConfig`](https://nuxt.com/docs/5.x/api/nuxt-config#runtimeconfig-1) instead. - (\*) Avoid depending on runtime hooks like `vue-renderer:*` for production - (\*) Avoid adding `serverMiddleware` by importing them inside the module. Instead, add them by referencing a file path so that they are independent of the module's context (\*) Unless it is for `nuxt dev` purpose only and guarded with `if (nuxt.options.dev) { }`. ::tip Continue reading about Nuxt 3 modules in the [Modules Author Guide](https://nuxt.com/docs/5.x/guide/modules). :: ### Use TypeScript (Optional) While it is not essential, most of the Nuxt ecosystem is shifting to use TypeScript, so it is highly recommended to consider migration. ::tip You can start migration by renaming `.js` files, to `.ts`. TypeScript is designed to be progressive! :: ::tip You can use TypeScript syntax for Nuxt 2 and 3 modules and plugins without any extra dependencies. :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Overview" description: "Nuxt 3 is a complete rewrite of Nuxt 2, and also based on a new set of underlying technologies." canonical_url: "https://nuxt.com/docs/5.x/migration/overview" --- # Overview > Nuxt 3 is a complete rewrite of Nuxt 2, and also based on a new set of underlying technologies. There are significant changes when migrating a Nuxt 2 app to Nuxt 3, although you can expect migration to become more straightforward as we move toward a stable release. ::note This migration guide is under progress to align with the development of Nuxt 3. :: Some of these significant changes include: 1. Moving from Vue 2 to Vue 3, including defaulting to the Composition API and script setup. 2. Moving from webpack 4 and Babel to Vite or webpack 5 and esbuild. 3. Moving from a runtime Nuxt dependency to a minimal, standalone server compiled with nitropack. ::tip If you need to remain on Nuxt 2, but want to benefit from Nuxt 3 features in Nuxt 2, you can alternatively check out [how to get started with Bridge](https://nuxt.com/docs/5.x/bridge/overview). :: ## Next Steps - Learn about differences in [configuration](https://nuxt.com/docs/5.x/migration/configuration) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Pages and Layouts" description: "Learn how to migrate from Nuxt 2 to Nuxt 3 pages and layouts." canonical_url: "https://nuxt.com/docs/5.x/migration/pages-and-layouts" --- # Pages and Layouts > Learn how to migrate from Nuxt 2 to Nuxt 3 pages and layouts. ## `app.vue` Nuxt 3 provides a central entry point to your app via `~/app.vue`. ::note If you don't have an `app.vue` file in your source directory, Nuxt will use its own default version. :: This file is a great place to put any custom code that needs to be run once when your app starts up, as well as any components that are present on every page of your app. For example, if you only have one layout, you can move this to `app.vue` instead. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/app"} :: ::link-example{to="https://nuxt.com/docs/examples/hello-world"} :: ### Migration Consider creating an `app.vue` file and including any logic that needs to run once at the top-level of your app. You can check out [an example here](https://nuxt.com/docs/5.x/directory-structure/app/app). ## Layouts If you are using layouts in your app for multiple pages, there is only a slight change required. In Nuxt 2, the `<Nuxt>` component is used within a layout to render the current page. In Nuxt 3, layouts use slots instead, so you will have to replace that component with a `<slot />`. This also allows advanced use cases with named and scoped slots. [Read more about layouts](https://nuxt.com/docs/5.x/directory-structure/app/layouts). You will also need to change how you define the layout used by a page using the `definePageMeta` compiler macro. Layouts will be kebab-cased. So `app/layouts/customLayout.vue` becomes `custom-layout` when referenced in your page. ### Migration 1. Replace `<Nuxt />` with `<slot />` ```diff [app/layouts/custom.vue] <template> <div id="app-layout"> <main> - <Nuxt /> + <slot /> </main> </div> </template> ``` 2. Use [`definePageMeta`](https://nuxt.com/docs/5.x/api/utils/define-page-meta) to select the layout used by your page. ```diff [app/pages/index.vue] + <script setup> + definePageMeta({ + layout: 'custom' + }) - <script> - export default { - layout: 'custom' - } </script> ``` 3. Move `~/layouts/_error.vue` to `~/error.vue`. See [the error handling docs](https://nuxt.com/docs/5.x/getting-started/error-handling). If you want to ensure that this page uses a layout, you can use [`<NuxtLayout>`](https://nuxt.com/docs/5.x/directory-structure/app/layouts) directly within `error.vue`: ```vue [error.vue] <template> <div> <NuxtLayout name="default"> <!-- --> </NuxtLayout> </div> </template> ``` ## Pages Nuxt 3 ships with an optional `vue-router` integration triggered by the existence of a [`app/pages/`](https://nuxt.com/docs/5.x/directory-structure/app/pages) directory in your source directory. If you only have a single page, you may consider instead moving it to `app.vue` for a lighter build. ### Dynamic Routes The format for defining dynamic routes in Nuxt 3 is slightly different from Nuxt 2, so you may need to rename some of the files within `app/pages/`. 1. Where you previously used `_id` to define a dynamic route parameter you now use `[id]`. 2. Where you previously used `_.vue` to define a catch-all route, you now use `[...slug].vue`. ### Nested Routes In Nuxt 2, you will have defined any nested routes (with parent and child components) using `<Nuxt>` and `<NuxtChild>`. In Nuxt 3, these have been replaced with a single `<NuxtPage>` component. ### Page Keys and Keep-alive Props If you were passing a custom page key or keep-alive props to `<Nuxt>`, you will now use `definePageMeta` to set these options. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/pages#special-metadata"} :: ### Page and Layout Transitions If you have been defining transitions for your page or layout directly in your component options, you will now need to use `definePageMeta` to set the transition. Since Vue 3, [-enter and -leave CSS classes have been renamed](https://v3-migration.vuejs.org/breaking-changes/transition.html). The `style` prop from `<Nuxt>` no longer applies to transition when used on `<slot>`, so move the styles to your `-active` class. ::read-more{to="https://nuxt.com/docs/5.x/getting-started/transitions"} :: ### Migration 1. Rename any pages with dynamic parameters to match the new format. 2. Update `<Nuxt>` and `<NuxtChild>` to be `<NuxtPage>`. 3. If you're using the Composition API, you can also migrate `this.$route` and `this.$router` to use [`useRoute`](https://nuxt.com/docs/5.x/api/composables/use-route) and [`useRouter`](https://nuxt.com/docs/5.x/api/composables/use-router) composables. #### Example: Dynamic Routes ::code-group ``` [Nuxt 2] - URL: /users - Page: /pages/users/index.vue - URL: /users/some-user-name - Page: /pages/users/_user.vue - Usage: params.user - URL: /users/some-user-name/edit - Page: /pages/users/_user/edit.vue - Usage: params.user - URL: /users/anything-else - Page: /pages/users/_.vue - Usage: params.pathMatch ``` ``` [Nuxt 3] - URL: /users - Page: /pages/users/index.vue - URL: /users/some-user-name - Page: /pages/users/[user].vue - Usage: params.user - URL: /users/some-user-name/edit - Page: /pages/users/[user]/edit.vue - Usage: params.user - URL: /users/anything-else - Page: /pages/users/[...slug].vue - Usage: params.slug ``` :: #### Example: Nested Routes and `definePageMeta` ::code-group ```vue [Nuxt 2] <template> <div> <NuxtChild keep-alive :keep-alive-props="{ exclude: ['modal'] }" :nuxt-child-key="$route.slug" /> </div> </template> <script> export default { transition: 'page', // or { name: 'page' } } </script> ``` ```vue [Nuxt 3] <template> <div> <NuxtPage /> </div> </template> <script setup lang="ts"> // This compiler macro works in both <script> and <script setup> definePageMeta({ // you can also pass a string or a computed property key: route => route.slug, transition: { name: 'page', }, keepalive: { exclude: ['modal'], }, }) </script> ``` :: ## `<NuxtLink>` Component Most of the syntax and functionality are the same for the global [NuxtLink](https://nuxt.com/docs/5.x/api/components/nuxt-link) component. If you have been using the shortcut `<NLink>` format, you should update this to use `<NuxtLink>`. `<NuxtLink>` is now a drop-in replacement for *all* links, even external ones. You can read more about it, and how to extend it to provide your own link component. ::read-more{to="https://nuxt.com/docs/5.x/api/components/nuxt-link"} :: ## Programmatic Navigation When migrating from Nuxt 2 to Nuxt 3, you will have to update how you programmatically navigate your users. In Nuxt 2, you had access to the underlying Vue Router with `this.$router`. In Nuxt 3, you can use the `navigateTo()` utility method which allows you to pass a route and parameters to Vue Router. ::warning Make sure to always `await` on [`navigateTo`](https://nuxt.com/docs/5.x/api/utils/navigate-to) or chain its result by returning from functions. :: ::code-group ```vue [Nuxt 2] <script> export default { methods: { navigate () { this.$router.push({ path: '/search', query: { name: 'first name', type: '1', }, }) }, }, } </script> ``` ```vue [Nuxt 3] <script setup lang="ts"> function navigate () { return navigateTo({ path: '/search', query: { name: 'first name', type: '1', }, }) } </script> ``` :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Plugins and Middleware" description: "Learn how to migrate from Nuxt 2 to Nuxt 3 plugins and middleware." canonical_url: "https://nuxt.com/docs/5.x/migration/plugins-and-middleware" --- # Plugins and Middleware > Learn how to migrate from Nuxt 2 to Nuxt 3 plugins and middleware. ## Plugins Plugins now have a different format, and take only one argument (`nuxtApp`). ::code-group ```ts [Nuxt 2] export default (ctx, inject) => { inject('injected', () => 'my injected function') } ``` ```ts [Nuxt 3] export default defineNuxtPlugin((nuxtApp) => { // now available on `nuxtApp.$injected` nuxtApp.provide('injected', () => 'my injected function') // You can alternatively use this format, which comes with automatic type support return { provide: { injected: () => 'my injected function', }, } }) ``` :: ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/plugins"} :: ::read-more{to="https://nuxt.com/docs/5.x/api/composables/use-nuxt-app"} Read more about the format of `nuxtApp`. :: ### Migration 1. Migrate your plugins to use the `defineNuxtPlugin` helper function. 2. Remove any entries in your `nuxt.config` plugins array that are located in your `app/plugins/` folder. All files in this directory at the top level (and any index files in any subdirectories) will be automatically registered. Instead of setting `mode` to `client` or `server`, you can indicate this in the file name. For example, `~/plugins/my-plugin.client.ts` will only be loaded on client-side. ## Route Middleware Route middleware has a different format. ::code-group ```js [Nuxt 2] export default function ({ store, redirect }) { // If the user is not authenticated if (!store.state.authenticated) { return redirect('/login') } } ``` ```ts [Nuxt 3] export default defineNuxtRouteMiddleware((to, from) => { const auth = useState('auth') if (!auth.value.authenticated) { return navigateTo('/login') } }) ``` :: Much like Nuxt 2, route middleware placed in your `~/middleware` folder is automatically registered. You can then specify it by name in a component. However, this is done with `definePageMeta` rather than as a component option. `navigateTo` is one of a number of route helper functions. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/app/middleware"} :: ### Migration 1. Migrate your route middleware to use the `defineNuxtRouteMiddleware` helper function. 2. Any global middleware (such as in your `nuxt.config`) can be placed in your `~/middleware` folder with a `.global` extension, for example `~/middleware/auth.global.ts`. ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Runtime Config" description: "Learn how to migrate from Nuxt 2 to Nuxt 3 runtime config." canonical_url: "https://nuxt.com/docs/5.x/migration/runtime-config" --- # Runtime Config > Learn how to migrate from Nuxt 2 to Nuxt 3 runtime config. If you wish to reference environment variables within your Nuxt 3 app, you will need to use runtime config. When referencing these variables within your components, you will have to use the [`useRuntimeConfig`](https://nuxt.com/docs/5.x/api/composables/use-runtime-config) composable in your setup method (or Nuxt plugin). In the `server/` portion of your app, you can use [`useRuntimeConfig`](https://nuxt.com/docs/5.x/api/composables/use-runtime-config) without any import. ::read-more{to="https://nuxt.com/docs/5.x/guide/going-further/runtime-config"} :: ## Migration 1. Add any environment variables that you use in your app to the `runtimeConfig` property of the `nuxt.config` file. 2. Migrate `process.env` to [`useRuntimeConfig`](https://nuxt.com/docs/5.x/api/composables/use-runtime-config) throughout the Vue part of your app. ::code-group ```ts [nuxt.config.ts] export default defineNuxtConfig({ runtimeConfig: { // Private config that is only available on the server apiSecret: '123', // Config within public will be also exposed to the client public: { apiBase: '/api', }, }, }) ``` ```vue [app/pages/index.vue] <script setup lang="ts"> const config = useRuntimeConfig() // instead of process.env you will now access config.public.apiBase console.log(config.public.apiBase) </script> ``` ```ts [server/api/hello.ts] export default defineEventhandler((event) => { const config = useRuntimeConfig() // In server, you can now access config.apiSecret, in addition to config.public console.log(config.apiSecret) console.log(config.public.apiBase) }) ``` ```ini [.env] # Runtime config values are automatically replaced by matching environment variables at runtime NUXT_API_SECRET=api_secret_token NUXT_PUBLIC_API_BASE=https://nuxtjs.org ``` :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Server" description: "Learn how to migrate from Nuxt 2 to Nuxt 3 server." canonical_url: "https://nuxt.com/docs/5.x/migration/server" --- # Server > Learn how to migrate from Nuxt 2 to Nuxt 3 server. In a built Nuxt 3 application, there is no runtime Nuxt dependency. That means your site will be highly performant, and ultra-slim. But it also means you can no longer hook into runtime Nuxt server hooks. ::read-more{to="https://nuxt.com/docs/5.x/guide/concepts/server-engine"} :: ## Steps 1. Remove the `render` key in your `nuxt.config`. 2. Any files in `~~/server/api` and `~~/server/middleware` will be automatically registered; you can remove them from your `serverMiddleware` array. 3. Update any other items in your `serverMiddleware` array to point to files or npm packages directly, rather than using inline functions. ::read-more{to="https://nuxt.com/docs/5.x/directory-structure/server"} :: ::read-more{to="https://nuxt.com/docs/5.x/guide/going-further/hooks#server-hooks-runtime"} :: ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt add" description: "Add Nuxt modules and layers to your application." canonical_url: "https://nuxt.com/docs/5.x/api/commands/add" --- # nuxt add > Add Nuxt modules and layers to your application. <!--add-cmd--> ```bash [Terminal] npx nuxt add <MODULENAME...> [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--skipInstall] [--skipConfig] [--dev] [--packageManager=<npm|pnpm|yarn|bun|deno|aube|nub|upm>] ``` <!--/add-cmd--> The `add` command installs [Nuxt modules](https://nuxt.com/modules) and [layers](https://nuxt.com/docs/5.x/getting-started/layers) into your application. It is the same command as [`nuxt module add`](https://nuxt.com/docs/5.x/api/commands/module#nuxt-module-add), with layers included. ## Arguments <!--add-args--> | Argument | Description | | ---------------- | ----------------------------------------------------------------------------- | | `MODULENAME...` | Specify one or more modules or layers to install by name, separated by spaces | | <!--/add-args--> | | ## Options <!--add-opts--> | Option | Default | Description | | --------------------------------------------------------------- | ------- | ----------------------------------------------- | | `--cwd=<directory>` | `.` | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--skipInstall` | | Skip npm install | | `--skipConfig` | | Skip nuxt.config.ts update | | `--dev` | | Install modules as dev dependencies | | `--packageManager=<npm\|pnpm\|yarn\|bun\|deno\|aube\|nub\|upm>` | | Package manager to install with | | <!--/add-opts--> | | | When running the command, it will: - install the package as a dependency using your package manager, unless you pass `--skipInstall` - add it to your [`package.json`](https://nuxt.com/docs/5.x/directory-structure/package) file - register it in your [`nuxt.config`](https://nuxt.com/docs/5.x/directory-structure/nuxt-config) file, in `modules` for a module and in `extends` for a layer, unless you pass `--skipConfig` **Example:** ```bash [Terminal] npx nuxt add pinia ``` Several packages can be added at once, and the modules will be resolved against the Nuxt version your project uses: ```bash [Terminal] npx nuxt add @nuxt/image @nuxt/fonts ``` Run it without a name to browse the available modules interactively. ::note `nuxt add <template> <name>` is deprecated. Use [`nuxt add-template`](https://nuxt.com/docs/5.x/api/commands/add-template) to scaffold files. :: ::read-more{to="https://nuxt.com/docs/5.x/api/commands/module"} Read more about searching for and removing modules. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/add.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt add-template" description: "Scaffold an entity into your Nuxt application." canonical_url: "https://nuxt.com/docs/5.x/api/commands/add-template" --- # nuxt add-template > Scaffold an entity into your Nuxt application. <!--add-template-cmd--> ```bash [Terminal] npx nuxt add-template <TEMPLATE> <NAME> [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--force] [--mode=<client|server>] [--method=<connect|delete|get|head|options|patch|post|put|trace>] [--global] [--api] [--pages] [--client] [--server] [--connect] [--delete] [--get] [--head] [--options] [--post] [--put] [--trace] [--patch] ``` <!--/add-template-cmd--> The `add-template` command scaffolds a file into the right directory for your project's structure. It replaces `nuxt add <template> <name>`, which still runs but is deprecated. ::read-more{to="https://nuxt.com/docs/5.x/api/commands/add"} Read more about `nuxt add`, which adds modules and layers to your application. :: ## Arguments <!--add-template-args--> | Argument | Description | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | | `TEMPLATE=<api\|app\|app-config\|component\|composable\|error\|layer\|layout\|middleware\|module\|page\|plugin\|server-middleware\|server-plugin\|server-route\|server-util>` | Specify which template to generate | | `NAME` | Specify name of the generated file | | <!--/add-template-args--> | | ## Options <!--add-template-opts--> | Option | Default | Description | | ------------------------------------------------------------------------- | ------- | ------------------------------------------------------ | | `--cwd=<directory>` | `.` | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--force` | `false` | Overwrite the file if it already exists | | `--mode=<client\|server>` | | Add a client or server suffix to a component or plugin | | `--method=<connect\|delete\|get\|head\|options\|patch\|post\|put\|trace>` | | Add an HTTP method suffix to an API route | | `--global` | | Create global route middleware | | `--api` | | Create a server route in the API directory | | `--pages` | | Include NuxtPage and NuxtLayout in the app template | | `--client` | | Shorthand for `--mode client` | | `--server` | | Shorthand for `--mode server` | | `--connect` | | Shorthand for `--method connect` | | `--delete` | | Shorthand for `--method delete` | | `--get` | | Shorthand for `--method get` | | `--head` | | Shorthand for `--method head` | | `--options` | | Shorthand for `--method options` | | `--post` | | Shorthand for `--method post` | | `--put` | | Shorthand for `--method put` | | `--trace` | | Shorthand for `--method trace` | | `--patch` | | Shorthand for `--method patch` | | <!--/add-template-opts--> | | | **Modifiers:** Some templates take an extra flag that adds a suffix (like `.client` or `.get`) to the generated file name. Each value `--mode` and `--method` accept is also a flag of its own, so `--mode client` and `--client` do the same thing. Files are written relative to your [`srcDir`](https://nuxt.com/docs/5.x/api/nuxt-config#srcdir), which defaults to `app/`, and server files relative to your [`serverDir`](https://nuxt.com/docs/5.x/api/nuxt-config#serverdir). The paths below assume those defaults. ```bash [Terminal] # Generates `app/plugins/sockets.client.ts` npx nuxt add-template plugin sockets --mode client ``` ## `nuxt add-template component` - Modifier flags: `--mode`, or `--client` / `--server` ```bash [Terminal] # Generates `app/components/TheHeader.vue` npx nuxt add-template component TheHeader ``` ## `nuxt add-template composable` ```bash [Terminal] # Generates `app/composables/foo.ts` npx nuxt add-template composable foo ``` ## `nuxt add-template layout` ```bash [Terminal] # Generates `app/layouts/custom.vue` npx nuxt add-template layout custom ``` ## `nuxt add-template plugin` - Modifier flags: `--mode`, or `--client` / `--server` ```bash [Terminal] # Generates `app/plugins/analytics.ts` npx nuxt add-template plugin analytics ``` ## `nuxt add-template page` ```bash [Terminal] # Generates `app/pages/about.vue` npx nuxt add-template page about ``` ```bash [Terminal] # Generates `app/pages/category/[id].vue` npx nuxt add-template page "category/[id]" ``` ## `nuxt add-template middleware` - Modifier flags: `--global` ```bash [Terminal] # Generates `app/middleware/auth.ts` npx nuxt add-template middleware auth ``` ## `nuxt add-template api` - Modifier flags: `--method`, or the method as a flag of its own (`--get`, `--post`, and so on) ```bash [Terminal] # Generates `server/api/hello.ts` npx nuxt add-template api hello ``` ## `nuxt add-template server-route` - Modifier flags: `--api` to write the route under `server/api` instead of `server/routes` ```bash [Terminal] # Generates `server/routes/webhook.ts` npx nuxt add-template server-route webhook ``` ## `nuxt add-template layer` ```bash [Terminal] # Generates `layers/subscribe/nuxt.config.ts` npx nuxt add-template layer subscribe ``` ::note A name that would resolve outside the project is refused, so pass a path relative to the project without leading slashes or `..` segments. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/add-template.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt analyze" description: "Analyze the production bundle or your Nuxt application." canonical_url: "https://nuxt.com/docs/5.x/api/commands/analyze" --- # nuxt analyze > Analyze the production bundle or your Nuxt application. <!--analyze-cmd--> ```bash [Terminal] npx nuxt analyze [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--dotenv=<path>...] [-e, --extends=<layer-name>...] [--name=<name>] [--serve] [--prerender] ``` <!--/analyze-cmd--> The `analyze` command builds Nuxt and analyzes the production bundle (experimental). The results are served on a local server when the build finishes, unless you pass `--no-serve` or `CI` is set in the environment. Routes are not prerendered while analyzing, since prerendering runs the built app and its output is not what is being measured. Pass `--prerender` if your analysis needs it. ## Arguments <!--analyze-args--> | Argument | Description | | -------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/analyze-args--> | | ## Options <!--analyze-opts--> | Option | Default | Description | | ------------------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--dotenv=<path>...` | | Path to `.env` file to load, relative to the root directory. Can be repeated, with later files taking precedence. | | `-e, --extends=<layer-name>...` | | Extend from a Nuxt layer | | `--name=<name>` | `default` | Name of the analysis | | `--serve` | `true` | Serve the analysis results | | `--no-serve` | | Skip serving the analysis results | | `--prerender` | `false` | Prerender routes while analyzing | | <!--/analyze-opts--> | | | ::note This command sets `process.env.NODE_ENV` to `production`. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/analyze.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt build" description: "Build your Nuxt application." canonical_url: "https://nuxt.com/docs/5.x/api/commands/build" --- # nuxt build > Build your Nuxt application. <!--build-cmd--> ```bash [Terminal] npx nuxt build [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--prerender] [--target=<target>] [--dotenv=<path>...] [--envName=<environment>] [-e, --extends=<layer-name>...] [--profile=<verbose>] ``` <!--/build-cmd--> The `build` command creates a `.output` directory with all your application, server and dependencies ready for production, and reports how long the build took. ## Arguments <!--build-args--> | Argument | Description | | ------------------ | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/build-args--> | | ## Options <!--build-opts--> | Option | Default | Description | | ------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--prerender` | | Build Nuxt and prerender static routes | | `--target=<target>` | | Deploy target for the configured server builder (e.g. `node-server`, `vercel`, `netlify`) | | `--dotenv=<path>...` | | Path to `.env` file to load, relative to the root directory. Can be repeated, with later files taking precedence. | | `--envName=<environment>` | | The environment to use when resolving configuration overrides (default is `production` when building, and `development` when running the dev server) | | `-e, --extends=<layer-name>...` | | Extend from a Nuxt layer | | `--profile=<verbose>` | | Profile performance, writing a V8 CPU profile and a JSON report on exit. Use `--profile=verbose` for a full console report. | | <!--/build-opts--> | | | ::note This command sets `process.env.NODE_ENV` to `production`. :: ::note `--prerender` will always set the target to `static` :: `--preset` is still accepted as another name for `--target`. When neither is passed, the `NITRO_PRESET` and `SERVER_PRESET` environment variables are used, in that order. `--profile` writes a V8 CPU profile to `nuxt-build.cpuprofile` in your project. The build timings it reports, along with `perf-report.json` and `perf-trace.json` in your build directory, come from Nuxt's own build profiling and need Nuxt v4.4 or later. --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/build.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt build-module" description: "Nuxt command to build your Nuxt module before publishing." canonical_url: "https://nuxt.com/docs/5.x/api/commands/build-module" --- # nuxt build-module > Nuxt command to build your Nuxt module before publishing. <!--build-module-cmd--> ```bash [Terminal] npx nuxt build-module [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--build] [--stub] [--sourcemap] [--prepare] ``` <!--/build-module-cmd--> The `build-module` command runs `@nuxt/module-builder` to generate `dist` directory within your `rootDir` that contains the full build for your **nuxt-module**. ## Arguments <!--build-module-args--> | Argument | Description | | ------------------------- | ---------------------------------------------- | | `ROOTDIR="."` | Specifies the working directory (default: `.`) | | <!--/build-module-args--> | | ## Options <!--build-module-opts--> | Option | Default | Description | | ------------------------------------ | ------- | -------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the working directory, this takes precedence over ROOTDIR (default: `.`) | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--build` | `false` | Build module for distribution | | `--stub` | `false` | Stub dist instead of actually building it for development | | `--sourcemap` | `false` | Generate sourcemaps | | `--prepare` | `false` | Prepare module for local development | | <!--/build-module-opts--> | | | ::read-more{to="https://github.com/nuxt/module-builder" icon="i-simple-icons-github" target="\_blank"} Read more about `@nuxt/module-builder`. :: --- - [Source](https://github.com/nuxt/module-builder/blob/main/src/cli.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt cleanup" description: "Remove common generated Nuxt files and caches." canonical_url: "https://nuxt.com/docs/5.x/api/commands/cleanup" --- # nuxt cleanup > Remove common generated Nuxt files and caches. <!--cleanup-cmd--> ```bash [Terminal] npx nuxt cleanup [ROOTDIR] [--cwd=<directory>] ``` <!--/cleanup-cmd--> The `cleanup` command removes common generated Nuxt files and caches, including: - your build directory, which is `.nuxt` unless [`buildDir`](https://nuxt.com/docs/5.x/api/nuxt-config#builddir) says otherwise - `.output` - `dist` - `node_modules/.vite` - `node_modules/.cache` ## Arguments <!--cleanup-args--> | Argument | Description | | -------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/cleanup-args--> | | ## Options <!--cleanup-opts--> | Option | Default | Description | | -------------------- | ------- | ----------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | <!--/cleanup-opts--> | | | --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/cleanup.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt curl" description: "The curl command sends an HTTP request to your running Nuxt dev server." canonical_url: "https://nuxt.com/docs/5.x/api/commands/curl" --- # nuxt curl > The curl command sends an HTTP request to your running Nuxt dev server. <!--curl-cmd--> ```bash [Terminal] npx nuxt curl <URL> [ROOTDIR] [--cwd=<directory>] [-X, --method=<method>] [-H, --header=<header>...] [-d, --data=<data>] [-i, --include] [-I, --head] [-v, --verbose] [--pretty] ``` <!--/curl-cmd--> The `curl` command sends an HTTP request to the `nuxt dev` server running for your project and prints the response. A path is resolved against that server, so you do not need to know which port it ended up on. ![nuxt curl](https://raw.githubusercontent.com/nuxt/cli/main/capture/output/nuxt-curl.svg) ## Arguments <!--curl-args--> | Argument | Description | | ----------------- | --------------------------------------------------------------- | | `URL=<url\|path>` | Absolute URL, or a path resolved against the running dev server | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/curl-args--> | | ## Options <!--curl-opts--> | Option | Default | Description | | -------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `-X, --method=<method>` | | HTTP method (default: GET, or POST when a body is provided) | | `-H, --header=<header>...` | | Request header in `Name: Value` form. Can be repeated. | | `-d, --data=<data>` | | Request body. Use `@-` to read stdin and `@<file>` to read a file. | | `-i, --include` | | Include the response status line and headers in the output | | `-I, --head` | | Send a `HEAD` request and show only the response headers | | `-v, --verbose` | | Print request and response headers to stderr | | `--pretty` | | Reindent and syntax-highlight output (default: on for a terminal, off when piped). Use `--pretty`/`--no-pretty` to force reindenting. | | <!--/curl-opts--> | | | The dev server is found through the `nuxt.lock` file it writes into your [build directory](https://nuxt.com/docs/5.x/directory-structure/nuxt), so the command only works while `nuxt dev` is running for that project. Pass an absolute URL to talk to any other server. **Examples:** ```bash [Terminal] # Request a path on the running dev server npx nuxt curl /api/hello # Include the status line and response headers npx nuxt curl /api/hello -i # Send JSON npx nuxt curl /api/todos -X POST -H "Content-Type: application/json" -d '{"title":"Buy milk"}' # Read the body from a file, or from stdin with `@-` npx nuxt curl /api/todos -d @todo.json ``` JSON, HTML, XML and other textual responses are reindented and syntax-highlighted when the output is a terminal, and passed through byte for byte when it is piped. Use `--no-pretty` to opt out, or `--pretty` to force it on when piping. ::note The command exits with `22` when the server answers with an HTTP error status, matching `curl --fail`. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/curl.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt dev" description: "The dev command starts a development server with hot module replacement at http://localhost:3000" canonical_url: "https://nuxt.com/docs/5.x/api/commands/dev" --- # nuxt dev > The dev command starts a development server with hot module replacement at http://localhost:3000 <!--dev-cmd--> ```bash [Terminal] npx nuxt dev [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--dotenv=<path>...] [--envName=<environment>] [-e, --extends=<layer-name>...] [--inspect] [--inspect-brk] [--tui] [--clear] [-f, --fork] [-p, --port=<port>] [--takeover] [--strictPort] [-h, --host=<host>] [-o, --open] [--open.url=<url|path>] [--clipboard] [--qr] [--tunnel] [--public] [--publicURL=<url>] [--https] [--https.cert=<path>] [--https.key=<path>] [--https.pfx=<path>] [--https.passphrase=<passphrase>] [--https.validityDays=<days>] [--https.domains=<domain>...] [--profile=<verbose>] ``` <!--/dev-cmd--> The `dev` command starts a development server with hot module replacement at <http://localhost:3000> ![nuxt dev](https://raw.githubusercontent.com/nuxt/cli/main/capture/output/nuxt-dev-static.svg) ## Arguments <!--dev-args--> | Argument | Description | | ---------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/dev-args--> | | ## Options <!--dev-opts--> | Option | Default | Description | | ------------------------------------ | ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--dotenv=<path>...` | | Path to `.env` file to load, relative to the root directory. Can be repeated, with later files taking precedence. | | `--envName=<environment>` | | The environment to use when resolving configuration overrides (default is `production` when building, and `development` when running the dev server) | | `-e, --extends=<layer-name>...` | | Extend from a Nuxt layer | | `--inspect` | | Enable the Node.js inspector for server code (`--inspect=[host:]port`), and for the CLI process on the next port | | `--inspect-brk` | | Like `--inspect`, and wait for a debugger to attach to the CLI process before loading Nuxt (`--inspect-brk=[host:]port`) | | `--tui` | `true` | Interactive terminal UI (pinned status panel, folded logs and single-key shortcuts) | | `--no-tui` | | Disable the interactive terminal UI and stream logs instead | | `--clear` | `false` | Clear console on restart | | `-f, --fork` | runtime-dependent | Serve the app from a forked child process (on by default wherever the runtime supports it) | | `--no-fork` | | Disable forked mode | | `-p, --port=<port>` | | Port to listen on (default: `NUXT_PORT \|\| NITRO_PORT \|\| PORT \|\| nuxtOptions.devServer.port`) | | `--takeover` | | Stop a dev server already running on this project and take its place | | `--no-takeover` | | Never stop a dev server already running on this project | | `--strictPort` | `false` | Exit if the requested port is unavailable instead of using another one | | `-h, --host=<host>` | | Host to listen on (default: `NUXT_HOST \|\| NITRO_HOST \|\| HOST \|\| nuxtOptions.devServer?.host`) | | `-o, --open` | `false` | Open the URL in the browser | | `--open.url=<url\|path>` | | Path or URL to open instead of the dev server root | | `--clipboard` | `false` | Copy the URL to the clipboard | | `--qr` | | Print a QR code for the public URL (enabled by default when one is available) | | `--tunnel` | | Expose the server via a Cloudflare quick tunnel | | `--public` | | Listen on all network interfaces and allow any host to connect | | `--publicURL=<url>` | | Public URL to display (used for QR code and clipboard) | | `--https` | | Enable HTTPS with a locally-trusted development certificate | | `--https.cert=<path>` | | Path to TLS certificate | | `--https.key=<path>` | | Path to TLS key | | `--https.pfx=<path>` | | Path to PKCS#12 (.p12/.pfx) keystore | | `--https.passphrase=<passphrase>` | | Passphrase for the TLS key or keystore | | `--https.validityDays=<days>` | | Validity in days for a generated self-signed certificate | | `--https.domains=<domain>...` | | Domain for a generated certificate. Can be repeated, or given as a comma-separated list. | | `--profile=<verbose>` | | Profile performance, writing a V8 CPU profile and a JSON report on exit. Use `--profile=verbose` for a full console report. | | <!--/dev-opts--> | | | The port and host can also be set via `NUXT_PORT`, `NITRO_PORT`, `PORT`, `NUXT_HOST`, `NITRO_HOST` or `HOST` environment variables. This command sets `process.env.NODE_ENV` to `development`. ## Interactive terminal UI In an interactive terminal, `nuxt dev` renders a pinned panel: the server URLs, startup progress, the current status and a row of shortcuts, with logs folded above it. It falls back to a plain stream of logs when the output is not a terminal, in CI, when a debugger is attached, or when the terminal is too small. | Key | Action | | --------- | --------------------------------------------- | | `r` | Restart the dev server | | `shift-r` | Restart with a cleared cache | | `o` | Open the app in your browser | | `y` | Copy the server URL to the clipboard | | `i` | Show versions, URLs, QR code and session info | | `l` | Browse the log history | | `e` | Open the logs at the last error | | `n` | Browse served requests | | `p` | Browse pages and server routes | | `c` | Clear logs, requests and the console | | `?` | Show all shortcuts | | `q` | Quit | Inside a view, `y` copies the selected row and `shift-y` copies every row the filters and search leave, keeping the newest when there is too much to paste. In the info view, `shift-y` copies the [`nuxt info`](https://nuxt.com/docs/5.x/api/commands/info) table instead. ### Tracing a request In the network view (`n`), select a request and press `enter` to see where its time went. The trace draws everything the server did for that request on one timeline: route middleware, server route handlers, Nuxt plugins, hooks, data fetching, rendering, and requests your app made to other servers. Below the timeline, it lists how long Vite spent compiling modules for the request, broken down by Vite plugin and slowest module, followed by the logs the request produced. The trace is built from the [tracing channels](https://nodejs.org/api/diagnostics_channel.html#class-tracingchannel) that Nuxt and Nitro publish, so how much it shows depends on your versions. `nuxt dev` turns on the `tracingChannel` option for you; set `tracingChannel: false` in your `nuxt.config` to opt out. ### Disabling the UI Pass `--no-tui` or set `NUXT_TUI=plain` to stream logs instead. You can also force the UI *on* by passing `NUXT_TUI=1` (though this doesn't do anything if the output is piped or redirected). ![nuxt dev with plain output](https://raw.githubusercontent.com/nuxt/cli/main/capture/output/nuxt-dev-plain-static.svg) The plain output offers a smaller set of shortcuts: `r` to restart, `o` to open, `u` to show the URLs, `qr` for a QR code, `copy` to copy the URL, `c` to clear the console, `h` for help and `q` to quit. ## Restarts When the dev server reloads or restarts it says what caused it, and a change to `nuxt.config` also lists the keys that actually differ, so a restart triggered by a formatting-only edit is distinguishable from one that changed your configuration. A reload happens in place; a restart replaces the process, which is what a change to `nuxt.config` or to an installed dependency needs. ## Taking over a running dev server A dev server records itself in `nuxt.lock` inside your [build directory](https://nuxt.com/docs/5.x/directory-structure/nuxt), so a second `nuxt dev` for the same project reports the one already running instead of racing it for the port. Pass `--takeover` to stop it and start in its place, or set `NUXT_IGNORE_LOCK=1` to run a second server anyway (unsupported). With `--open`, the prompt also offers to open the running server in your browser without stopping it, which is the default when that server was started in a terminal. `--open.url` is resolved against the running server's URL. The same lock file is how [`nuxt curl`](https://nuxt.com/docs/5.x/api/commands/curl) and [`nuxt task`](https://nuxt.com/docs/5.x/api/commands/task) find the server to talk to. ## HTTPS `--https` serves over TLS, generating a locally-trusted certificate with `mkcert` when it is installed and a self-signed one otherwise. `--https.cert` and `--https.key` use a certificate you already have, and `--https.pfx` with `--https.passphrase` a PKCS#12 keystore. ::note Node does not read your system trust store, so requests made from Node to a server using a generated certificate will not trust it. Set `NODE_EXTRA_CA_CERTS` to the certificate authority that issued it. :: ## Debugging `--inspect` opens the Node.js inspector for your server code: server routes, middleware and server-side rendering, which run in a Nitro worker thread. It accepts an optional `[host:]port` and defaults to `127.0.0.1:9229`, so Chrome DevTools (`chrome://inspect`) and most editors find it without configuration. Your debugger reconnects automatically when the server reloads. ### Debugging the CLI `nuxt.config`, modules and build hooks run in the CLI process instead, which gets its own inspector on the next port (`9230` by default). Add `localhost:9230` as a target in `chrome://inspect` (**Configure...**) or point your editor at it. To debug code that runs while Nuxt is loading, use `--inspect-brk`. The CLI process then waits for a debugger to attach to the next port before loading Nuxt: ```bash npx nuxt dev --inspect-brk ``` `--profile` writes a V8 CPU profile to `nuxt-dev.cpuprofile` in your project when the process exits. `--profile=verbose` also prints a full report to the console. ::note The build timings, `perf-report.json` and `perf-trace.json` come from Nuxt's own build profiling, which needs Nuxt v4.4 or later. On an earlier version the CPU profile is still written. :: ## Environment variables | Variable | Purpose | | --------------------------------- | -------------------------------------------------------------------- | | `NUXT_PORT`, `NITRO_PORT`, `PORT` | Port to listen on, in that order of precedence | | `NUXT_HOST`, `NITRO_HOST`, `HOST` | Host to listen on, in that order of precedence | | `NUXT_TUI` | `1` to force the interactive UI on, `plain` to opt out | | `NUXT_TERM_THEME` | `light` or `dark`, when the terminal's background cannot be detected | | `NUXT_IGNORE_LOCK` | `1` to ignore a dev server already running for this project | | `NUXT_IGNORE_UPDATE_CHECK` | `1` to stop the CLI checking for newer Nuxt releases | --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/dev.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt devtools" description: "The devtools command allows you to enable or disable Nuxt DevTools on a per-project basis." canonical_url: "https://nuxt.com/docs/5.x/api/commands/devtools" --- # nuxt devtools > The devtools command allows you to enable or disable Nuxt DevTools on a per-project basis. <!--devtools-cmd--> ```bash [Terminal] npx nuxt devtools <COMMAND> [ROOTDIR] [--cwd=<directory>] ``` <!--/devtools-cmd--> Running `nuxt devtools enable` will install the Nuxt DevTools globally, and also enable it within the particular project you are using. It is saved as a preference in your user-level `.nuxtrc`. If you want to remove devtools support for a particular project, you can run `nuxt devtools disable`. ## Arguments <!--devtools-args--> | Argument | Description | | --------------------------- | ---------------------------------------------------- | | `COMMAND=<enable\|disable>` | Command to run | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/devtools-args--> | | ## Options <!--devtools-opts--> | Option | Default | Description | | --------------------- | ------- | ----------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | <!--/devtools-opts--> | | | ::read-more{icon="i-simple-icons-nuxtdotjs" to="https://devtools.nuxt.com" target="\_blank"} Read more about the **Nuxt DevTools**. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/devtools.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt docs" description: "The docs command searches the Nuxt documentation for the version your project uses." canonical_url: "https://nuxt.com/docs/5.x/api/commands/docs" --- # nuxt docs > The docs command searches the Nuxt documentation for the version your project uses. <!--docs-cmd--> ```bash [Terminal] npx nuxt docs [QUERY] [--cwd=<directory>] [--open] ``` <!--/docs-cmd--> The `docs` command searches the Nuxt documentation and opens the best match in your browser. Without a query it opens the documentation home page. ## Arguments <!--docs-args--> | Argument | Description | | ----------------- | ------------------------------------- | | `QUERY` | Words to search the documentation for | | <!--/docs-args--> | | ## Options <!--docs-opts--> | Option | Default | Description | | ------------------- | ------- | -------------------------------------------------- | | `--cwd=<directory>` | `.` | Specify the root directory of your Nuxt project | | `--open` | `true` | Open the best match in a browser | | `--no-open` | | Print the matching pages without opening a browser | | <!--/docs-opts--> | | | Results are ranked by page title, then by section heading, then by description, and the matches are printed before the best one is opened. When several pages match and the terminal is interactive, you are asked which one to open. ```bash [Terminal] npx nuxt docs "server routes" ``` The search runs against the documentation for the Nuxt version the project depends on rather than whatever is currently published, so the answers match the version you are using. `@nuxt/docs` is used when it is installed in the project, and downloaded otherwise. --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/docs.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt generate" description: "Pre-renders every route of the application and stores the result in plain HTML files." canonical_url: "https://nuxt.com/docs/5.x/api/commands/generate" --- # nuxt generate > Pre-renders every route of the application and stores the result in plain HTML files. <!--generate-cmd--> ```bash [Terminal] npx nuxt generate [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--target=<target>] [--dotenv=<path>...] [--envName=<environment>] [-e, --extends=<layer-name>...] [--profile=<verbose>] ``` <!--/generate-cmd--> The `generate` command pre-renders every route of your application and stores the result in plain HTML files that you can deploy on any static hosting services. The command triggers the `nuxt build` command with the `prerender` argument set to `true` ## Arguments <!--generate-args--> | Argument | Description | | --------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/generate-args--> | | ## Options <!--generate-opts--> | Option | Default | Description | | ------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--target=<target>` | | Deploy target for the configured server builder (e.g. `node-server`, `vercel`, `netlify`) | | `--dotenv=<path>...` | | Path to `.env` file to load, relative to the root directory. Can be repeated, with later files taking precedence. | | `--envName=<environment>` | | The environment to use when resolving configuration overrides (default is `production` when building, and `development` when running the dev server) | | `-e, --extends=<layer-name>...` | | Extend from a Nuxt layer | | `--profile=<verbose>` | | Profile performance, writing a V8 CPU profile and a JSON report on exit. Use `--profile=verbose` for a full console report. | | <!--/generate-opts--> | | | ::read-more{to="https://nuxt.com/docs/5.x/getting-started/deployment#static-hosting"} Read more about pre-rendering and static hosting. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/generate.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt info" description: "The info command logs information about the current or specified Nuxt project." canonical_url: "https://nuxt.com/docs/5.x/api/commands/info" --- # nuxt info > The info command logs information about the current or specified Nuxt project. <!--info-cmd--> ```bash [Terminal] npx nuxt info [ROOTDIR] [--cwd=<directory>] [--json] ``` <!--/info-cmd--> The `info` command logs information about the current or specified Nuxt project: the versions of Nuxt, Nitro and Vite it resolves, the package manager, the modules it loads and the build modules in its configuration. Use `--json` when you want to read it from a script. ## Arguments <!--info-args--> | Argument | Description | | ----------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/info-args--> | | ## Options <!--info-opts--> | Option | Default | Description | | ------------------- | ------- | ----------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--json` | | Print project info as JSON | | <!--/info-opts--> | | | --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/info.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "create nuxt" description: "The init command initializes a fresh Nuxt project." canonical_url: "https://nuxt.com/docs/5.x/api/commands/init" --- # create nuxt > The init command initializes a fresh Nuxt project. <!--init-cmd--> ```bash [Terminal] npm create nuxt@latest [DIR] -- [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [-t, --template=<template-name>] [-f, --force] [--offline] [--preferOffline] [--install] [--gitInit] [--shell] [--packageManager=<npm|pnpm|yarn|bun|deno|aube|nub|upm>] [-M, --modules=<module-names>] [--nightly=<dist-tag>] ``` <!--/init-cmd--> The `create-nuxt` command initializes a fresh Nuxt project using [unjs/giget](https://github.com/unjs/giget). It asks which template to start from, whether to install modules, and which package manager to use, then installs dependencies and prints the commands to run next. ![npm create nuxt](https://raw.githubusercontent.com/nuxt/cli/main/capture/output/nuxt-init.svg) ::note `nuxt init` was removed from `@nuxt/cli`. Use `npm create nuxt@latest`, or the equivalent for your package manager. :: ## Arguments <!--init-args--> | Argument | Description | | ----------------- | ----------------- | | `DIR=""` | Project directory | | <!--/init-args--> | | ## Options <!--init-opts--> | Option | Default | Description | | --------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | | `--cwd=<directory>` | `.` | Specify the directory to create the project in | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `-t, --template=<template-name>` | | Template name | | `-f, --force` | | Override existing directory | | `--offline` | | Force offline mode | | `--preferOffline` | | Prefer offline mode | | `--install` | `true` | Install dependencies once the project has been scaffolded | | `--no-install` | | Skip installing dependencies | | `--gitInit` | | Initialize git repository | | `--no-gitInit` | | Skip git repository initialization | | `--shell` | | Start shell after installation in project directory | | `--packageManager=<npm\|pnpm\|yarn\|bun\|deno\|aube\|nub\|upm>` | | Package manager choice | | `-M, --modules=<module-names>` | | Nuxt modules to install (comma separated without spaces) | | `--no-modules` | | Skip module installation prompt | | `--nightly=<dist-tag>` | | Use Nuxt nightly release channel (a `nuxt-nightly` dist tag, defaulting to `latest`) | | <!--/init-opts--> | | | ## Non-interactive use Without a terminal to prompt in, the answers it would have asked for have to be passed as arguments: the directory, `--template`, `--packageManager` and `--gitInit`. Anything missing is reported along with the available templates, and the command exits with `2`. ```bash [Terminal] npm create nuxt@latest my-app -- --template minimal --packageManager pnpm --no-gitInit ``` ::note The `--` is needed with `npm create`, which reads anything flag-shaped after the initializer as npm's own config and warns `Unknown cli config` instead of passing it on. Other package managers forward the whole command line as it is, so pass the flags to them directly: `pnpm create nuxt@latest my-app --template minimal`. :: ## Environment Variables - `NUXI_INIT_REGISTRY`: Set to a custom template registry. ([learn more](https://github.com/unjs/giget#custom-registry)). - Default registry is loaded from [nuxt/starter/templates](https://github.com/nuxt/starter/tree/templates/templates) --- - [Source](https://github.com/nuxt/cli/blob/main/packages/create-nuxt/src/init.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt module" description: "Search, add and remove modules in your Nuxt application with the command line." canonical_url: "https://nuxt.com/docs/5.x/api/commands/module" --- # nuxt module > Search, add and remove modules in your Nuxt application with the command line. Nuxt provides a few utilities to work with [Nuxt modules](https://nuxt.com/modules) seamlessly. ## `nuxt module add` <!--module-add-cmd--> ```bash [Terminal] npx nuxt module add <MODULENAME...> [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--skipInstall] [--skipConfig] [--dev] [--packageManager=<npm|pnpm|yarn|bun|deno|aube|nub|upm>] ``` <!--/module-add-cmd--> ### Arguments <!--module-add-args--> | Argument | Description | | ----------------------- | ------------------------------------------------------------------- | | `MODULENAME...` | Specify one or more modules to install by name, separated by spaces | | <!--/module-add-args--> | | ### Options <!--module-add-opts--> | Option | Default | Description | | --------------------------------------------------------------- | ------- | ----------------------------------------------- | | `--cwd=<directory>` | `.` | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--skipInstall` | | Skip npm install | | `--skipConfig` | | Skip nuxt.config.ts update | | `--dev` | | Install modules as dev dependencies | | `--packageManager=<npm\|pnpm\|yarn\|bun\|deno\|aube\|nub\|upm>` | | Package manager to install with | | <!--/module-add-opts--> | | | The command lets you install [Nuxt modules](https://nuxt.com/modules) in your application with no manual work. When running the command, it will: - install the module as a dependency using your package manager - add it to your [package.json](https://nuxt.com/docs/5.x/directory-structure/package) file - update your [`nuxt.config`](https://nuxt.com/docs/5.x/directory-structure/nuxt-config) file **Example:** Installing the [`Pinia`](https://nuxt.com/modules/pinia) module ```bash [Terminal] npx nuxt module add pinia ``` Run it without a module name to pick from the modules compatible with your Nuxt version. ::note [`nuxt add`](https://nuxt.com/docs/5.x/api/commands/add) is the same command, and also accepts layers. :: ## `nuxt module remove` <!--module-remove-cmd--> ```bash [Terminal] npx nuxt module remove [MODULENAME...] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--skipInstall] [--skipConfig] ``` <!--/module-remove-cmd--> ### Arguments <!--module-remove-args--> | Argument | Description | | -------------------------- | ------------------------------------------------------------------ | | `MODULENAME...` | Specify one or more modules to remove by name, separated by spaces | | <!--/module-remove-args--> | | ### Options <!--module-remove-opts--> | Option | Default | Description | | ------------------------------------ | ------- | ----------------------------------------------- | | `--cwd=<directory>` | `.` | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--skipInstall` | | Skip dependency uninstall | | `--skipConfig` | | Skip nuxt.config.ts update | | <!--/module-remove-opts--> | | | The command uninstalls the package and removes it from the `modules` array in your [`nuxt.config`](https://nuxt.com/docs/5.x/directory-structure/nuxt-config). **Example:** ```bash [Terminal] npx nuxt module remove pinia ``` Run it without a module name to pick from the modules the project currently uses. ## `nuxt module search` <!--module-search-cmd--> ```bash [Terminal] npx nuxt module search <QUERY> [--cwd=<directory>] [--nuxtVersion=<3|4|4.2.0>] [--json] ``` <!--/module-search-cmd--> ![nuxt module search](https://raw.githubusercontent.com/nuxt/cli/main/capture/output/nuxt-module-search.svg) ### Arguments <!--module-search-args--> | Argument | Description | | -------------------------- | ---------------------- | | `QUERY` | keywords to search for | | <!--/module-search-args--> | | ### Options <!--module-search-opts--> | Option | Default | Description | | ----------------------------- | ------- | ---------------------------------------------------------------------------------- | | `--cwd=<directory>` | `.` | Specify the root directory of your Nuxt project | | `--nuxtVersion=<3\|4\|4.2.0>` | | Filter by Nuxt version and list compatible modules only (auto detected by default) | | `--json` | | Print output as JSON | | <!--/module-search-opts--> | | | The command searches for Nuxt modules matching your query that are compatible with your Nuxt version, showing each module's description with the matched text highlighted. **Example:** ```bash [Terminal] npx nuxt module search pinia ``` Use `--json` to get the results as machine-readable JSON. --- - [Source](https://github.com/nuxt/cli/tree/main/packages/nuxt-cli/src/commands/module) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "Nuxt CLI" description: "The Nuxt CLI runs, builds and inspects your Nuxt application from the command line." canonical_url: "https://nuxt.com/docs/5.x/api/commands/overview" --- # Nuxt CLI > The Nuxt CLI runs, builds and inspects your Nuxt application from the command line. `@nuxt/cli` is the `nuxt` command: it runs the dev server, builds your application for production, scaffolds files, manages modules and answers questions about your project. It is a dependency of `nuxt`, so a Nuxt project already has it. ```bash [Terminal] npx nuxt dev ``` The binary is installed as `nuxt`, and also as `nuxi`, `nuxi-ng` and `nuxt-cli` for compatibility with older setups. Scaffolding a new project is a separate package, [`create-nuxt`](https://nuxt.com/docs/5.x/api/commands/init), so that it can be run without a project to install into. `nuxi` is a third package: a standalone CLI with no dependencies, for running commands outside a project or installing globally. ## Requirements The CLI runs on Node v22.21+, v24.11+ or v26+, and imports TypeScript configuration natively on those versions. Nuxt 2 and `@nuxt/bridge` are not supported. ## Running commands Inside a project, your package manager runs the version the project depends on: ```bash [Terminal] pnpm nuxt dev npm exec nuxt dev yarn nuxt dev bun nuxt dev ``` Outside a project, or installed globally, `npx nuxi` runs the same commands. It hands each one over to the `@nuxt/cli` installed in the project when there is one, so the code that runs matches the version of Nuxt it sits alongside rather than whatever is on your machine. It falls back to its own bundled commands when there is no project CLI, when the project's copy predates v3.26, or when the project's copy does not know the command, so a project pinned to an older release still gets newer commands. ## Global options `--cwd` may be passed before the command name as well as after it, which is useful in a monorepo: ```bash [Terminal] npx nuxt --cwd apps/web dev ``` Commands that take a `ROOTDIR` positional treat an explicit `--cwd` as an override of it. `--help` on any command prints its arguments and options, and `--version` prints the CLI version. ## Mistyped commands and flags An unrecognised command is matched against the ones that exist, and an unrecognised flag against the ones the command declares. In an interactive terminal you are offered the correction to run instead; otherwise it is reported as a warning, so a misspelled flag no longer looks like a flag that simply had no effect. ## Project commands A command the CLI does not provide is looked up as a `nuxt-<command>` binary in your project, so a dependency can add a command of its own. This is how [`nuxt build-module`](https://nuxt.com/docs/5.x/api/commands/build-module) works: it runs the `nuxt-build-module` binary that `@nuxt/module-builder` installs. ## Shell completions `nuxt complete <shell>` prints a completion script, powered by [`@bomb.sh/tab`](https://github.com/bombshell-dev/tab). It completes commands, flags and some flag values: ports and hosts for `nuxt dev`, Nitro presets for `nuxt build --preset`, and log levels. ::code-group ```bash [zsh] npx nuxt complete zsh > "${fpath[1]}/_nuxt" ``` ```bash [bash] npx nuxt complete bash > /etc/bash_completion.d/nuxt ``` ```bash [fish] npx nuxt complete fish > ~/.config/fish/completions/nuxt.fish ``` :: `powershell` is also supported, and `create-nuxt complete <shell>` has its own script which completes starter template names. ::note Completions work through your package manager too (`pnpm nuxt <Tab>`), if you install [tab's package manager completions](https://github.com/bombshell-dev/tab?tab=readme-ov-file#package-manager-completions). :: ## Update notifications Once a day the CLI checks in the background whether a newer Nuxt release exists and, if so, prints a short nudge to run [`nuxt upgrade`](https://nuxt.com/docs/5.x/api/commands/upgrade) once the command has finished. The `latest` dist-tag is read from the registry configured in your `.npmrc` and cached in your user-level `.nuxtrc`. The nudge is not shown for prereleases, and not for a patch release within the same minor unless you are at least five patches behind. The check is skipped in CI, on StackBlitz, and when the output is not a terminal. Set `NUXT_IGNORE_UPDATE_CHECK=1` or `NO_UPDATE_NOTIFIER=1` to skip it for one run, or add `updateCheck.enabled=false` to your user-level `.nuxtrc` to opt out for good. ## Networks and proxies Commands that reach the network (installing a module, searching modules, downloading a template, checking for updates) report why a request failed rather than the raw error, and suggest the retry that is likely to work. Behind a proxy that re-signs TLS traffic that means `NODE_EXTRA_CA_CERTS`, pointing at your organisation's root certificate. Node only reads proxy environment variables when `NODE_USE_ENV_PROXY` is set, and the CLI passes that setting on to the processes it spawns, so a package manager install started by `nuxt add` sees the same proxy configuration you do. ## Prereleases Every commit on `main` is published to the nightly channel: ```bash [Terminal] npm install -D @nuxt/cli-nightly npx @nuxt/cli-nightly dev ``` `create-nuxt-nightly` scaffolds with it, and `npm create nuxt@latest -- --nightly` scaffolds a project against the Nuxt nightly channel. Builds for an individual pull request are published by [pkg.pr.new](https://pkg.pr.new) and linked from the pull request itself, which is the quickest way to check whether a fix works before it is released. --- - [Source](https://github.com/nuxt/cli) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt prepare" description: "The prepare command creates a .nuxt directory in your application and generates types." canonical_url: "https://nuxt.com/docs/5.x/api/commands/prepare" --- # nuxt prepare > The prepare command creates a .nuxt directory in your application and generates types. <!--prepare-cmd--> ```bash [Terminal] npx nuxt prepare [ROOTDIR] [--cwd=<directory>] [--dotenv=<path>...] [--logLevel=<silent|info|verbose>] [--envName=<environment>] [-e, --extends=<layer-name>...] ``` <!--/prepare-cmd--> The `prepare` command creates a [`.nuxt`](https://nuxt.com/docs/5.x/directory-structure/nuxt) directory in your application and generates types. This can be useful in a CI environment or as a `postinstall` command in your [`package.json`](https://nuxt.com/docs/5.x/directory-structure/package). ## Arguments <!--prepare-args--> | Argument | Description | | -------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/prepare-args--> | | ## Options <!--prepare-opts--> | Option | Default | Description | | ------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--dotenv=<path>...` | | Path to `.env` file to load, relative to the root directory. Can be repeated, with later files taking precedence. | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--envName=<environment>` | | The environment to use when resolving configuration overrides (default is `production` when building, and `development` when running the dev server) | | `-e, --extends=<layer-name>...` | | Extend from a Nuxt layer | | <!--/prepare-opts--> | | | ::note This command sets `process.env.NODE_ENV` to `production`. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/prepare.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt preview" description: "The preview command starts a server to preview your application after the build command." canonical_url: "https://nuxt.com/docs/5.x/api/commands/preview" --- # nuxt preview > The preview command starts a server to preview your application after the build command. <!--preview-cmd--> ```bash [Terminal] npx nuxt preview [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--envName=<environment>] [-e, --extends=<layer-name>...] [-p, --port=<port>] [-h, --host=<host>] [--takeover] [--strictPort] [--dotenv=<path>...] ``` <!--/preview-cmd--> The `preview` command starts a server to preview your Nuxt application after running the `build` command. `nuxt start` is the same command under another name. When running your application in production refer to the [Deployment section](https://nuxt.com/docs/5.x/getting-started/deployment). When the configured `server.builder` produces no server at all (a client-only build, for example), there is nothing to run: the static output is served directly instead, with unmatched paths falling back to the client entry so client-side routing works. Some Nitro presets do not produce a server that can be run locally. For those, the preset's own preview command is run instead, and the command tells you what it is running. ## Arguments <!--preview-args--> | Argument | Description | | -------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/preview-args--> | | ## Options <!--preview-opts--> | Option | Default | Description | | ------------------------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--envName=<environment>` | | The environment to use when resolving configuration overrides (default is `production` when building, and `development` when running the dev server) | | `-e, --extends=<layer-name>...` | | Extend from a Nuxt layer | | `-p, --port=<port>` | | Port to listen on (default: `NUXT_PORT \|\| NITRO_PORT \|\| PORT`) | | `-h, --host=<host>` | | Host to listen on (default: `NUXT_HOST \|\| NITRO_HOST \|\| HOST`) | | `--takeover` | | Stop a preview server already running on this project and take its place | | `--no-takeover` | | Never stop a preview server already running on this project | | `--strictPort` | `false` | Exit if the requested port is unavailable instead of using another one | | `--dotenv=<path>...` | | Path to `.env` file to load, relative to the root directory. Can be repeated, with later files taking precedence. | | <!--/preview-opts--> | | | ## Taking over a running preview A preview records itself in `node_modules/.cache/nuxt/preview`, so a second `nuxt preview` for the same project deals with the one already running the way [`nuxt dev`](https://nuxt.com/docs/5.x/api/commands/dev#taking-over-a-running-dev-server) deals with a running dev server: | Running preview was started | New preview is started | What happens | | --------------------------- | ---------------------- | --------------------------------------------------- | | without a terminal | without a terminal | It is stopped, and the new one takes its port. | | without a terminal | in a terminal | You are asked, defaulting to taking it over. | | in a terminal | without a terminal | The new one exits, saying where the running one is. | | in a terminal | in a terminal | You are asked, defaulting to not starting. | Pass `--takeover` to always stop it and start in its place, or `--no-takeover` to never do so. Choosing "Start anyway" at the prompt, or passing a different `--port`, runs a second preview alongside it instead. When another server, including `nuxt dev`, is using the requested port, the preview automatically uses another free port without stopping that server. Pass `--strictPort` to exit instead. The requested port comes from `--port`, `NUXT_PORT`, `NITRO_PORT`, or `PORT`, defaulting to `3000`. Port selection applies to static previews and preview commands that run directly with Node.js, Bun, or Deno. Other preview commands choose their own port. This command sets `process.env.NODE_ENV` to `production`. To override, define `NODE_ENV` in a `.env` file or as command-line argument. ::note For convenience, in preview mode, your [`.env`](https://nuxt.com/docs/5.x/directory-structure/env) file will be loaded into `process.env`. (However, in production you will need to ensure your environment variables are set yourself. For example, with Node.js 20+ you could do this by running `NODE_ENV=production node --env-file .env .output/server/index.mjs` to start your server.) :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/preview.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt task" description: "List and run Nitro tasks on your dev server." canonical_url: "https://nuxt.com/docs/5.x/api/commands/task" --- # nuxt task > List and run Nitro tasks on your dev server. The `task` command talks to the [Nitro tasks](https://nitro.build/guide/tasks) your project exposes, using the `nuxt dev` server running for it. Pass `--url` to target another server. ## `nuxt task list` <!--task-list-cmd--> ```bash [Terminal] npx nuxt task list [ROOTDIR] [--cwd=<directory>] [--url=<url>] [--json] ``` <!--/task-list-cmd--> ![nuxt task list](https://raw.githubusercontent.com/nuxt/cli/main/capture/output/nuxt-task-list.svg) ### Arguments <!--task-list-args--> | Argument | Description | | ---------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/task-list-args--> | | ### Options <!--task-list-opts--> | Option | Default | Description | | ---------------------- | ------- | ------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--url=<url>` | | URL of the Nuxt server to talk to (default: the running dev server) | | `--json` | | Print output as JSON | | <!--/task-list-opts--> | | | ## `nuxt task run` <!--task-run-cmd--> ```bash [Terminal] npx nuxt task run <NAME> [ROOTDIR] [--cwd=<directory>] [--url=<url>] [--payload=<json>] ``` <!--/task-run-cmd--> ### Arguments <!--task-run-args--> | Argument | Description | | --------------------- | ---------------------------------------------------- | | `NAME=<name>` | Name of the task to run | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/task-run-args--> | | ### Options <!--task-run-opts--> | Option | Default | Description | | --------------------- | ------- | ----------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--url=<url>` | | URL of the Nuxt server to talk to (default: the running dev server) | | `--payload=<json>` | | Task payload, either as a JSON object or as `--payload.key=value` pairs | | <!--/task-run-opts--> | | | The task's result is printed as JSON. A payload can be given as a single JSON object or built up from individual keys: ```bash [Terminal] npx nuxt task run db:seed --payload '{"count":10}' npx nuxt task run db:seed --payload.count=10 ``` ::note Nitro only scans your `tasks` directory when tasks are enabled, so a server with none exposed may just need this: ```ts [nuxt.config.ts] export default defineNuxtConfig({ nitro: { experimental: { tasks: true, }, }, }) ``` :: ::read-more{to="https://nitro.build/guide/tasks" icon="i-simple-icons-nitro" target="\_blank"} Read more about Nitro tasks. :: --- - [Source](https://github.com/nuxt/cli/tree/main/packages/nuxt-cli/src/commands/task) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt test" description: "The test command runs tests using @nuxt/test-utils." canonical_url: "https://nuxt.com/docs/5.x/api/commands/test" --- # nuxt test > The test command runs tests using @nuxt/test-utils. <!--test-cmd--> ```bash [Terminal] npx nuxt test [ROOTDIR] [--cwd=<directory>] [--dev] [--watch] ``` <!--/test-cmd--> The `test` command runs tests using [`@nuxt/test-utils`](https://nuxt.com/docs/5.x/getting-started/testing). This command sets `process.env.NODE_ENV` to `test` if not already set. ## Arguments <!--test-args--> | Argument | Description | | ----------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/test-args--> | | ## Options <!--test-opts--> | Option | Default | Description | | ------------------- | ------- | ----------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--dev` | | Run in dev mode | | `--watch` | | Watch mode | | <!--/test-opts--> | | | ::note This command sets `process.env.NODE_ENV` to `test`. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/test.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt typecheck" description: "The typecheck command runs vue-tsc or Golar to check types throughout your app." canonical_url: "https://nuxt.com/docs/5.x/api/commands/typecheck" --- # nuxt typecheck > The typecheck command runs vue-tsc or Golar to check types throughout your app. <!--typecheck-cmd--> ```bash [Terminal] npx nuxt typecheck [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--dotenv=<path>...] [-e, --extends=<layer-name>...] [--checker=<vue-tsc|golar>] [-b, --build] [--incremental] ``` <!--/typecheck-cmd--> The `typecheck` command runs [`vue-tsc`](https://github.com/vuejs/language-tools/tree/master/packages/tsc) or [Golar](https://golar.dev/languages/vue/) to check types throughout your app. If neither is installed you are offered the install command for your package manager. ## Arguments <!--typecheck-args--> | Argument | Description | | ---------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/typecheck-args--> | | ## Options <!--typecheck-opts--> | Option | Default | Description | | ------------------------------------ | ------- | ----------------------------------------------------------------------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--dotenv=<path>...` | | Path to `.env` file to load, relative to the root directory. Can be repeated, with later files taking precedence. | | `-e, --extends=<layer-name>...` | | Extend from a Nuxt layer | | `--checker=<vue-tsc\|golar>` | | Type checker to use | | `-b, --build` | | Type-check in build mode, using TypeScript project references (detected automatically by default) | | `--no-build` | | Type-check without TypeScript project references | | `--incremental` | `true` | Reuse type information from previous runs | | `--no-incremental` | | Type-check from scratch, without reusing type information from previous runs | | <!--/typecheck-opts--> | | | ## Type checkers `--checker golar` runs [Golar](https://golar.dev/languages/vue/) instead of `vue-tsc`, and a `golar.config.*` file in your project makes it the default. A config file is written for you if one is missing. ## Build mode A solution-style `tsconfig.json` (one that only lists `references`) is type-checked in build mode automatically. `--build` forces it, and `--no-build` turns it off. ## Incremental type-checking In build mode, type information from each run is stored in the `tsBuildInfoFile` of every project referenced by `tsconfig.json`, so later runs only re-check what changed. From Nuxt v4.7, Nuxt's generated project configs store it in `.nuxt/cache/typecheck`, which is kept by `nuxt prepare` and `nuxt dev`. The cache is discarded when the type checker or TypeScript version changes, and is not used if any referenced project has no `tsBuildInfoFile`. Pass `--no-incremental` to remove the cached type information and type-check from scratch. ## Memory The type checker runs in its own process. Unless you set `--max-old-space-size`, the heap limit of `vue-tsc` is raised to half of the memory of your machine (or of the container it runs in), up to 8 GB and at most the memory currently available. It is never set lower than Node's default. Golar type-checks outside of the JavaScript heap, so its limit is left alone. If the type checker is killed, for example because it ran out of memory, `nuxt typecheck` reports the signal and exits with `128` plus the signal number (`134` for `SIGABRT`, `137` for `SIGKILL`). To raise the heap limit yourself: ```bash [Terminal] NODE_OPTIONS=--max-old-space-size=8192 npx nuxt typecheck ``` ::note This command sets `process.env.NODE_ENV` to `production`. To override, define `NODE_ENV` in a [`.env`](https://nuxt.com/docs/5.x/directory-structure/env) file or as a command-line argument. :: ::read-more{to="https://nuxt.com/docs/5.x/guide/concepts/typescript#type-checking"} Read more on how to enable type-checking at build or development time. :: --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/typecheck.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages. --- title: "nuxt upgrade" description: "The upgrade command upgrades Nuxt to the latest version." canonical_url: "https://nuxt.com/docs/5.x/api/commands/upgrade" --- # nuxt upgrade > The upgrade command upgrades Nuxt to the latest version. <!--upgrade-cmd--> ```bash [Terminal] npx nuxt upgrade [ROOTDIR] [--cwd=<directory>] [--logLevel=<silent|info|verbose>] [--dedupe] [-f, --force] [-ch, --channel=<stable|nightly|v4|v4-nightly|v5-nightly>] ``` <!--/upgrade-cmd--> The `upgrade` command upgrades Nuxt to the latest version. It updates the version in your [`package.json`](https://nuxt.com/docs/5.x/directory-structure/package), reinstalls dependencies with your package manager and removes the build cache. `--channel` picks what to upgrade to: `stable` for the latest release, `v4` to stay on a major, and `nightly`, `v4-nightly` or `v5-nightly` for the nightly release channel. `--force` recreates the lockfile and `node_modules` from scratch, and `--dedupe` deduplicates dependencies afterwards where the package manager supports it. A dependency pinned to a pnpm catalog (`"nuxt": "catalog:"`) is upgraded in `pnpm-workspace.yaml`, where the version actually lives, rather than in `package.json`. ## Arguments <!--upgrade-args--> | Argument | Description | | -------------------- | ---------------------------------------------------- | | `ROOTDIR` | The root directory of your Nuxt project (default: .) | | <!--/upgrade-args--> | | ## Options <!--upgrade-opts--> | Option | Default | Description | | -------------------------------------------------------------- | -------- | --------------------------------------------------- | | `--cwd=<directory>` | | Specify the root directory of your Nuxt project | | `--logLevel=<silent\|info\|verbose>` | | Specify build-time log level | | `--dedupe` | | Dedupe dependencies after upgrading | | `-f, --force` | | Force upgrade to recreate lockfile and node_modules | | `-ch, --channel=<stable\|nightly\|v4\|v4-nightly\|v5-nightly>` | `stable` | Specify a channel to install from | | <!--/upgrade-opts--> | | | --- - [Source](https://github.com/nuxt/cli/blob/main/packages/nuxt-cli/src/commands/upgrade.ts) ## Sitemap See the full [sitemap](https://nuxt.com/sitemap.md) for all pages.