Nuxt 4 en 2026: nueva estructura de directorios y migración desde Nuxt 3

Guía completa de migración a Nuxt 4: estructura de directorios app/, capa singleton de obtención de datos, mejoras en TypeScript e instrucciones paso a paso con ejemplos de código.

Nuxt 4 nueva estructura de directorios y migración desde Nuxt 3

Nuxt 4 introduce una estructura de directorios rediseñada que separa el código de la aplicación de la configuración, junto con una capa singleton de obtención de datos, reactividad superficial por defecto y contextos TypeScript divididos. Lanzado en julio de 2025 y actualmente en la versión 4.4, esta actualización mayor se enfoca en la evolución más que en la revolución, haciendo que la ruta de migración desde Nuxt 3 sea considerablemente más fluida que el salto de Nuxt 2 a 3.

Migración automatizada disponible

El equipo de Nuxt se asoció con Codemod para automatizar la mayoría de los pasos de migración. Basta con ejecutar npx codemod@latest nuxt/4/migration-recipe para gestionar la reestructuración de directorios, las actualizaciones de obtención de datos y los reemplazos de APIs obsoletas de forma automática.

La nueva estructura del directorio app/ en Nuxt 4

Nuxt 4 traslada todo el código fuente de la aplicación a un directorio app/ por defecto. Esta separación resuelve un problema real: los observadores de archivos en Linux y Windows funcionan significativamente mejor cuando el código de la aplicación reside en un subdirectorio dedicado en lugar de estar mezclado con node_modules/, .git/ y archivos de configuración.

La nueva organización sigue esta estructura:

text
my-nuxt-app/
├─ app/
│  ├─ assets/
│  ├─ components/
│  ├─ composables/
│  ├─ layouts/
│  ├─ middleware/
│  ├─ pages/
│  ├─ plugins/
│  ├─ utils/
│  ├─ app.vue
│  ├─ app.config.ts
│  └─ error.vue
├─ content/
├─ public/
├─ shared/         # Nuevo: código compartido entre app y servidor
├─ server/
└─ nuxt.config.ts

El directorio shared/ es una adición notable. Cualquier composable o utilidad ubicado en shared/ se auto-importa tanto en la aplicación Vue como en el servidor Nitro, eliminando la necesidad de importaciones manuales al compartir esquemas de validación, definiciones de tipos o funciones utilitarias entre ambos contextos.

Migración paso a paso de Nuxt 3 a Nuxt 4

El proceso de actualización comienza con un solo comando. Nuxt detecta la estructura plana existente y continúa funcionando sin ningún cambio, por lo que la migración puede realizarse de forma incremental.

bash
# Actualizar Nuxt y deduplicar dependencias
npx nuxt upgrade --dedupe

Después de actualizar el paquete, es necesario mover los archivos de la aplicación al directorio app/:

bash
# Automatizar la reestructuración de directorios
npx codemod@latest nuxt/4/file-structure

Este codemod mueve assets/, components/, composables/, layouts/, middleware/, pages/, plugins/, utils/, app.vue, error.vue y app.config.ts dentro de app/. Los archivos que pertenecen a la raíz — nuxt.config.ts, server/, public/ y content/ — permanecen en su lugar.

Para proyectos que necesiten postergar la reestructuración, el directorio fuente puede configurarse explícitamente:

nuxt.config.tstypescript
export default defineNuxtConfig({
  srcDir: '.',
  dir: { app: 'app' },
})

Esta configuración le indica a Nuxt 4 que resuelva los archivos desde la raíz del proyecto, replicando exactamente el comportamiento de Nuxt 3.

Capa singleton de obtención de datos y claves reactivas

Nuxt 4 cambia fundamentalmente la forma en que useAsyncData y useFetch gestionan los datos. Múltiples componentes que llaman a la misma clave ahora comparten una única referencia reactiva en lugar de mantener copias independientes.

app/composables/useProductData.tstypescript
export function useProductData(productId: string) {
  return useAsyncData(
    `product-${productId}`,
    () => $fetch(`/api/products/${productId}`),
    {
      getCachedData: (key, nuxtApp, ctx) => {
        // ctx.cause indica la razón del fetch
        if (ctx.cause === 'refresh:manual') return undefined
        return nuxtApp.payload.data[key]
      },
    },
  )
}

