SvelteKit 3 можно освоить на одном небольшом проекте, если сразу видеть вход, действие и результат. Трекер привычек показывает базовые части SvelteKit на видимом сценарии: форма создаёт запись, кнопка меняет состояние, а прогресс пересчитывается автоматически. Ниже вы соберёте работающий пример и проверите обычный и граничный сценарии.
В первом фреймворк-проекте легко утонуть в маршрутах и серверных API, не успев понять реактивность компонента. Поэтому отделим настройку, входные данные, основное действие и проверку. В этой статье вы соберёте одностраничный трекер с добавлением привычки, отметкой выполнения и восстановлением данных после перезагрузки. Проверка: новая привычка появляется без перезагрузки, прогресс меняется после клика, а localStorage возвращает список при повторном открытии.
создать SvelteKit-приложение и открыть маршрут /. Не расширяйте проект до первого успешного запуска.
добавление обновляет список сразу. Проверяйте значение, а не отсутствие ошибки.
После успеха добавьте серию дней и отдельный фильтр «только невыполненные». Так копирование станет практикой.
Как устроен учебный проект SvelteKit 3
Страница хранит массив привычек, производное значение считает выполненные элементы, effect синхронизирует данные с localStorage, а шаблон отображает состояние. Держите ввод, логику и отображение раздельно: тогда каждый этап можно проверить отдельно.
| Этап | Действие | Проверяемый результат |
|---|---|---|
| Проект | создать SvelteKit-приложение и открыть маршрут / | страница загружается без ошибок |
| Состояние | описать массив привычек через $state | добавление обновляет список сразу |
| Производное | посчитать completed через $derived | счётчик меняется после отметки |
| Хранение | читать и записывать localStorage только в браузере | перезагрузка восстанавливает данные |
Контрольные точки: проект, состояние, производное и хранение. Следующий шаг начинается после совпадения с ожидаемым результатом.

