DEV Community

Alex Spinov
Alex Spinov

Posted on

TanStack Start Has a Free Full-Stack Framework: Type-Safe Routing, Server Functions, and Full-Document SSR

TanStack Router proved that routing can be fully type-safe — every param, search query, and loader return type checked at compile time. But it was client-only.

What if you got that same type safety across the full stack? Server functions, SSR, streaming — all with end-to-end TypeScript inference?

That's TanStack Start.

What TanStack Start Gives You

  • 100% type-safe routing — route params, search params, loader data — all inferred
  • Server functions — call backend code from components with createServerFn
  • Full-document SSR — server renders the complete HTML document, not just React
  • Streaming — progressive rendering with Suspense boundaries
  • Search params as state — URL search params are first-class, validated state
  • Framework agnostic goal — React first, other frameworks planned

Quick Start

npx create-start@latest my-app
cd my-app && npm install && npm run dev
Enter fullscreen mode Exit fullscreen mode

Type-Safe Route Params

// src/routes/users/$userId.tsx
import { createFileRoute } from "@tanstack/react-router";

export const Route = createFileRoute("/users/$userId")({
  loader: async ({ params }) => {
    // params.userId is typed as string — not `any`
    const user = await fetchUser(params.userId);
    return { user };
  },
  component: UserPage,
});

function UserPage() {
  const { user } = Route.useLoaderData();
  // user is fully typed based on loader return
  return <h1>{user.name}</h1>;
}
Enter fullscreen mode Exit fullscreen mode

Rename a route param? TypeScript catches every broken reference instantly.

Search Params as Validated State

import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";

const searchSchema = z.object({
  page: z.number().default(1),
  sort: z.enum(["name", "date", "price"]).default("date"),
  filter: z.string().optional(),
});

export const Route = createFileRoute("/products")({
  validateSearch: searchSchema,
  component: ProductList,
});

function ProductList() {
  const { page, sort, filter } = Route.useSearch();
  // All typed! page: number, sort: "name" | "date" | "price"

  const navigate = Route.useNavigate();

  return (
    <div>
      <select 
        value={sort}
        onChange={(e) => navigate({ search: { sort: e.target.value } })}
      >
        <option value="name">Name</option>
        <option value="date">Date</option>
        <option value="price">Price</option>
      </select>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

URL becomes the source of truth: ?page=2&sort=price&filter=shoes. Shareable, bookmarkable, type-safe.

Server Functions

import { createServerFn } from "@tanstack/start";

const getProducts = createServerFn("GET", async (search: string) => {
  // Runs on server — direct DB access, no API route needed
  const products = await db.product.findMany({
    where: { name: { contains: search } },
  });
  return products;
});

// Use directly in components or loaders
const products = await getProducts("laptop");
Enter fullscreen mode Exit fullscreen mode

Nested Layouts with Parallel Loading

// src/routes/__root.tsx — app shell
export const Route = createRootRoute({
  component: () => (
    <html>
      <body>
        <Nav />
        <Outlet />  {/* Child routes render here */}
      </body>
    </html>
  ),
});

// src/routes/dashboard.tsx — dashboard layout
export const Route = createFileRoute("/dashboard")({
  loader: () => fetchDashboardData(),  // Loads in parallel with child
  component: () => (
    <div className="dashboard-layout">
      <Sidebar />
      <Outlet />
    </div>
  ),
});

// src/routes/dashboard/analytics.tsx
export const Route = createFileRoute("/dashboard/analytics")({
  loader: () => fetchAnalytics(),  // Loads in parallel with parent
  component: Analytics,
});
Enter fullscreen mode Exit fullscreen mode

When to Choose TanStack Start

Choose TanStack Start when:

  • Type safety is non-negotiable — you want the compiler to catch routing bugs
  • Search params are a core part of your UX (filters, pagination, table state)
  • You already use TanStack Query/Table/Form and want the full ecosystem
  • You want React SSR without Next.js opinions

Skip TanStack Start when:

  • You need a battle-tested production framework today (Start is newer)
  • Your team doesn't use TypeScript
  • You need extensive middleware/edge runtime support

The Bottom Line

TanStack Start brings the type safety that TanStack Router pioneered to the full stack. If you've ever had a runtime error from a misspelled route param or an incorrect search query — Start eliminates that class of bugs entirely.

Start here: tanstack.com/start


Need custom data extraction, scraping, or automation? I build tools that collect and process data at scale — 78 actors on Apify Store and 265+ open-source repos. Email me: Spinov001@gmail.com | My Apify Actors

Top comments (0)