Portada de UrsaKit para la guía de actualizar una app de SvelteKit 2 a SvelteKit 3

SvelteKit 3 salió el 1 de octubre de 2026, y sus notas son inusualmente sinceras: es el mismo framework con más pulido, más seguridad de tipos y menos trastos. Eso es buena noticia para una actualización, porque el trabajo es en su mayoría mecánico; pero unos pocos cambios tocan suficientes archivos como para que la migración oficial sea el único punto de partida sensato.

Esta guía ejecuta la migración y luego recorre los cuatro cambios que no puede terminar por ti.

01Qué cambió realmente

Los cambios rompientes son pocos y de amplio alcance:

ÁreaSvelteKit 2SvelteKit 3
Config del frameworksvelte.config.jsvite.config.ts
Alias de código compartido$libsubpath imports #lib
Config de TypeScriptextiende ./.svelte-kit/tsconfig.jsonextiende $app/tsconfig
Variables de entornoimplícitasdeclaradas en src/env.ts
APIs de service worker$service-worker$app/env, $app/paths, $app/manifest
Enrutado superficialpushState / replaceStategoto(..., { shallow: true })
Vite7 soportado8 obligatorio (Rolldown)

Otros dos cambios importan en producción: el manejo de errores ahora cubre errores de render además de los de load, y todos los errores —incluidos los que lanzas tú con error(...)— pasan por tu lógica de handleError. Las trazas de pila también llevan sourcemap ahora.

Requiere Svelte 5 y Vite 8

SvelteKit 3 se apoya en los error boundaries de Svelte 5 y requiere Vite 8. Si sigues en Svelte 4, sube primero a Svelte 5 y mantén las dos migraciones separadas.

02Ejecuta primero la migración oficial

El CLI sv incluye una migración que reescribe lo que puede y deja una lista de TODO para el resto. Ejecútala en una rama limpia, sin trabajo sin confirmar.

git checkout -b chore/sveltekit-3
npx sv migrate sveltekit-3 --tasks all --confirm

--tasks all ejecuta cada tarea de migración en orden, y --confirm omite el prompt interactivo para que la ejecución sea reproducible. Revisa la lista de TODO antes de editar nada a mano: te dice exactamente qué imports y bloques de config no pudo reescribir.

Después actualiza dependencias y deja que el toolchain saque a la luz lo que queda:

npm install
npx svelte-kit sync
npm run check

svelte-kit sync regenera los tipos de los que dependen el tsconfig y el cambio a #lib. Si lo omites, el editor reportará imports rotos que la build resolvería sin problema.

03Mueve la configuración a vite.config.ts

El plugin de SvelteKit recibe ahora su configuración donde Vite la necesita, en lugar de a través de una resolución asíncrona de svelte.config.js. En la práctica, importas el plugin en vite.config.ts y le pasas el objeto que antes era la config.

import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'

export default defineConfig({
  plugins: [
    sveltekit({
      // El objeto `kit` que antes vivía en svelte.config.js.
      adapter: undefined,
      alias: {}
    })
  ]
})

La forma exacta del objeto está documentada en la guía de migración de SvelteKit 3, porque depende del adapter y las opciones de preprocesador que usara tu app. Trata el bloque anterior como el destino, no como una respuesta para copiar y pegar: mueve tus ajustes de kit, borra svelte.config.js y confirma que el adapter resuelve en una build real, no solo en dev.

04Sustituye $lib por #lib

El código compartido sigue en src/lib; solo cambia la ruta de import. Los subpath imports son una función de Node que Vite y TypeScript entienden de forma nativa, y por eso SvelteKit pudo borrar el código que antes sincronizaba ambos.

// SvelteKit 2
import { formatPrice } from '$lib/format'
import { Button } from '$lib/components'

// SvelteKit 3
import { formatPrice } from '#lib/format.ts'
import { Button } from '#lib/components/index.ts'

La trampa es la resolución. Los subpath imports deben ser inequívocos, así que #lib/format a secas falla y necesitas #lib/format.ts o #lib/format/index.ts. La tarea de migración reescribe casi todos los imports; cuando termine, busca cualquier $lib restante.

grep -rn "\$lib" src/ --include='*.ts' --include='*.svelte' | head -30

Un subpath import ambiguo falla al comprobar tipos

Si npm run check dice que no encuentra #lib/foo, la solución casi siempre es una extensión explícita o un index.ts. Es una regla de resolución, no un bug de SvelteKit.

05Simplifica el tsconfig y declara las variables de entorno

Tu tsconfig.json ya no extiende un archivo generado dentro de .svelte-kit. Extiende $app/tsconfig, que trae más opciones de compilador recomendadas, así que la mayoría de apps pueden borrar su propio bloque compilerOptions. Debes fijar include y exclude explícitamente, y añadir el service worker a exclude.

