
TanStack Start — полный разбор нового React фреймворка
Если ты слышал про TanStack - скорее всего думаешь про TanStack Query или TanStack Router. Оба инструмента заработали сильную репутацию. Но TanStack Start - это другое. Это полноценный full-stack React фреймворк который в 2026 году стал серьёзным конкурентом Next.js.
Я сам на Next.js 15 - поэтому изучил TanStack Start внимательно. Вот что нужно знать.
React, Next.js, TanStack Start - кто есть кто
Прежде чем делать выбор - важно понять базовое различие которое часто упускают.
React - это библиотека, а не фреймворк. Это движок для рендеринга компонентов и управления состоянием. Сам по себе React не даёт тебе роутинг, data fetching, сборку или стратегию рендеринга. Ты собираешь это сам.
Next.js и TanStack Start - это фреймворки поверх React. Они принимают за тебя решения которые иначе пришлось бы принимать самому.
Самая частая ошибка - выбирают Next.js «потому что популярный», а потом никогда не используют серверный рендеринг потому что всё приложение за авторизацией. В итоге не используешь все преимущества, потому что они в общем не нужны.
Что такое TanStack Start
TanStack Start - это full-stack React мета-фреймворк построенный поверх TanStack Router, использующий Vite как инструмент сборки и Vinxi как абстракцию бандлера.
Главная философия - «просто React». Никаких React Server Components. Никакой магии компилятора. Никаких директив 'use client'. Ты пишешь React компоненты так как всегда - а фреймворк даёт тебе full-stack возможности поверх этого.
Фреймворк создал Таннер Линслей - разработчик TanStack Query, TanStack Router и TanStack Table. Люди которые ими пользуются знают его подход: строгая типизация, предсказуемость, никакой магии.
Чем отличается от Next.js
Next.js - RSC-first. Компоненты рендерятся на сервере по умолчанию. Ты opt-in в клиентское поведение через 'use client'.
TanStack Start - клиент-first. Сервер - это возможность к которой ты явно обращаешься, а не дефолтный контекст рендеринга. Это фундаментальное архитектурное различие.
// Next.js - сервер по умолчанию, opt-in в клиент
'use client'
import { useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
return <button onClick={() => setCount(c => c + 1)}>{count}</button>
}
// TanStack Start - клиент по умолчанию,
// серверные функции вызываются явно
import { createServerFn } from '@tanstack/start'
const getUser = createServerFn({ method: 'GET' })
.handler(async () => {
return db.user.findFirst() // выполняется только на сервере
})
Server Functions - замена API роутам
Одна из самых интересных концепций TanStack Start. Server Functions - это функции которые гарантированно выполняются на сервере. Вместо того чтобы писать отдельный API endpoint - ты просто создаёшь серверную функцию и вызываешь её напрямую из компонента.
// Раньше нужно было два файла:
// 1. /api/notes/route.ts - API endpoint
// 2. компонент - fetch('/api/notes')
// TanStack Start - один файл, одна функция
import { createServerFn } from '@tanstack/start'
import { db } from '~/db'
// Эта функция выполняется на сервере
// но вызывается как обычная JS функция
export const getNotes = createServerFn({ method: 'GET' })
.handler(async () => {
return db.note.findMany({ orderBy: { createdAt: 'desc' } })
})
// В компоненте - просто вызываем как обычную функцию
function NotesPage() {
const notes = Route.useLoaderData()
return <ul>{notes.map(n => <li key={n.id}>{n.title}</li>)}</ul>
}
Никаких fetch('/api/...'). Никаких отдельных файлов для API. Типы автоматически прокидываются с сервера на клиент.
Роутинг и загрузка данных
Роутинг в TanStack Start - файловый, как в Next.js. Но данные загружаются через loader функции которые запускаются до рендеринга компонента - параллельно для всех совпавших роутов.
// src/routes/notes/$noteId.tsx
import { createFileRoute } from '@tanstack/react-router'
import { getNote } from '~/server/notes'
export const Route = createFileRoute('/notes/$noteId')({
// validateSearch типизирует query параметры
validateSearch: z.object({
tab: z.enum(['preview', 'edit']).optional(),
}),
// loader запускается на сервере при SSR
// и на клиенте при навигации
loader: async ({ params }) => {
return getNote({ data: params.noteId })
},
component: NoteComponent,
})
function NoteComponent() {
// Всё полностью типизировано - TypeScript знает
// тип данных из loader
const note = Route.useLoaderData()
const { noteId } = Route.useParams()
const { tab } = Route.useSearch()
return <article>{note.content}</article>
}
Параллельная загрузка данных - killer фича. Когда ты переходишь на /notes/123 - loader для /notes и loader для /notes/123 запускаются одновременно. Не последовательно как в useEffect.
Сквозная типизация - главное преимущество
Это то где TanStack Start реально выделяется. Типы проходят от базы данных через серверные функции до компонентов без потерь - без единого any и без ручных аннотаций.
// db/schema.ts - определяем схему один раз
export const notes = pgTable('notes', {
id: text('id').primaryKey(),
title: text('title').notNull(),
content: text('content').notNull(),
createdAt: timestamp('created_at').defaultNow(),
})
// server/notes.ts - серверная функция с типом из схемы
export const getNote = createServerFn({ method: 'GET' })
.validator(z.string()) // валидация входных данных
.handler(async ({ data: noteId }) => {
const note = await db.query.notes.findFirst({
where: eq(notes.id, noteId),
})
if (!note) throw notFound()
return note // тип автоматически выведен из Drizzle
})
// routes/notes/$noteId.tsx - тип пришёл с сервера
function NoteComponent() {
const note = Route.useLoaderData()
// note.title, note.content, note.createdAt
// - всё типизировано, TypeScript знает каждое поле
}
Никаких as Note, никаких ! операторов. Вся цепочка от PostgreSQL до JSX типизирована.
Middleware - защита серверных функций
Middleware в TanStack Start - это переиспользуемые цепочки логики которые применяются к серверным функциям. Авторизация, логирование, CSP - всё через middleware.
// middleware.ts
import { createMiddleware } from '@tanstack/start'
import { auth } from './my-auth'
// Создаём middleware для авторизации
export const authMiddleware = createMiddleware().server(
async ({ next, request }) => {
const session = await auth.getSession({
headers: request.headers
})
if (!session) {
throw new Error('Unauthorized')
}
// Прокидываем сессию в контекст - типизировано
return next({ context: { session } })
}
)
// Middleware для проверки прав
export const adminMiddleware = createMiddleware()
.middleware([authMiddleware]) // композиция middleware
.server(async ({ next, context }) => {
if (context.session.user.role !== 'admin') {
throw new Error('Forbidden')
}
return next()
})
// server/notes.ts - применяем middleware к функции
export const deleteNote = createServerFn({ method: 'POST' })
.middleware([authMiddleware]) // только авторизованные
.validator(z.string())
.handler(async ({ data: noteId, context }) => {
// context.session типизирован из middleware
const note = await db.query.notes.findFirst({
where: and(
eq(notes.id, noteId),
eq(notes.userId, context.session.user.id)
),
})
if (!note) throw notFound()
await db.delete(notes).where(eq(notes.id, noteId))
})
Важный момент который часто упускают: route guard и middleware - это разные вещи. Route guard (beforeLoad) защищает UX - перенаправляет неавторизованного пользователя до рендеринга страницы. Middleware в серверной функции защищает данные - потому что серверная функция это HTTP endpoint который можно вызвать напрямую минуя роут.
Структура проекта
src/
routes/
__root.tsx # корневой layout
index.tsx # главная страница
notes/
index.tsx # /notes
$noteId.tsx # /notes/:id
_protected/ # группа защищённых роутов
dashboard.tsx # /dashboard
settings.tsx # /settings
server/
notes.ts # серверные функции для заметок
auth.ts # серверные функции для авторизации
middleware.ts # глобальные middleware
start.ts # конфигурация старта
db/
index.ts # подключение к базе
schema.ts # схема Drizzle
Файловый роутинг понятен - но структура серверных функций в отдельной папке это паттерн который команда TanStack рекомендует. Логика сервера отдельно от UI - принцип чистой архитектуры.
Деплой без привязки к платформе
Ты деплоишь на AWS Lambda, Cloudflare Workers, обычный Node.js сервер или Bun - и фреймворк формирует вывод соответственно через адаптеры.
// vite.config.ts
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
export default defineConfig({
plugins: [
tanstackStart({
server: {
preset: 'cloudflare-worker', // или 'node', 'aws-lambda', 'bun'
}
})
]
})
Это принципиальное отличие от Next.js который оптимизирован под Vercel. Ничего плохого в Vercel - но если твоя инфраструктура на других платформах, ты это чувствуешь.
Один разработчик который год держит TanStack Start в продакшне: два приложения на Cloudflare Workers, полностью stateless, общаются с бэкенд API через tRPC. Весь стек портируемый потому что ничего не зависит от проприетарных фич платформы.
Когда выбирать каждый инструмент
TanStack Start:
- Динамические приложения - дашборды, SaaS, admin panels, internal tools
- Типизация критически важна на всём стеке
- Команда уже использует TanStack Router или Query
- Нужна гибкость в деплое без vendor lock-in
- Важна прозрачность - никакой магии под капотом
Next.js:
- Публичные сайты где важен SEO и первая загрузка
- Контентные сайты, блоги, документация, e-commerce
- Команда уже инвестировала в RSC модель
- Нужна глубина экосистемы - 1.5M еженедельных загрузок vs ~50k у TanStack Start
Plain React + Vite:
- Всё приложение за авторизацией - никакой поисковик не видит
- Нужна минимальная абстракция и полный контроль
- Важна предсказуемость - Vite стабилен и редко меняется под тобой
Текущий статус
TanStack Start - около 50 000 еженедельных загрузок npm на середину 2026 года. Разрыв с Next.js примерно 30 к 1. Community-ответы на edge cases малочисленнее. Документации меньше.
Но траектория важна. Рост TanStack Start в 2025-2026 повторяет то как выглядело принятие Vite в 2021-2022. Быстрый рост среди опытных разработчиков - с более широким принятием следом.
Стоит ли смотреть
Я на Next.js 15 - и пока менять не планирую. Экосистема, документация, поддержка React 19 - всё работает. Но TanStack Start слежу внимательно.
Если бы начинал новый проект сегодня - особенно dashboard или SaaS где важна сквозная типизация и гибкость деплоя - серьёзно рассмотрел бы TanStack Start.
Это уже не экспериментальный инструмент. Люди разрабатывают реальные приложения - auth, billing, RBAC, i18n, e2e тесты. И разрыв с Next.js по возможностям продолжает сокращаться.
Выбирай инструмент под задачу а не под тренд. Правильный фреймворк тот который соответствует твоей модели данных. 🚀

Комментарии