Почему я перешёл на SvelteKit для своих проектов

sveltekitfrontendwebdev

Процесс сборки SvelteKit: от Markdown и Svelte до статического HTML

Я бэкендер. Пишу на 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.jsAstroSvelteKit
Простота для новичкаСреднеПростоПросто
Размер бандлаБольшойМаленькийМаленький
Статическая сборкаДаДаДа
SSR + APIДаОграниченноДа
Многословность кодаВысокаяСредняяНизкая
Runtime в браузереReact (~40 KB)МинимальныйНет

SvelteKit не лучший выбор для enterprise-проекта с командой из 50 человек (там экосистема React пока выигрывает). Но для персональных проектов, блогов, дашбордов — это самый продуктивный инструмент, который я нашёл.

А этот блог — живое доказательство. Собран за выходные, деплоится одной командой, грузится мгновенно.

© 2026 Terminal Notes. Built with SvelteKit.