Почему я перешёл на SvelteKit для своих проектов
Я бэкендер. Пишу на Python и Go, настраиваю серверы, ковыряюсь в конфигах Nginx. Фронтенд для меня всегда был необходимым злом — чем-то, что приходится делать, когда нужен UI, но что хочется закончить как можно быстрее.
За последние пару лет я перепробовал несколько фреймворков для своих pet-проектов и в итоге остановился на SvelteKit. В этой статье расскажу почему.
Отправная точка: что мне вообще нужно
Мои типичные задачи на фронте:
- Статический блог (вот этот, например)
- Дашборд для мониторинга серверов
- Лендинг для side-проекта
- Простая админка для бота
Никаких сложных SPA с десятками экранов. Никаких enterprise-приложений с командой из 20 фронтендеров. Мне нужно что-то, что работает быстро, деплоится просто и не заставляет меня изучать экосистему размером с небольшую страну.
Next.js: мощно, но избыточно
Next.js — де-факто стандарт в React-мире. Я начинал с него и первое время был доволен. Но со временем накопились претензии.
Во-первых, React сам по себе многословен. Простой счётчик на React выглядит так:
import { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Счётчик: {count}</p>
<button onClick={() => setCount(count + 1)}>
+1
</button>
</div>
);
} Тот же компонент на Svelte:
<script>
let count = 0;
</script>
<p>Счётчик: {count}</p>
<button on:click={() => count++}>
+1
</button> Разница — не просто в количестве строк. В Svelte нет useState, нет useEffect, нет useCallback, нет useMemo. Реактивность встроена в язык. Переменная изменилась — UI обновился. Точка.
Во-вторых, экосистема Next.js перегружена. App Router, Server Components, Server Actions, Edge Runtime, Middleware, use client, use server — каждый год появляется новая концепция, которую нужно освоить. Для бэкендера, который заглядывает во фронт раз в месяц, это утомительно.
В-третьих, бандл. Даже минимальное Next.js-приложение тащит с собой React runtime (~40 KB gzipped). Svelte компилируется в чистый JavaScript без runtime — бандл получается заметно меньше.
Astro: почти идеально, но не совсем
Astro мне понравился больше. Философия «отправляй меньше JavaScript» — это именно то, что я хочу. Island-архитектура, поддержка Markdown из коробки, можно использовать компоненты из разных фреймворков.
Но у Astro есть ограничение: это в первую очередь генератор статических сайтов. Когда мне понадобился API-эндпоинт или серверный рендеринг с авторизацией, пришлось бы городить костыли. SvelteKit в этом плане универсальнее — он работает и как SSG, и как SSR, и как полноценный сервер с API-роутами.
Кроме того, Astro использует свой .astro-формат для компонентов, а для интерактивности всё равно нужен какой-то фреймворк. Получается лишний слой абстракции.
SvelteKit: что зацепило
Файловый роутинг, который имеет смысл
Структура папок в SvelteKit — это и есть роутинг. Никаких конфигов, никаких react-router, никаких магических файлов.
src/routes/
├── +page.svelte → /
├── +layout.svelte → layout для всех страниц
├── about/
│ └── +page.svelte → /about
├── blog/
│ ├── +page.svelte → /blog
│ └── [slug]/
│ ├── +page.svelte → /blog/some-post
│ └── +page.ts → загрузка данных
└── api/
└── health/
└── +server.ts → GET /api/health Для бэкендера эта структура интуитивна. Хочешь новую страницу — создай папку и файл. Хочешь API-эндпоинт — создай +server.ts. Всё.
Загрузка данных: просто и предсказуемо
В SvelteKit данные для страницы загружаются в +page.ts (или +page.server.ts для серверных данных):
// src/routes/blog/[slug]/+page.ts
export async function load({ params }) {
const post = await import(`$lib/posts/${params.slug}.md`);
return {
content: post.default,
meta: post.metadata
};
} Сравните с getServerSideProps / getStaticProps / App Router async компонентами в Next.js, которые менялись с каждой мажорной версией. В SvelteKit есть один способ загрузки данных, и он работает.
adapter-static: статика без боли
Для этого блога мне нужен статический сайт — набор HTML/CSS/JS-файлов, которые можно закинуть на любой сервер. В SvelteKit это делается одной строчкой в конфиге:
// svelte.config.js
import adapter from '@sveltejs/adapter-static';
import { mdsvex } from 'mdsvex';
/** @type {import('@sveltejs/kit').Config} */
const config = {
extensions: ['.svelte', '.md'],
preprocess: [mdsvex({ extensions: ['.md'] })],
kit: {
adapter: adapter({
pages: 'build',
assets: 'build',
fallback: null,
precompress: true
})
}
};
export default config; npm run build — и в папке build лежит готовый статический сайт. Его можно раздавать через Nginx, закинуть на GitHub Pages или положить куда угодно. Никакого Node.js-сервера в продакшне, никакого Docker-контейнера. Просто файлы.
Нет Virtual DOM
Это может звучать как мелочь, но для меня это принципиально. React и Vue используют Virtual DOM — промежуточное представление UI в памяти, которое потом сравнивается с реальным DOM для вычисления изменений.
Svelte работает иначе. Компилятор на этапе сборки генерирует императивный код, который точечно обновляет DOM. Нет diff-алгоритма в рантайме, нет лишних аллокаций в памяти. Результат — меньше кода, быстрее работа.
Как собран этот блог
Стек максимально простой:
- SvelteKit — фреймворк
- mdsvex — Markdown-процессор для Svelte (аналог MDX для React)
- Tailwind CSS — стили
- adapter-static — сборка в статику
Посты хранятся как .md-файлы в src/lib/posts/. Каждый файл содержит frontmatter с метаданными и собственно контент:
---
title: "Заголовок поста"
date: "2025-11-19"
tags:
- sveltekit
- webdev
published: true
---
Текст поста с поддержкой **Markdown** и `кода`. Список постов формируется динамически при сборке:
// src/lib/posts.ts
export async function getPosts() {
const paths = import.meta.glob('/src/lib/posts/*.md', { eager: true });
const posts = Object.entries(paths)
.map(([path, file]) => {
const slug = path.split('/').pop()?.replace('.md', '');
return {
slug,
...file.metadata
};
})
.filter((post) => post.published)
.sort((a, b) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
);
return posts;
} Tailwind: утилитарный CSS для тех, кто не любит CSS
Отдельно скажу про Tailwind. Я знаю, что у многих фронтендеров к нему неоднозначное отношение. Но для бэкендера, который не хочет писать CSS-файлы и придумывать имена классов, это идеальный инструмент:
<article class="prose prose-invert mx-auto max-w-3xl px-4 py-8">
<h1 class="text-3xl font-bold tracking-tight text-white">
{post.title}
</h1>
<time class="text-sm text-zinc-400">
{formatDate(post.date)}
</time>
<svelte:component this={post.content} />
</article> Всё в одном месте: разметка, стили, логика. Открываешь файл — и сразу понимаешь, как выглядит компонент.
Деплой
Деплой статического SvelteKit-сайта — это scp (или rsync) на сервер:
npm run build
rsync -avz --delete build/ user@server:/var/www/blog/ В Nginx достаточно минимального конфига:
server {
listen 80;
server_name blog.example.com;
root /var/www/blog;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# Кэширование статики
location ~* .(js|css|png|jpg|svg|woff2)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
} Можно добавить CI/CD через GitHub Actions или GitLab CI, но для блога с тремя читателями это overkill.
Итого
Если вы бэкенд-разработчик и вам нужен фронтенд-фреймворк для side-проектов, попробуйте SvelteKit. Вот мой чеклист:
| Критерий | Next.js | Astro | SvelteKit |
|---|---|---|---|
| Простота для новичка | Средне | Просто | Просто |
| Размер бандла | Большой | Маленький | Маленький |
| Статическая сборка | Да | Да | Да |
| SSR + API | Да | Ограниченно | Да |
| Многословность кода | Высокая | Средняя | Низкая |
| Runtime в браузере | React (~40 KB) | Минимальный | Нет |
SvelteKit не лучший выбор для enterprise-проекта с командой из 50 человек (там экосистема React пока выигрывает). Но для персональных проектов, блогов, дашбордов — это самый продуктивный инструмент, который я нашёл.
А этот блог — живое доказательство. Собран за выходные, деплоится одной командой, грузится мгновенно.