TanStack Start full-stack React framework with Bun runtime. Use for TanStack Router, server functions, vinxi, or encountering SSR, build, preset errors.
Provides TanStack Start full-stack framework support with Bun runtime. Use when creating SSR React apps with file-based routing, server functions, or encountering Vinxi build errors.
/plugin marketplace add secondsky/claude-skills/plugin install bun@claude-skillsThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Run TanStack Start (full-stack React framework) with Bun.
# Create new TanStack Start project
bunx create-tanstack-start@latest my-app
cd my-app
# Install dependencies
bun install
# Development
bun run dev
# Build
bun run build
# Preview
bun run start
{
"scripts": {
"dev": "vinxi dev",
"build": "vinxi build",
"start": "vinxi start"
},
"dependencies": {
"@tanstack/react-router": "^1.139.0",
"@tanstack/start": "^1.120.0",
"react": "^19.2.0",
"react-dom": "^19.2.0",
"vinxi": "^0.5.10"
}
}
import { defineConfig } from "@tanstack/start/config";
export default defineConfig({
server: {
preset: "bun",
},
});
app/
├── routes/
│ ├── __root.tsx # Root layout
│ ├── index.tsx # /
│ ├── about.tsx # /about
│ ├── users/
│ │ ├── index.tsx # /users
│ │ └── $userId.tsx # /users/:userId
│ └── api/
│ └── users.ts # /api/users
└── client.tsx
// app/routes/index.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
return <h1>Welcome Home</h1>;
}
// app/routes/users/index.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/users/")({
loader: async () => {
const response = await fetch("/api/users");
return response.json();
},
component: Users,
});
function Users() {
const users = Route.useLoaderData();
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
// app/routes/users/$userId.tsx
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/users/$userId")({
loader: async ({ params }) => {
const response = await fetch(`/api/users/${params.userId}`);
return response.json();
},
component: UserDetail,
});
function UserDetail() {
const user = Route.useLoaderData();
const { userId } = Route.useParams();
return (
<div>
<h1>{user.name}</h1>
<p>User ID: {userId}</p>
</div>
);
}
// app/routes/users/index.tsx
import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/start";
import { Database } from "bun:sqlite";
const getUsers = createServerFn("GET", async () => {
const db = new Database("data.sqlite");
const users = db.query("SELECT * FROM users").all();
db.close();
return users;
});
const createUser = createServerFn("POST", async (name: string) => {
const db = new Database("data.sqlite");
db.run("INSERT INTO users (name) VALUES (?)", [name]);
db.close();
return { success: true };
});
export const Route = createFileRoute("/users/")({
loader: () => getUsers(),
component: Users,
});
function Users() {
const users = Route.useLoaderData();
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const name = formData.get("name") as string;
await createUser(name);
// Refetch or update state
};
return (
<div>
<form onSubmit={handleSubmit}>
<input name="name" placeholder="Name" />
<button type="submit">Add User</button>
</form>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
import { createServerFn } from "@tanstack/start";
import { getWebRequest } from "@tanstack/start/server";
const getSession = createServerFn("GET", async () => {
const request = getWebRequest();
const cookies = request.headers.get("Cookie");
// Parse and validate session
return { userId: "123", role: "admin" };
});
const protectedAction = createServerFn("POST", async (data: any) => {
const session = await getSession();
if (session.role !== "admin") {
throw new Error("Unauthorized");
}
// Perform action
return { success: true };
});
// app/routes/api/users.ts
import { createAPIFileRoute } from "@tanstack/start/api";
import { Database } from "bun:sqlite";
export const Route = createAPIFileRoute("/api/users")({
GET: async ({ request }) => {
const db = new Database("data.sqlite");
const users = db.query("SELECT * FROM users").all();
db.close();
return Response.json(users);
},
POST: async ({ request }) => {
const { name } = await request.json();
const db = new Database("data.sqlite");
db.run("INSERT INTO users (name) VALUES (?)", [name]);
db.close();
return Response.json({ success: true });
},
});
// app/routes/__root.tsx
import { createRootRoute, Link, Outlet } from "@tanstack/react-router";
export const Route = createRootRoute({
component: Root,
});
function Root() {
return (
<html>
<head>
<meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>My App</title>
</head>
<body>
<nav>
<Link to="/">Home</Link>
<Link to="/users">Users</Link>
<Link to="/about">About</Link>
</nav>
<main>
<Outlet />
</main>
</body>
</html>
);
}
// app/routes/users/$userId.tsx
export const Route = createFileRoute("/users/$userId")({
loader: async ({ params }) => {
const response = await fetch(`/api/users/${params.userId}`);
if (!response.ok) {
throw new Error("User not found");
}
return response.json();
},
errorComponent: ({ error }) => (
<div>
<h1>Error</h1>
<p>{error.message}</p>
</div>
),
pendingComponent: () => <div>Loading...</div>,
component: UserDetail,
});
// app/routes/users/index.tsx
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
const searchSchema = z.object({
page: z.number().default(1),
limit: z.number().default(10),
search: z.string().optional(),
});
export const Route = createFileRoute("/users/")({
validateSearch: searchSchema,
loader: async ({ search }) => {
const { page, limit, search: query } = search;
// Fetch with pagination
return fetchUsers({ page, limit, query });
},
component: Users,
});
NITRO_PRESET=bun bun run build
bun .output/server/index.mjs
FROM oven/bun:1 AS builder
WORKDIR /app
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile
COPY . .
RUN bun run build
FROM oven/bun:1
WORKDIR /app
COPY --from=builder /app/.output ./output
EXPOSE 3000
CMD ["bun", ".output/server/index.mjs"]
| Error | Cause | Fix |
|---|---|---|
Cannot find bun:sqlite | Wrong preset | Set server.preset: "bun" |
Server function failed | Network error | Check function definition |
Route not found | File naming | Check route file location |
Hydration mismatch | Server/client diff | Check loader data |
Load references/router-api.md when:
Load references/forms.md when:
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.