---
title: "Nuxt 3.9"
description: "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."
canonical_url: https://nuxt.com/blog/v3-9
last_updated: 2026-04-15
---

# 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>`](/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](/docs/api/composables/use-loading-indicator) and in the original PR ([#24010](https://github.com/nuxt/nuxt/pull/24010)).

</tip>

### 🏁 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`](/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="/docs/api/utils/call-once">



</read-more>

### 🚨 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`.

</read-more>

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 ✨**
