Split View: SvelteKit 3.0 프리릴리스 읽기 — 깨지는 것, 조여지는 기본값, 그리고 여전히 experimental인 것
SvelteKit 3.0 프리릴리스 읽기 — 깨지는 것, 조여지는 기본값, 그리고 여전히 experimental인 것
- 들어가며 — 발표 글 없이, npm에 먼저 올라왔다
- 타임라인 — 2.x가 어디까지 달려왔는지 먼저
- v3가 올리는 바닥 — 런타임과 툴체인 최소선
- 지워지는 API — 2년 치 폐기 예고의 청산
- 조용히 바뀌는 기본값 — 보안 쪽이 많다
- 여전히 experimental인 것 — v3는 기능 메이저가 아니다
- 옆 동네와의 대조 — 메이저가 하는 일이 다르다
- 그래서 지금 무엇을 해야 하나
- 마치며
- 참고 자료
들어가며 — 발표 글 없이, npm에 먼저 올라왔다
svelte.dev 블로그에 "SvelteKit 3을 소개합니다" 같은 글은 아직 없습니다. 대신 npm 레지스트리에는 이미 흔적이 선명합니다 — @sveltejs/kit의 3.0.0-next.0이 2026년 6월 5일에 올라왔고, 이 글을 쓰는 시점(2026-07-17) 기준 최신 프리릴리스는 7월 14일의 3.0.0-next.8입니다. 6주가 채 안 되는 기간에 아홉 번의 프리릴리스가 나왔고, 안정판 2.69.3(7월 13일)과 나란히 두 트랙으로 릴리스가 돌고 있습니다.
저장소 쪽 증거도 같습니다. sveltejs/kit에는 version-3 브랜치가 열려 있고, 3.0 마일스톤은 오늘 기준 닫힌 이슈 107개, 열린 이슈 27개입니다. 정식 출시일은 발표된 바 없습니다 — 그러니 이 글의 내용도 "next.8 시점의 스냅샷"으로 읽어야 합니다. 프리릴리스니까 여기서 또 바뀔 수 있습니다.
그런데 version-3 브랜치의 체인지로그를 next.0부터 next.8까지 통독하면 한 가지가 분명해집니다. 이 메이저에는 새 기능이 거의 없습니다. 대신 2년 치 폐기 예고를 청산하고, 툴체인 바닥을 올리고, 기본값을 조입니다. 그리고 커뮤니티가 SvelteKit 3의 얼굴로 기대하는 것들 — remote functions, 컴포넌트 안의 await — 은 v3 프리릴리스에서도 여전히 experimental입니다. 이 구도를 이해하는 것이 마이그레이션 판단의 절반입니다.
타임라인 — 2.x가 어디까지 달려왔는지 먼저
v3를 읽으려면 2.x 후반의 흐름을 알아야 합니다. 날짜는 전부 npm 레지스트리의 publish 시각 기준입니다.
2024-12-16 kit 2.12 $app/state 추가 (룬 기반 — $app/stores의 후임)
2025-07-14 svelte 5.36 컴포넌트 await (experimental.async 플래그)
2025-07-24 kit 2.26 $app/paths에 resolve()/asset() 추가 (옛 base/assets 대체)
2025-07-31 kit 2.27 remote functions (experimental 플래그)
2026-01-22 svelte 5.48 ← 나중에 kit v3의 최소 Svelte 버전이 되는 릴리스
2026-05-01 kit 2.59 query.live 추가 (experimental)
2026-05-22 kit 2.61 query.live async iterable화, 쿼리 .run() 제거(파괴적)
2026-06-02 kit 2.62 svelte.config.js 없이 Vite 플러그인에 설정 전달 가능
2026-06-04 kit 2.63 명시적 환경 변수 (experimental)
2026-06-05 kit 3.0.0-next.0 ← 프리릴리스 시작
2026-07-14 kit 3.0.0-next.8 ← 현재
두 가지가 눈에 띕니다. 첫째, 2.62와 2.63은 v3 직전에 나온 "예고편"입니다 — 7월 다이제스트의 표현을 빌리면 설정의 vite.config 이동과 명시적 환경 변수는 둘 다 "Kit 3에서 이렇게 된다는 프리뷰"로 2.x에 먼저 실렸습니다. 둘째, 2.61에서 보듯 experimental 딱지가 붙은 기능은 마이너 릴리스에서도 파괴적 변경이 계속 일어납니다. .run() 제거, requested(...) 시그니처 변경 같은 것들이 semver 마이너로 나왔습니다 — 플래그를 켰다면 semver가 지켜 주지 않는 구역에 들어간 것입니다.
v3가 올리는 바닥 — 런타임과 툴체인 최소선
next.0의 체인지로그가 못 박는 최소 요구사항입니다.
- Node 22 이상 (#12548)
- TypeScript 6 이상 (#15930) — npm에는 6.0.2·6.0.3 안정판이 있고, 그 위로 TypeScript 7.0이 이미 GA(7.0.2, 2026-07-08)입니다. 참고로 svelte-check 4.7.0에는 tsgo(네이티브 TS) 지원이 experimental로 들어와 있습니다.
- Vite 8 — 처음에는 Vite 8 요구(#15371)였다가, next.5에서
vite@^8.0.12로 조여졌습니다. 체인지로그 문구 그대로 옮기면 "안정판 rolldown 1.0.0을 번들한 첫 Vite 8 릴리스"이기 때문입니다(#16134). npm 날짜로 교차 확인하면 rolldown 1.0.0이 2026-05-07, vite 8.0.12가 2026-05-11 — 나흘 차이로 맞물립니다. 즉 SvelteKit 3은 Rust 번들러 위에서만 돕니다. - @sveltejs/vite-plugin-svelte 7 (#15371)
- Svelte 5.48.0 이상 — 여기가 중요합니다. Svelte 6이 아닙니다. 5.48은 2026년 1월에 나온 평범한 마이너입니다. v3는 Svelte의 세대 교체와 커플링되어 있지 않습니다.
체인지로그는 Vite 8 요구의 근거로 "Vite hook filter를 통한 더 빠른 빌드와 Vite environment API를 쓰는 더 강력한 어댑터"를 듭니다 — 구체적인 빌드 시간 수치는 어디에도 공개된 것이 없으므로, 여기서는 프로젝트 자신의 표현이라는 것만 적어 둡니다.
지워지는 API — 2년 치 폐기 예고의 청산
v3에서 제거되거나 이름이 바뀌는 것들입니다. 전부 후임 API가 2.x에 먼저 들어와 있던 것들이라, "갑작스러운 제거"는 사실상 없습니다.
| 사라지는 것 | 후임 | 후임이 들어온 시점 |
|---|---|---|
$app/stores 모듈 전체 (#15499) | $app/state | kit 2.12 (2024-12) |
$app/paths의 base·assets·resolveRoute (#15507) | resolve()·asset() | kit 2.26 (2025-07) |
invalidateAll (deprecated) (#16289) | refreshAll (goto 옵션에도 적용) | v3 next.8에서 신설 |
$app/environment | $app/env로 개명 (별칭은 유지, next.1) | v3 |
$env/* 4종 모듈 | 명시적 환경 변수 ($app/env/private·$app/env/public) | kit 2.63 (experimental) |
svelte.config.js 필수 | Vite 플러그인에 설정 전달 (#16007) | kit 2.62부터 가능 |
| 폴더 안의 param 파일 | params.js/ts 단일 파일 (#16189) | v3 |
preloadStrategy 옵션 | 항상 modulepreload + output.linkHeaderPreload | v3 |
CSRF checkOrigin | trustedOrigins | kit 2.x에서 이미 deprecated |
@sveltejs/kit/node/polyfills, 어댑터용 createEntries | 없음 (제거) | — |
몇 가지 짚을 점이 있습니다.
$app/stores의 제거는 룬 전환의 마침표입니다. Svelte 5 룬이 스토어를 밀어낸 지 오래지만, SvelteKit의 page·navigating·updated 스토어는 하위 호환 때문에 살아 있었습니다. 후임 $app/state가 나온 지 1년 반이 지났으니 청산 시점으로는 온건한 편입니다.
invalidateAll → refreshAll은 어휘 통일입니다. PR #16289를 읽어 보면 의도가 드러납니다 — remote functions 세계에서 데이터 갱신은 query.refresh()이고, load 함수 세계의 "무효화"와 용어가 갈라져 있었습니다. refreshAll은 그 둘을 한 단어로 모으는 이름이고, load 함수를 항상 다시 실행하도록 옵션도 정리됐습니다. 주의할 점: refreshAll은 2.69.3에는 없습니다. v3로 올라갈 때 바꾸면 되는 일이지, 오늘 할 수 있는 일은 아닙니다.
환경 변수는 모듈 마법에서 명시적 선언으로 갑니다. 문서의 예고 문구는 명확합니다 — 명시적 환경 변수가 v3의 기본이 되고, $env/* 모듈과 $app/environment는 제거됩니다. 새 방식은 src/env.ts에서 선언합니다.
// src/env.ts — kit 2.63+에서 experimental.explicitEnvironmentVariables로 미리 써 볼 수 있다
import { defineEnvVars } from '@sveltejs/kit/hooks';
export const variables = defineEnvVars({
API_KEY: {}, // 기본은 private — $app/env/private로만 임포트 가능
});
지금까지 $env/static/private가 "임포트 위치로 노출 여부를 강제"하는 영리한 장치였다면, 새 방식은 앱에 존재하는 환경 변수의 목록 자체를 한 파일에 타입과 함께 못 박습니다. 어떤 변수가 있는지 코드만 봐서는 알 수 없던 문제가 사라지는 대신, 변수 하나 추가할 때마다 선언이 한 줄 늘어납니다.
조용히 바뀌는 기본값 — 보안 쪽이 많다
제거보다 조용하지만 실제로 동작이 달라지는 것들입니다. 마이그레이션에서 시간을 잡아먹는 건 보통 이쪽입니다.
- 외부 리다이렉트가 기본 금지됩니다 (#16198). 외부 URL(그리고
javascript:URL)로redirect하려면 이제external옵션을 명시적으로 넘겨야 합니다. 로그인 후?next=파라미터로 리다이렉트하는 코드가 있다면 — open redirect의 단골 자리인데 — v3에서 그대로 두면 깨집니다. 깨지는 게 의도입니다. - 쿠키 기본
path가'/'가 됩니다 (#15398). 지금까지는 현재 경로 기준이었고, 이게 "쿠키를 설정했는데 다른 페이지에서 안 보인다"는 단골 질문의 원인이었습니다. cookie v1 채택으로 쿠키 이름도 ASCII로 제한됩니다(#13386). - form action 실패가
fail()에 담은 HTTP 상태 코드를 실제 응답 코드로 씁니다 (#16200). 지금까지 성공 200에 실려 오던 것이 바뀌므로, 상태 코드를 보고 동작하는 프록시·모니터링이 있다면 영향을 받습니다. 204 응답은 정말로 본문 없이 나갑니다. handleError가 응답 상태 코드에 영향을 줄 수 있게 됩니다 (#16162).goto가 앱 라우트로 해석되지 않는 URL에서 reject합니다 (#16164) — 외부 URL에서 이미 그랬던 것과 동작을 맞췄습니다.- 내비게이션 중
invalidate호출이 내비게이션을 중단시키지 않습니다 (#16188).
참고로 remote functions 응답에 cache-control: private, no-store를 강제해 개인화된 쿼리 결과가 공유 캐시에 남지 않게 하는 수정은 v3가 아니라 2.65.2에 이미 들어갔습니다 — 보안 기본값 조이기는 v3 전용 테마가 아니라 최근 몇 달의 일관된 방향입니다.
여전히 experimental인 것 — v3는 기능 메이저가 아니다
여기가 이 글에서 가장 중요한 절입니다.
Remote functions는 v3에서도 experimental입니다. 공식 문서는 지금도 "currently experimental… subject to change without notice"라고 적혀 있고, kit.experimental.remoteFunctions와 compilerOptions.experimental.async 두 플래그를 요구합니다. 더 시사적인 건 v3 프리릴리스의 방향입니다 — next.7은 오히려 플래그 없이 *.remote.ts/js 파일을 두는 것을 금지했습니다(#16247, 2026-07-07 머지). 안정화가 임박했다면 나올 수 없는 변경입니다. 2.27로 2025년 7월에 나와 1년 가까이 지나도록, 그리고 v3 메이저를 넘어서도, remote functions의 API 안정성 보장은 없습니다.
컴포넌트 await도 마찬가지입니다. Svelte 5.36(2025-07-14)부터 experimental.async 플래그로 써 왔고, 문서는 플래그가 제거되는 시점을 명시합니다 — "The experimental flag will be removed in Svelte 6." 그리고 Svelte 6은 npm에 존재하지 않습니다 — 6.x는 정식판도 프리릴리스도 발행된 적이 없고, svelte의 latest는 5.56.6(2026-07-16)입니다. kit v3의 최소 요구가 Svelte 5.48인 것이 이 구도를 요약합니다: SvelteKit의 메이저와 Svelte의 메이저는 분리된 열차입니다. 컴파일러 쪽의 experimental 목록에는 fork API(5.42)와 async SSR(5.39)도 그대로 남아 있습니다.
반대 방향의 졸업도 하나 있습니다 — OpenTelemetry 트레이싱은 next.7에서 experimental 네임스페이스를 벗어나 정식 API가 됐습니다(#16260).
정리하면 v3의 정체는 이렇습니다. 화제성 있는 기능은 전부 experimental 트랙에서 2.x와 v3에 동시에 실려 가고, 메이저 버전은 그 밑의 바닥 — 런타임, 번들러, 이름, 기본값 — 을 갈아 끼우는 데 쓰입니다. "v3로 올리면 remote functions가 안정된다"는 기대는 현재 증거로는 성립하지 않습니다.
옆 동네와의 대조 — 메이저가 하는 일이 다르다
같은 주에 React 생태계를 달군 뉴스가 React Compiler의 Rust 포팅이었다는 점은 대조가 흥미롭습니다. React는 수동 메모이제이션을 없애기 위해 컴파일러 인프라를 새로 짓는 중이고 — 그것도 실험적 WIP 단계 — Svelte는 처음부터 컴파일러가 프레임워크였으니 그 단계가 없습니다. 대신 Svelte 쪽 메이저는 번들러(rolldown)와 런타임 바닥을 올리는 데 쓰입니다. 2026년의 두 프레임워크 모두 "새 기능 대방출"이 아니라 기반 공사에 메이저급 에너지를 쓰고 있다는 공통점이 있고, 어느 쪽도 당장 앱 개발자의 코드를 크게 바꾸지 않습니다.
그래서 지금 무엇을 해야 하나
오늘 2.x에서 미리 할 수 있는 것 — 전부 v3 마이그레이션의 선불입니다.
$app/stores임포트를$app/state로 옮깁니다. 1년 반 된 API라 레퍼런스도 충분합니다.$app/paths의base·assets문자열 결합을resolve()·asset()으로 바꿉니다.- 설정을
vite.config의 플러그인 옵션으로 옮겨 봅니다(2.62+). 새 프로젝트라면 sv CLI 0.16이 이미 그렇게 스캐폴딩합니다. - Node 22, Vite 8, vite-plugin-svelte 7로 먼저 올라가 봅니다 — 이건 v3 없이도 지금 가능한 검증입니다.
invalidateAll사용처를 파악해 둡니다. 바꾸는 건 v3에서지만, 어디서 쓰는지 아는 건 지금 됩니다.
하지 말아야 할 것.
3.0.0-next.*를 프로덕션에 올리는 것. 프리릴리스는 next.8까지 오는 동안에도 파괴적 변경이 계속 나왔습니다($app/stores제거는 next.7,refreshAll은 next.8에 나왔습니다).- remote functions를 안정 API로 간주하고 팀 표준이나 공개 라이브러리를 세우는 것. 2.61의
.run()제거 같은 일이 또 일어나지 않는다는 보장이 없습니다 — experimental의 정의가 그것입니다. - "Svelte 6 나오면 한꺼번에"라며 v3 대비를 미루는 것. 위에서 봤듯 두 메이저는 분리돼 있어서, 기다림의 대상이 서로 다릅니다.
어댑터·라이브러리 저자라면 일이 더 있습니다. createEntries가 사라지고, 어댑터가 Vite 플러그인을 직접 제공할 수 있게 되며(#16206), Vite environment API 기반으로 어댑터가 재편됩니다. 앱 코드보다 이쪽 생태계가 v3 출시 시점의 실질적 병목이 될 가능성이 높습니다.
마치며
SvelteKit 3.0 프리릴리스의 체인지로그는 화려하지 않습니다. 그리고 그게 좋은 신호라고 생각합니다. 재학습을 요구하는 메이저 — 라우터가 바뀌고 데이터 로딩 모델이 바뀌는 종류 — 가 아니라, 폐기 예고를 이행하고 바닥을 올리고 기본값을 조이는 메이저입니다. 룬을 다시 배울 필요도, load 함수를 다시 쓸 필요도 없습니다.
대신 기대치는 정확히 잡아야 합니다. remote functions와 컴포넌트 await가 "정식"이 되는 사건은 SvelteKit 3이 아닙니다 — 전자는 아직 기약이 없고, 후자는 문서상 Svelte 6의 일입니다. v3는 그 미래가 올라설 바닥(rolldown 기반 Vite 8, Node 22, 정리된 API 표면)을 까는 릴리스로 읽는 것이 체인지로그와 일치하는 독해입니다. 프리릴리스가 진행 중이므로, 정식 출시 때 이 글의 세부 사항 일부는 달라져 있을 수 있습니다 — 그때 다시 확인할 것을 전제로 쓰는 스냅샷입니다.
참고 자료
- @sveltejs/kit CHANGELOG (version-3 브랜치) — 3.0.0-next.0~next.8 전체 변경 사항
- npm: @sveltejs/kit 버전 목록 — 프리릴리스 발행 일자 확인
- PR #16289 — invalidateAll deprecation과 refreshAll
- PR #16198 — 외부 리다이렉트 기본 금지
- PR #16247 — 플래그 없는 remote 파일 금지
- PR #15507 — $app/paths의 base/assets/resolveRoute 제거
- SvelteKit 문서: Remote functions — experimental 고지와 플래그
- Svelte 문서: await expressions — "The experimental flag will be removed in Svelte 6"
- SvelteKit 문서: Environment variables — 명시적 환경 변수와 v3 예고
- What's new in Svelte: July 2026 — config-in-vite와 명시적 env의 "Kit 3 프리뷰" 설명
- What's new in Svelte: June 2026 — query.live와 2.x 내 파괴적 변경들
- Svelte 5 룬 심층 — 반응성 재설계 배경(관련 글)
- React Compiler Rust 포팅 — 옆 동네 메이저 작업과의 대조(관련 글)
- TypeScript 7 GA — kit v3의 TS 최소 요구와 얽히는 배경(관련 글)
Reading the SvelteKit 3.0 Pre-Release — What Breaks, What Tightens, and What Is Still Experimental
- Introduction — No Announcement Post, It Just Showed Up on npm First
- Timeline — Where 2.x Has Already Gotten To
- The Floor v3 Raises — Minimum Runtime and Toolchain
- APIs Being Removed — Settling Two Years of Deprecation Notices
- Defaults Changing Quietly — Mostly on the Security Side
- What's Still Experimental — v3 Is Not a Feature Major
- A Contrast With the Neighborhood — Majors Do Different Jobs
- So What Should You Do Right Now
- Closing
- References
Introduction — No Announcement Post, It Just Showed Up on npm First
There is not yet a "Introducing SvelteKit 3" post on the svelte.dev blog. But the trail on the npm registry is already unmistakable — @sveltejs/kit's 3.0.0-next.0 went up on June 5, 2026, and as of the time of this writing (2026-07-17) the latest pre-release is 3.0.0-next.8 from July 14. Nine pre-releases in under six weeks, running on two tracks side by side with the stable 2.69.3 (July 13).
The repository tells the same story. sveltejs/kit has an open version-3 branch, and the 3.0 milestone shows 107 closed issues and 27 open issues as of today. No stable release date has been announced — so everything in this post should be read as "a snapshot as of next.8." It is a pre-release; things can still change from here.
But read the version-3 branch's changelog straight through from next.0 to next.8 and one thing becomes clear. This major has almost no new features. Instead it settles two years of deprecation notices, raises the toolchain floor, and tightens defaults. And the things the community expects to be the face of SvelteKit 3 — remote functions, await inside components — are still experimental even in the v3 pre-release. Understanding this framing is half the migration decision.
Timeline — Where 2.x Has Already Gotten To
To read v3, you first need the tail end of 2.x. All dates below are the publish timestamps from the npm registry.
2024-12-16 kit 2.12 $app/state added (rune-based — successor to $app/stores)
2025-07-14 svelte 5.36 component await (experimental.async flag)
2025-07-24 kit 2.26 resolve()/asset() added to $app/paths (replaces old base/assets)
2025-07-31 kit 2.27 remote functions (experimental flag)
2026-01-22 svelte 5.48 ← the release that later becomes kit v3's minimum Svelte version
2026-05-01 kit 2.59 query.live added (experimental)
2026-05-22 kit 2.61 query.live becomes an async iterable, query .run() removed (breaking)
2026-06-02 kit 2.62 config can be passed to the Vite plugin without svelte.config.js
2026-06-04 kit 2.63 explicit environment variables (experimental)
2026-06-05 kit 3.0.0-next.0 ← pre-release begins
2026-07-14 kit 3.0.0-next.8 ← current
Two things stand out. First, 2.62 and 2.63 are the "trailers" that shipped right before v3 — in the words of the July digest, moving config into vite.config and explicit environment variables both landed in 2.x first as a "preview of how this will work in Kit 3." Second, as 2.61 shows, features tagged experimental keep taking breaking changes even in minor releases. Removing .run(), changing the requested(...) signature — these shipped as semver minors. If you turned the flag on, you stepped outside the zone semver protects.
The Floor v3 Raises — Minimum Runtime and Toolchain
These are the minimum requirements pinned down by next.0's changelog.
- Node 22 or later (#12548)
- TypeScript 6 or later (#15930) — npm has stable 6.0.2/6.0.3, and above that TypeScript 7.0 has already gone GA (7.0.2, 2026-07-08). Note that svelte-check 4.7.0 also has experimental tsgo (native TS) support.
- Vite 8 — this started as a Vite 8 requirement (#15371), then was tightened to
vite@^8.0.12in next.5. In the changelog's own words, this is because it's "the first Vite 8 release to bundle stable rolldown 1.0.0" (#16134). Cross-checking npm dates: rolldown 1.0.0 shipped 2026-05-07, vite 8.0.12 shipped 2026-05-11 — four days apart, locked together. In other words, SvelteKit 3 only runs on top of the Rust bundler. - @sveltejs/vite-plugin-svelte 7 (#15371)
- Svelte 5.48.0 or later — this matters. Not Svelte 6. 5.48 is an ordinary minor from January 2026. v3 is not coupled to Svelte's generational shift.
The changelog cites "faster builds through Vite hook filters and stronger adapters that use the Vite environment API" as the rationale for the Vite 8 requirement — no concrete build-time figures have been published anywhere, so I'll only note here that this is the project's own framing.
APIs Being Removed — Settling Two Years of Deprecation Notices
These are the things removed or renamed in v3. In every case a successor API already shipped in 2.x first, so there is effectively no "sudden removal."
| Going away | Successor | When the successor landed |
|---|---|---|
the entire $app/stores module (#15499) | $app/state | kit 2.12 (2024-12) |
base/assets/resolveRoute from $app/paths (#15507) | resolve()/asset() | kit 2.26 (2025-07) |
invalidateAll (deprecated) (#16289) | refreshAll (also applies to the goto option) | newly introduced in v3 next.8 |
$app/environment | renamed to $app/env (alias kept, next.1) | v3 |
the four $env/* modules | explicit environment variables ($app/env/private/$app/env/public) | kit 2.63 (experimental) |
svelte.config.js required | pass config to the Vite plugin (#16007) | possible since kit 2.62 |
| param files inside a folder | a single params.js/ts file (#16189) | v3 |
the preloadStrategy option | always modulepreload + output.linkHeaderPreload | v3 |
CSRF checkOrigin | trustedOrigins | already deprecated within kit 2.x |
@sveltejs/kit/node/polyfills, createEntries for adapters | none (removed) | — |
A few points worth flagging.
Removing $app/stores is the final period on the rune transition. Svelte 5 runes pushed stores out long ago, but SvelteKit's page/navigating/updated stores stayed alive for backward compatibility. Its successor $app/state has now had a year and a half out in the world, so as retirements go, this one is on the gentle side.
invalidateAll → refreshAll is a vocabulary unification. Read PR #16289 and the intent is clear — in the world of remote functions, refreshing data is query.refresh(), and that vocabulary had drifted apart from "invalidation" in the world of load functions. refreshAll is the name that folds both into one word, and the option to always rerun load functions was cleaned up alongside it. One thing to note: refreshAll does not exist in 2.69.3. It's something you switch to when you move up to v3, not something you can do today.
Environment variables move from module magic to explicit declaration. The docs' preview language is unambiguous — explicit environment variables become the default in v3, and the $env/* modules and $app/environment are removed. The new approach is declared in src/env.ts.
// src/env.ts — you can try this early on kit 2.63+ via experimental.explicitEnvironmentVariables
import { defineEnvVars } from '@sveltejs/kit/hooks';
export const variables = defineEnvVars({
API_KEY: {}, // private by default — importable only from $app/env/private
});
Where $env/static/private used to be the clever trick of "enforcing exposure by way of import location," the new approach pins down the entire list of environment variables that exist in the app, with types, in a single file. The problem of not being able to tell what variables exist just by reading the code goes away — in exchange, every new variable means one more line of declaration.
Defaults Changing Quietly — Mostly on the Security Side
These are quieter than removals, but they actually change behavior. This is usually where migration time gets eaten.
- External redirects are banned by default (#16198). To
redirectto an external URL (or ajavascript:URL) you now have to explicitly pass theexternaloption. If you have code that redirects post-login via a?next=parameter — a classic open-redirect spot — leaving it as-is in v3 will break it. Breaking is the point. - The default cookie
pathbecomes'/'(#15398). Until now it was based on the current path, which was the source of the classic "I set a cookie but it's not visible on another page" question. Adopting cookie v1 also restricts cookie names to ASCII (#13386). - Form action failures use the HTTP status code carried in
fail()as the actual response code (#16200). This used to arrive riding on a success 200, so this changes — anything watching status codes, like a proxy or monitoring setup, is affected. 204 responses now genuinely go out with no body. handleErrorcan now affect the response status code (#16162).gotorejects on a URL that can't be resolved to an app route (#16164) — matching behavior it already had for external URLs.- Calling
invalidateduring navigation no longer aborts the navigation (#16188).
For what it's worth, the fix that forces cache-control: private, no-store on remote function responses — so personalized query results don't stick around in a shared cache — already landed in 2.65.2, not v3. Tightening security defaults isn't a v3-only theme; it's been the consistent direction of the last several months.
What's Still Experimental — v3 Is Not a Feature Major
This is the most important section of this post.
Remote functions are still experimental even in v3. The official docs still say "currently experimental… subject to change without notice," and require two flags: kit.experimental.remoteFunctions and compilerOptions.experimental.async. What's more telling is the direction of the v3 pre-releases — next.7 actually banned putting *.remote.ts/js files in place without the flag (#16247, merged 2026-07-07). That is not a change you'd make if stabilization were imminent. Nearly a year after shipping as 2.27 in July 2025, and even past the v3 major, there is no API stability guarantee for remote functions.
Component await is the same story. It's been usable behind the experimental.async flag since Svelte 5.36 (2025-07-14), and the docs spell out exactly when the flag comes off — "The experimental flag will be removed in Svelte 6." And Svelte 6 does not exist on npm — no 6.x has ever shipped, stable or pre-release, and svelte's latest is 5.56.6 (2026-07-16). The fact that kit v3's minimum requirement is Svelte 5.48 sums up this framing: SvelteKit's major version and Svelte's major version are separate trains. The compiler side's experimental list also still holds the fork API (5.42) and async SSR (5.39).
There is one graduation running the other direction — OpenTelemetry tracing left the experimental namespace and became a stable API in next.7 (#16260).
To sum up, this is what v3 actually is: every feature that's generating buzz is shipping simultaneously to both 2.x and v3 on the experimental track, and the major version is spent swapping out the floor underneath — runtime, bundler, names, defaults. The expectation that "remote functions will stabilize once you move to v3" does not hold up against the current evidence.
A Contrast With the Neighborhood — Majors Do Different Jobs
It's an interesting contrast that the news that lit up the React ecosystem the same week was the Rust port of the React Compiler. React is building new compiler infrastructure from scratch to eliminate manual memoization — and that work is still at an experimental WIP stage — while Svelte has had a compiler as its foundation from day one, so it never had that stage to go through. Instead, Svelte's major is spent raising the bundler (rolldown) and runtime floor. Both frameworks share a common thread in 2026: spending major-version-level energy on foundation work rather than a "big feature dump," and neither one is about to substantially change an app developer's code anytime soon.
So What Should You Do Right Now
What you can do today, on 2.x, ahead of time — all of it is prepayment toward the v3 migration.
- Move
$app/storesimports to$app/state. It's a year-and-a-half-old API by now, so there's plenty of reference material. - Replace string concatenation of
base/assetsfrom$app/pathswithresolve()/asset(). - Try moving config into
vite.config's plugin options (2.62+). If you're starting a new project, sv CLI 0.16 already scaffolds it that way. - Try moving to Node 22, Vite 8, and vite-plugin-svelte 7 first — this is a validation you can do right now, without v3.
- Map out where you use
invalidateAll. Actually switching happens at v3, but knowing where it's used is something you can do now.
What you shouldn't do.
- Put
3.0.0-next.*into production. Breaking changes kept landing all the way through to next.8 in the pre-release ($app/storeswas removed in next.7,refreshAllarrived in next.8). - Treat remote functions as a stable API and build a team standard or a public library on top of it. There's no guarantee something like 2.61's
.run()removal doesn't happen again — that's the definition of experimental. - Put off v3 prep on the theory of "I'll do it all at once when Svelte 6 lands." As shown above, the two majors are decoupled, so the thing you're waiting on differs.
If you're an adapter or library author there's more work ahead. createEntries is gone, adapters will be able to supply Vite plugins directly (#16206), and adapters are being reorganized around the Vite environment API. That ecosystem, more than app code, is the likelier practical bottleneck at v3's release.
Closing
The changelog for the SvelteKit 3.0 pre-release isn't flashy. And I think that's a good sign. This isn't the kind of major that demands relearning — a swapped-out router, a changed data-loading model — it's a major that pays off deprecation notices, raises the floor, and tightens defaults. You don't have to relearn runes, and you don't have to rewrite your load functions.
What you do need to get exactly right is expectations. Remote functions and component await becoming "official" is not an event that happens at SvelteKit 3 — the former has no timeline yet, and the latter is, per the docs, Svelte 6's job. The reading that lines up with the changelog is that v3 is the release laying the floor that future stands on (a rolldown-based Vite 8, Node 22, a cleaned-up API surface). Since the pre-release is still in progress, some of the details in this post may have changed by the stable release — this is written as a snapshot on the premise that it'll be checked again then.
References
- @sveltejs/kit CHANGELOG (version-3 branch) — the full set of changes from 3.0.0-next.0 to next.8
- npm: @sveltejs/kit version list — confirming pre-release publish dates
- PR #16289 — the invalidateAll deprecation and refreshAll
- PR #16198 — external redirects banned by default
- PR #16247 — banning flagless remote files
- PR #15507 — removal of base/assets/resolveRoute from $app/paths
- SvelteKit docs: Remote functions — the experimental notice and flags
- Svelte docs: await expressions — "The experimental flag will be removed in Svelte 6"
- SvelteKit docs: Environment variables — explicit environment variables and the v3 preview
- What's new in Svelte: July 2026 — explaining config-in-vite and explicit env as a "Kit 3 preview"
- What's new in Svelte: June 2026 — query.live and breaking changes within 2.x
- Svelte 5 Runes deep dive — the background of the reactivity redesign (related post)
- The React Compiler's Rust port — a contrast with a neighboring major-version effort (related post)
- TypeScript 7 GA — background entangled with kit v3's minimum TS requirement (related post)