Tres cambios destacan en esta nueva capa de datos:

  • Referencias compartidas: llamar a useProductData('abc') en dos componentes devuelve las mismas refs data, error y status. Actualizar una actualiza ambas.
  • Limpieza automática: cuando el último componente que usa una clave se desmonta, Nuxt libera los datos asociados de la memoria.
  • Claves reactivas: envolver una clave en un computed o ref dispara automáticamente un nuevo fetch cuando el valor cambia.

El callback getCachedData ahora recibe un objeto de contexto con una propiedad cause ('initial', 'refresh:hook', 'refresh:manual' o 'watch'), permitiendo un control granular sobre cuándo servir datos en caché versus obtener datos frescos.

Valores por defecto modificados

Las propiedades data y error de useAsyncData/useFetch ahora tienen undefined como valor por defecto en lugar de null. Es necesario actualizar las verificaciones === null a === undefined o utilizar una comparación no estricta.

Reactividad superficial por defecto para mejor rendimiento

Nuxt 4 cambia data de useAsyncData y useFetch a shallowRef en lugar de ref. Vue ya no rastrea recursivamente cada propiedad anidada, lo que ofrece mejoras de rendimiento medibles para respuestas de API con objetos profundamente anidados o arrays grandes.

app/pages/dashboard.vuetypescript
<script setup lang="ts">
// Los datos ahora usan shallowRef por defecto
const { data: metrics } = await useFetch('/api/dashboard/metrics')

// La mutación directa de propiedades no activa la reactividad
// metrics.value.visits = 100  // No provocará un re-render

// Reemplazar el valor completo para activar las actualizaciones
metrics.value = { ...metrics.value, visits: 100 }

// O activar la reactividad profunda para esta llamada específica
const { data: settings } = await useFetch('/api/settings', {
  deep: true,
})
</script>

Para la mayoría de las visualizaciones de solo lectura (dashboards, listados de productos, páginas de artículos), la reactividad superficial funciona sin ningún cambio de código. La opción deep: true sigue disponible para formularios o interfaces interactivas que mutan propiedades anidadas directamente.

¿Listo para aprobar tus entrevistas de Vue.js / Nuxt.js?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

División de contextos TypeScript y seguridad de tipos mejorada

Nuxt 4 genera configuraciones TypeScript separadas para cada contexto del proyecto:

  • .nuxt/tsconfig.app.json — Código de la aplicación Vue
  • .nuxt/tsconfig.server.json — Código del servidor Nitro
  • .nuxt/tsconfig.shared.json — Utilidades compartidas
  • .nuxt/tsconfig.node.json — Configuración en tiempo de build

Esta separación significa que el IDE ya no sugiere APIs exclusivas del servidor en el código del cliente, y viceversa. Un único tsconfig.json en la raíz del proyecto referencia las cuatro configuraciones:

json
{
  "files": [],
  "references": [
    { "path": "./.nuxt/tsconfig.app.json" },
    { "path": "./.nuxt/tsconfig.server.json" },
    { "path": "./.nuxt/tsconfig.shared.json" },
    { "path": "./.nuxt/tsconfig.node.json" }
  ]
}

La verificación de tipos en CI también cambia. El comando vue-tsc ahora requiere la bandera -b (modo build) para procesar correctamente las referencias de proyecto:

bash
# Antes (Nuxt 3)
nuxt prepare && vue-tsc --noEmit

# Después (Nuxt 4)
nuxt prepare && vue-tsc -b --noEmit

Otro cambio en TypeScript: compilerOptions.noUncheckedIndexedAccess está activado por defecto (true). Acceder a un elemento de array o propiedad de objeto por índice ahora devuelve T | undefined, capturando errores potenciales en tiempo de ejecución durante la compilación.

Nombres de componentes normalizados y Vue Router v5

Nuxt 4.3 realizó la actualización a Vue Router v5, eliminando la dependencia de unplugin-vue-router. Para la mayoría de las aplicaciones, esta actualización es transparente.

Las convenciones de nomenclatura de componentes están ahora estandarizadas. Un componente ubicado en components/dashboard/MetricsCard.vue obtiene el nombre DashboardMetricsCard de manera consistente en todos los contextos — incluyendo <KeepAlive>, Vue DevTools y utilidades de testing.

vue
<!-- app/pages/dashboard.vue -->
<template>
  <NuxtPage :keepalive="{
    include: ['DashboardMetricsCard', 'DashboardRecentActivity'],
  }" />
</template>

Los proyectos que utilizan <KeepAlive> con filtros de nombres de componentes deben actualizar los nombres para coincidir con esta nueva convención. El comportamiento anterior, donde el nombre podía variar según el contexto, ya no aplica.

Gestión de cambios importantes en el manejo del head

