tanstack-start-project-setup

$npx mdskill add netlify/swar-templates/tanstack-start-project-setup

Initialize TanStack Start projects with router, query, and build configs.

  • Creates new React applications with integrated routing and query.
  • Depends on TanStack Router, Vinxi, Vite, and TypeScript.
  • Analyzes project goals to select appropriate configuration templates.
  • Generates complete project structures including server functions and layouts.
SKILL.md
.github/skills/tanstack-start-project-setupView on GitHub ↗
---
name: tanstack-start-project-setup
description: Set up and configure TanStack Start projects. Use when creating new projects, configuring the router, setting up TanStack Query integration, or configuring build settings.
license: Apache-2.0
metadata:
  author: tanstack
  version: "1.0"
---

# TanStack Start Project Setup

TanStack Start is a full-stack React framework built on TanStack Router, Vinxi, and Vite.

## When to Use

- Creating a new TanStack Start project
- Configuring router settings
- Setting up TanStack Query
- Adding TypeScript configuration
- Configuring for Netlify deployment

## Quick Start

```bash
# Create new project
npx create-tanstack-start@latest my-app

# Or with specific template
npx create-tanstack-start@latest my-app --template basic
```

## Project Structure

```
my-app/
├── src/
│   ├── routes/
│   │   ├── __root.tsx        # Root layout
│   │   ├── index.tsx         # Home page (/)
│   │   └── ...
│   ├── server/
│   │   └── *.functions.ts    # Server functions
│   ├── components/
│   ├── lib/
│   ├── router.tsx            # Router configuration
│   └── routeTree.gen.ts      # Auto-generated
├── public/
├── app.config.ts             # TanStack Start config
├── package.json
├── tsconfig.json
└── netlify.toml              # Netlify deployment
```

## Essential Files

### app.config.ts

```typescript
// app.config.ts
import { defineConfig } from '@tanstack/react-start/config';

export default defineConfig({
  // Vite configuration
  vite: {
    // Add Vite plugins here
    plugins: [],
  },
  
  // Server configuration
  server: {
    // Server preset (netlify, vercel, node, etc.)
    preset: 'netlify',
  },
  
  // Router configuration
  tsr: {
    // Route file location
    routesDirectory: './src/routes',
    
    // Generated route tree location
    generatedRouteTree: './src/routeTree.gen.ts',
  },
});
```

### router.tsx

```tsx
// src/router.tsx
import { createRouter } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';

export function getRouter() {
  const router = createRouter({
    routeTree,
    
    // Enable scroll restoration
    scrollRestoration: true,
    
    // Preload on hover
    defaultPreload: 'intent',
    
    // Preload stale time
    defaultPreloadStaleTime: 0,
    
    // Default error component
    defaultErrorComponent: ({ error }) => (
      <div>Error: {error.message}</div>
    ),
    
    // Default pending component
    defaultPendingComponent: () => <div>Loading...</div>,
    
    // Default not found component
    defaultNotFoundComponent: () => <div>Not Found</div>,
  });
  
  return router;
}

// Type registration for full type safety
declare module '@tanstack/react-router' {
  interface Register {
    router: ReturnType<typeof getRouter>;
  }
}
```

### Root Route (__root.tsx)

```tsx
// src/routes/__root.tsx
import {
  Outlet,
  createRootRoute,
  HeadContent,
  Scripts,
} from '@tanstack/react-router';
import type { ReactNode } from 'react';

export const Route = createRootRoute({
  head: () => ({
    meta: [
      { charSet: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { title: 'My TanStack Start App' },
      { name: 'description', content: 'Built with TanStack Start' },
    ],
    links: [
      { rel: 'stylesheet', href: '/styles.css' },
      { rel: 'icon', href: '/favicon.ico' },
    ],
  }),
  component: RootComponent,
});

function RootComponent() {
  return (
    <html lang="en">
      <head>
        <HeadContent />
      </head>
      <body>
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}
```

## TanStack Query Integration

### Installation

```bash
npm install @tanstack/react-query
```

### Setup

```tsx
// src/router.tsx
import { createRouter } from '@tanstack/react-router';
import { QueryClient } from '@tanstack/react-query';
import { routeTree } from './routeTree.gen';

export function getRouter() {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        staleTime: 1000 * 60, // 1 minute
        gcTime: 1000 * 60 * 5, // 5 minutes
      },
    },
  });
  
  const router = createRouter({
    routeTree,
    context: {
      queryClient,
    },
  });
  
  return router;
}

declare module '@tanstack/react-router' {
  interface Register {
    router: ReturnType<typeof getRouter>;
  }
}
```

### Root with Query Provider

```tsx
// src/routes/__root.tsx
import { QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';

export const Route = createRootRouteWithContext<{
  queryClient: QueryClient;
}>()({
  component: RootComponent,
});

function RootComponent() {
  const { queryClient } = Route.useRouteContext();
  
  return (
    <QueryClientProvider client={queryClient}>
      <html lang="en">
        <head>
          <HeadContent />
        </head>
        <body>
          <Outlet />
          <Scripts />
          <ReactQueryDevtools />
        </body>
      </html>
    </QueryClientProvider>
  );
}
```