От входных данных до видимого результата. Сначала проверяется вход, затем действие и только потом интерфейс.
Собираем рабочий пример шаг за шагом
Пример сверялся с актуальной документацией официальному релизу SvelteKit 3 на 19 октября 2026 года. Создайте проект командой npx sv create habits, выберите TypeScript и минимальный шаблон, затем выполните npm run dev. Проверьте текущую папку через pwd или Get-Location. Вставьте пример целиком, выполните один раз, затем разбирайте по частям.
<script lang="ts">
import { browser } from '$app/environment'
type Habit = { id: number; title: string; done: boolean }
let title = $state('')
let habits = $state<Habit[]>(browser ? JSON.parse(localStorage.getItem('habits') ?? '[]') : [])
let completed = $derived(habits.filter((habit) => habit.done).length)
$effect(() => {
if (browser) localStorage.setItem('habits', JSON.stringify(habits))
})
function addHabit() {
const clean = title.trim()
if (!clean) return
habits.push({ id: Date.now(), title: clean, done: false })
title = ''
}
</script>
<h1>Привычки: {completed}/{habits.length}</h1>
<form onsubmit={(event) => { event.preventDefault(); addHabit() }}>
<input bind:value={title} placeholder="Например, 20 минут кода" />
<button>Добавить</button>
</form>
{#each habits as habit (habit.id)}
<label><input type="checkbox" bind:checked={habit.done} /> {habit.title}</label>
{/each}- Форма добавляет непустую привычку и сразу очищает поле.
- Счётчик
completed/totalменяется после отметки без ручного обновления DOM. - Повторный запуск даёт тот же предсказуемый результат.
- Пустой ввод получает понятное сообщение вместо падения.
$state хранит изменяемые данные, $derived вычисляет значение из них, а $effect выполняет внешнюю синхронизацию. Найдите в примере вход, проверку и место формирования результата.

Что отличает устойчивый проект от случайного запуска. Явные границы делают ошибку локальной и понятной.
Что происходит внутри и почему это работает
Реактивность отслеживает чтение и изменение состояния. Вызов push меняет проксированный массив, поэтому список и производный счётчик обновляются. localStorage доступен только в браузере, что важно для серверного рендеринга SvelteKit. Логику держите отдельно от вывода: функция получает аргумент и возвращает значение, а интерфейс показывает его. Хранение является побочным эффектом, но чтение при инициализации должно учитывать SSR. Проверка browser защищает код от обращения к window на сервере.
| Часть | За что отвечает | Что проверить |
|---|---|---|
| $state | хранит поле формы и массив привычек | изменение массива обновляет список |
| $derived | считает выполненные элементы из текущего массива | значение совпадает с числом отмеченных checkbox |
| $effect | сохраняет сериализованный массив во внешнее хранилище | перезагрузка возвращает последнее состояние |
Не используйте effect для вычисления completed: это производное значение, ему не нужно отдельное изменяемое состояние. Чем меньше дублирующихся переменных, тем меньше расхождений. Effect оставляйте для синхронизации с внешним миром: storage, сетью или сторонним API. Не прячьте поток в одном обработчике: сохраните вход, проверьте его, вызовите операцию и только затем обновите интерфейс.
Проверяем проект и добавляем свою функцию
Добавьте удаление привычки и кнопку «Сбросить отметки». Оба действия должны автоматически обновлять счётчик и localStorage. До правки запишите ожидаемое поведение одним предложением. После граничного теста повторите обычный сценарий.
- Сохраните ожидаемый результат: новая привычка появляется без перезагрузки, прогресс меняется после клика, а localStorage возвращает список при повторном открытии.
- Измените правило: добавьте серию дней и отдельный фильтр «только невыполненные». Сначала предскажите итог.
- Передайте граничное значение и убедитесь, что приложение не падает.
- Верните корректные данные и объясните роли трёх основных частей.
- страница загружается без ошибок
- добавление обновляет список сразу
- счётчик меняется после отметки
- перезагрузка восстанавливает данные
Хороший Svelte-компонент хранит минимальное состояние и выводит всё остальное из него. Полезная правка проходит от нового ввода до видимого результата.

Четыре проверки перед следующим шагом. Каждый следующий этап начинается после наблюдаемого результата.
Что запомнить и куда двигаться дальше
Следующий шаг - вынести HabitRow в компонент и добавить серверное хранилище только если данные должны синхронизироваться между устройствами. Для закрепления откройте в проекте React + Vite, в todo list на чистом JavaScript и в офлайн PWA.
Как изучить тему в Кодике
Сначала закрепите события формы, массивы и TypeScript-типы, затем разбирайте runes и SSR. Сначала восстановите главную функцию и проверьте её на трёх значениях.
| Шаг | Что разобрать | Мини-проверка |
|---|---|---|
| 1. Основа | Формы, массивы, map/filter и localStorage | Объясните вход и выход функции. |
| 2. Инструмент | $state, $derived, $effect и условия browser | Повторите команду по памяти. |
| 3. Проект | Трекер с добавлением, отметкой и сохранением | Проверьте два сценария. |
| 4. Рефакторинг | Компоненты, тесты и серверное хранилище | Разделите ввод, логику и вывод. |
Удалите localStorage и убедитесь, что основная логика всё ещё работает. Затем верните синхронизацию как отдельный слой. Базовый синтаксис закрепите через курс JavaScript.
Во время SSR объекта window нет. Используйте browser или onMount.
Счётчик расходится с массивом. Вычисляйте его через $derived.
Пробелы создают бессмысленные строки. Применяйте trim и ранний return.
Индекс меняется после удаления. Используйте устойчивый id в each-блоке.
Что получится в итоге

Так выглядит законченный учебный проект: есть понятный вход, видимый результат, защита от неверных данных и место для следующего улучшения.
Чем $derived отличается от $state?
$state хранит изменяемое значение, а $derived вычисляется из другого состояния.
Почему localStorage проверяется через browser?
SvelteKit может выполнить компонент на сервере, где браузерных API нет.
Что проверять первым, если SvelteKit 3 не запускается?
Версию среды, текущую папку, команду запуска и первую строку ошибки.
Как понять, что проект SvelteKit 3 работает?
Проверьте основной сценарий, неверный ввод, повторный запуск и один граничный случай.
Можно ли начать с готового кода?
Да. Добейтесь результата, измените одно правило и восстановите ключевую функцию без копирования.
Выберите одно улучшение и проведите его от ввода до результата.
Если не хватает основы, перейдите курс JavaScript, затем восстановите ключевую функцию без подсказки.