Nuxt 4 incluye Unhead v2, que elimina varias propiedades obsoletas de useHead y useSeoMeta:

app/pages/product/[id].vuetypescript
<script setup lang="ts">
const route = useRoute()
const { data: product } = await useFetch(`/api/products/${route.params.id}`)

// Unhead v2: eliminación de vmid, hid, children, body
useSeoMeta({
  title: () => product.value?.name ?? 'Product',
  ogTitle: () => product.value?.name ?? 'Product',
  description: () => product.value?.description ?? '',
  ogImage: () => product.value?.imageUrl ?? '',
})
</script>

Para proyectos que dependen de parámetros de template o el ordenamiento por alias, estas funcionalidades deben instalarse como plugins explícitos:

app/plugins/unhead.tstypescript
import { TemplateParamsPlugin, AliasSortingPlugin } from '@unhead/vue/plugins'

export default defineNuxtPlugin({
  setup() {
    const unhead = injectHead()
    unhead.use(TemplateParamsPlugin)
    unhead.use(AliasSortingPlugin)
  },
})
Línea de tiempo de soporte de Nuxt 3

Nuxt 3 continúa recibiendo actualizaciones de seguridad y correcciones de errores críticos hasta el 31 de julio de 2026. Después de esa fecha, Nuxt 3 dejará de tener soporte. Planificar la migración ahora evita ejecutar aplicaciones en producción sobre un framework sin mantenimiento.

Lista de verificación de migración y errores comunes

El codemod automatizado gestiona la mayoría de los cambios, pero varios elementos requieren atención manual:

  • Eliminación de window.__NUXT__: reemplazar con useNuxtApp().payload. Este objeto global se elimina después de la hidratación en Nuxt 4.
  • Hook pages:extend: cambiar al nuevo hook pages:resolved, que se ejecuta después del escaneo de metadatos de páginas.
  • Booleano dedupe: reemplazar refresh({ dedupe: true }) con refresh({ dedupe: 'cancel' }) y false con 'defer'.
  • Estilos inline: solo los estilos de componentes Vue se incluyen inline por defecto; el CSS global se carga como archivos separados. Agregar features: { inlineStyles: true } para restaurar el comportamiento de Nuxt 3.
  • clearNuxtState: ahora restablece a los valores iniciales en lugar de undefined. Usar clearNuxtState('key', { reset: false }) para el comportamiento anterior.

Una secuencia de migración práctica para aplicaciones en producción:

  1. Ejecutar npx nuxt upgrade --dedupe y verificar que la aplicación compile correctamente
  2. Ejecutar el codemod: npx codemod@latest nuxt/4/migration-recipe
  3. Mover archivos a app/ (automatizado por el codemod file-structure)
  4. Actualizar las verificaciones de null a undefined en la lógica de obtención de datos
  5. Probar los componentes <KeepAlive> con los nombres normalizados
  6. Actualizar la verificación de tipos en CI para usar vue-tsc -b --noEmit
  7. Ejecutar la suite completa de tests y corregir los errores de TypeScript expuestos por noUncheckedIndexedAccess

Para profundizar en los conceptos de Vue y Nuxt, es posible explorar las preguntas de entrevista Vue/Nuxt en SharpSkill, o revisar la guía de SSR y generación estática para obtener contexto sobre estrategias de renderizado que también aplican en Nuxt 4.

Conclusión

  • Nuxt 4.4 (versión actual a abril de 2026) estabiliza la estructura del directorio app/, la obtención de datos singleton y los contextos TypeScript divididos como valores predeterminados listos para producción
  • El comando npx codemod@latest nuxt/4/migration-recipe automatiza la reestructuración de directorios, el reemplazo de APIs obsoletas y las actualizaciones de obtención de datos
  • La reactividad superficial mediante shallowRef mejora el rendimiento de las páginas de solo lectura sin requerir cambios de código en la mayoría de los casos
  • Las configuraciones TypeScript separadas por contexto (app, server, shared, node) eliminan las filtraciones de tipos entre contextos y mejoran el autocompletado del IDE
  • Nuxt 3 alcanza su fin de vida el 31 de julio de 2026 — migrar antes de esa fecha garantiza que las aplicaciones funcionen sobre una versión soportada y mantenida activamente
  • Vue Router v5 y Unhead v2 ofrecen APIs más limpias a costa de eliminar propiedades obsoletas que deben auditarse durante la migración

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Etiquetas

#nuxt
#vue
#migración
#typescript
#tutorial

Compartir

Artículos relacionados