## TypeScript Configuration

```json
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "module": "ESNext",
    "moduleResolution": "bundler",
    "jsx": "react-jsx",
    "strict": true,
    "noEmit": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "paths": {
      "~/*": ["./src/*"]
    }
  },
  "include": ["src/**/*", "app.config.ts"],
  "exclude": ["node_modules"]
}
```

## Netlify Deployment

### netlify.toml

```toml
[build]
  command = "npm run build"
  publish = ".output/public"

[build.environment]
  NODE_VERSION = "20"

# Functions directory (auto-configured by TanStack Start)
[functions]
  directory = ".output/server"
```

### app.config.ts for Netlify

```typescript
// app.config.ts
import { defineConfig } from '@tanstack/react-start/config';

export default defineConfig({
  server: {
    preset: 'netlify',
  },
});
```

## Environment Variables

### .env Files

```bash
# .env (local development)
DATABASE_URL=postgres://localhost:5432/mydb
VITE_APP_NAME=My App

# .env.production (production)
DATABASE_URL=postgres://prod-server:5432/mydb
VITE_APP_NAME=My App
```

### Accessing Variables

```typescript
// Server-side (server functions, loaders on server)
const dbUrl = process.env.DATABASE_URL;

// Client-side (must be prefixed with VITE_)
const appName = import.meta.env.VITE_APP_NAME;
```

## CSS/Styling Setup

### Tailwind CSS

```bash
npm install tailwindcss @tailwindcss/vite
```

```typescript
// app.config.ts
import { defineConfig } from '@tanstack/react-start/config';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  vite: {
    plugins: [tailwindcss()],
  },
});
```

```css
/* src/styles.css */
@import 'tailwindcss';
```

### CSS Modules

```tsx
// Component.tsx
import styles from './Component.module.css';

export function Component() {
  return <div className={styles.container}>Hello</div>;
}
```

## Development Scripts

```json
// package.json
{
  "scripts": {
    "dev": "vinxi dev",
    "build": "vinxi build",
    "start": "vinxi start",
    "lint": "eslint src/",
    "typecheck": "tsc --noEmit"
  }
}
```

## Recommended Package.json

```json
{
  "name": "my-tanstack-app",
  "type": "module",
  "scripts": {
    "dev": "vinxi dev",
    "build": "vinxi build",
    "start": "vinxi start"
  },
  "dependencies": {
    "@tanstack/react-query": "^5.0.0",
    "@tanstack/react-router": "^1.0.0",
    "@tanstack/react-start": "^1.0.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "vinxi": "^0.5.0",
    "zod": "^3.23.0"
  },
  "devDependencies": {
    "@types/react": "^19.0.0",
    "@types/react-dom": "^19.0.0",
    "typescript": "^5.6.0"
  }
}
```

## Database Setup (Drizzle)

```bash
npm install drizzle-orm@beta @netlify/database
npm install -D drizzle-kit@beta
```

```typescript
// drizzle.config.ts
import { defineConfig } from 'drizzle-kit';

export default defineConfig({
  dialect: 'postgresql',
  schema: './db/schema.ts',
  out: 'netlify/database/migrations',
});
```

```typescript
// db/index.ts
import { drizzle } from 'drizzle-orm/netlify-db';
import * as schema from './schema';

export const db = drizzle({ schema });
```

## Common Configurations

### Custom 404 Page

```tsx
// src/routes/__root.tsx
export const Route = createRootRoute({
  notFoundComponent: () => (
    <div className="not-found">
      <h1>404 - Page Not Found</h1>
      <Link to="/">Go Home</Link>
    </div>
  ),
});
```

### Global Error Boundary

```tsx
// src/routes/__root.tsx
export const Route = createRootRoute({
  errorComponent: ({ error, reset }) => (
    <div className="error-page">
      <h1>Something went wrong</h1>
      <p>{error.message}</p>
      <button onClick={reset}>Try Again</button>
    </div>
  ),
});
```

### Loading States

```tsx
// src/routes/__root.tsx
export const Route = createRootRoute({
  pendingComponent: () => (
    <div className="loading">
      <span className="spinner" />
      Loading...
    </div>
  ),
});
```

## Checklist for New Projects

- [ ] Create project with `create-tanstack-start`
- [ ] Configure `app.config.ts` with Netlify preset
- [ ] Set up TypeScript paths (`~/`)
- [ ] Configure TanStack Query if needed
- [ ] Set up CSS solution (Tailwind recommended)
- [ ] Create `.env` for local development
- [ ] Configure `netlify.toml` for deployment
- [ ] Set up database if needed (Drizzle + Netlify Database)
- [ ] Add ESLint configuration
- [ ] Set environment variables in Netlify dashboard
More from netlify/swar-templates