Las variables de entorno cambian más. En lugar de recurrir a $env/static/private y compañía según haga falta, declaras en src/env.ts las variables de las que depende tu app, marcando cada una como pública o privada y como resuelta en build o al arrancar. Las librerías de Standard Schema pueden validarlas.

// src/env.ts — forma ilustrativa; consulta la guía de migración para la API exacta.
import { defineEnv } from '$app/env'

export const env = defineEnv({
  DATABASE_URL: { private: true },
  PUBLIC_API_BASE: { public: true, buildTime: true }
})

La recompensa son variables con tipos, validadas e importadas donde se usan, en vez de sacadas de un módulo cajón de sastre. El coste es que cada referencia implícita a process.env necesita una declaración, y una que falte falla en build en lugar de en runtime, que es justo la idea.

06Service workers, errores y enrutado superficial

Tres cambios menores que conviene revisar a mano:

  • Los service workers dejan de importar de $service-worker y toman lo que necesitan de $app/env, $app/paths y el nuevo $app/manifest. Si quieres tipos precisos, añade un tsconfig.json junto al service worker que extienda $app/tsconfig/service-worker.
  • El manejo de errores ahora cubre errores de render, y todos los errores pasan por handleError. Audita esa función: la lógica de logging que asumía que solo llegaban errores de load puede ver de repente mucho más tráfico.
  • El enrutado superficial usa goto(url, { shallow: true }) en vez de pushState/replaceState. Las navegaciones superficiales ahora disparan beforeNavigate, y persistState: true conserva el estado de la página tras una recarga.

07Verifica la actualización antes de fusionar

  1. Ejecuta npm run check y elimina cada error de tipos; no fusiones rodeándolos.
  2. Ejecuta la build de producción, no solo dev, para que se recorran el adapter y la ruta de Vite 8.
  3. Prueba de punta a punta un flujo completo de usuario, incluida una form action y una ruta que falle.
  4. Revisa el reporte de errores: provoca un error de render y confirma que llega a handleError con traza mapeada.
  5. Compara vite.config.ts con el antiguo svelte.config.js para asegurarte de que ninguna opción de kit se perdió en el traslado.

Haz que la migración sea su propio commit

La migración de sv toca muchos archivos. Confírmala sola y revísala con una herramienta de diff antes de añadir tus propios arreglos. Mezclar un renombrado mecánico de 200 archivos con trabajo de features hace casi imposible bisecar una regresión.

08Errores comunes y soluciones

ErrorCausaSolución
No encuentra el módulo #lib/fooSubpath import ambiguoUsa #lib/foo.ts o #lib/foo/index.ts
Una opción de config no surte efectoSigue en un svelte.config.js residualMuévela al plugin sveltekit()
Errores de tipos en un checkout limpioLos tipos generados están obsoletosEjecuta npx svelte-kit sync
Falta una variable de entorno en buildSin declarar en src/env.tsDeclárala, pública o privada
Fallan los tipos del service workerImport antiguo de $service-workerImporta de los módulos $app/*

La actualización es una tarde de trabajo en una app pequeña y una semana cuidadosa en una grande, pero la forma es predecible: deja que sv haga los renombrados y dedica tu atención al traslado de config, las declaraciones de entorno y la ruta de errores. Esos son los tres sitios donde una build en verde todavía puede esconder una app rota.

Preguntas frecuentes

¿SvelteKit 3 es una reescritura?

No. El equipo lo describe como el mismo framework con más pulido, más seguridad de tipos y menos código heredado. Casi todos los cambios son mecánicos y la migración oficial cubre la mayoría.

¿Cuál es el mayor cambio rompiente?

La configuración sale de svelte.config.js y entra en vite.config.ts. El resto es menor, pero el renombrado a #lib y el cambio de tsconfig tocan casi todos los archivos de una app grande.

¿Hay que usar extensiones de archivo explícitas con #lib?

Sí, para la resolución. Como son subpath imports de Node, TypeScript y Node exigen rutas inequívocas, así que `#lib/foo` pasa a `#lib/foo.ts` o `#lib/foo/index.ts`.

¿Están estables las remote functions en SvelteKit 3?

Todavía no. Las remote functions siguen necesitando un flag experimental, y el equipo dice que terminarlas es la prioridad tras el lanzamiento.

¿Puedo actualizar directamente una app de Svelte 4?

SvelteKit 3 requiere Svelte 5, así que el orden práctico es pasar primero a Svelte 5 y luego ejecutar la migración de SvelteKit. Hacer las dos cosas a la vez hace mucho más difícil aislar un fallo.

Volver a los artículos

Artículos relacionados

Herramientas relacionadas