---
title: "Getting Started with TanStack Start And Convex: Your SaaS Foundation"
description: "Learn how to get started with TanStack Start And Convex and start building your first SaaS"
date: 2025-04-29
categories: ["cms"]
tags: ["tanstack","react"]
---

Welcome to the first article in our series on building a modern SaaS application! We'll use **TanStack Start**, a cutting-edge React framework, alongside [Convex](https://www.convex.dev/), Clerk, Radix UI, and Polar.sh to create a full-stack app.

**Important Note**: TanStack Start is in Alpha/Beta. APIs may change before a stable release. Embrace the innovation, but expect potential updates!

## What is TanStack Start?

[TanStack Start](https://tanstack.com/start/latest) is a modern React framework from the TanStack team, known for libraries like TanStack Query and Router. It offers:

- **File-based Routing**: Similar to Next.js or Remix.
- **Server-Side Rendering (SSR)**: With client-side hydration.
- **Integrated Data Fetching**: Seamless with TanStack Query.
- **Vite Tooling**: Fast development and builds.

## What is Convex?

[Convex](https://www.convex.dev/) is a backend-as-a-service platform that simplifies building scalable, real-time applications. It provides:

- **Realtime Database**: Automatically syncs data changes to clients, ideal for dynamic apps. Learn more in the [Convex Database Docs](https://docs.convex.dev/database).
- **Serverless Functions**: Write [queries](https://docs.convex.dev/functions/query-functions) (for reading data) and [mutations](https://docs.convex.dev/functions/mutation-functions) (for writing data) in TypeScript/JavaScript, hosted by Convex.
- **Type Safety**: Shares types between frontend and backend, reducing errors.
- **Dashboard**: A web-based [Convex Dashboard](https://docs.convex.dev/dashboard) to manage data, view logs, and debug.
- **Additional Features**: File storage, scheduled tasks, and vector search.

Convex integrates seamlessly with TanStack Start via the `@convex-dev/react-query` package, allowing you to fetch and mutate data using TanStack Query hooks.

## Installation (The Easy Way with Convex)

Use the Convex template to scaffold a project with TanStack Start and Convex pre-configured:

```bash
npm create convex@latest -- -t tanstack-start my-saas-app
cd my-saas-app
```

This creates a project in `my-saas-app` with React, TypeScript, TanStack Start, and Convex client setup. The template includes a `convex/` directory for backend logic and pre-configures environment variables.

For manual setup, refer to the [TanStack Start Getting Started Guide](https://tanstack.com/start/latest/docs/getting-started) and integrate Convex in the next article.

## Starting the Development Environment

Run the development server to initialize your app and Convex backend:

```bash
npm run dev
```

You'll see output like this:

```
> my-saas-app@1.0.0 dev
> npx convex dev --once && npm-run-all --parallel dev:convex dev:start

? Welcome to Convex! Would you like to login to your account? Start without an account (run Convex locally)
Let's set up your first project.
? Choose a name: my-saas-app
This command, `npx convex dev`, will run your Convex backend locally and update it with the function you write in the `convex/` directory.
Use `npx convex dashboard` to view and interact with your project from a web UI.
Use `npx convex docs` to read the docs and `npx convex help` to see other commands.
? Continue? Yes
✔ Downloaded Convex backend binary
✔ Downloaded Convex dashboard
✔ Started running a deployment locally at http://127.0.0.1:3210 and saved its:
    name as CONVEX_DEPLOYMENT to .env.local
    URL as VITE_CONVEX_URL to .env.local
Run `npx convex login` at any time to create an account and link this deployment.

Write your Convex functions in convex/
Give us feedback at https://convex.dev/community or support@convex.dev
View the Convex dashboard at http://127.0.0.1:6790/?d=anonymous-my-saas-app

✔ 11:29:40 Convex functions ready! (959.55ms)

> my-saas-app@1.0.0 dev:start
> vinxi dev

> my-saas-app@1.0.0 dev:convex
> convex dev

vinxi v0.4.3
vinxi starting dev server

♻️  Generating routes...
✔ Started running a deployment locally at http://127.0.0.1:3210 and saved its name as CONVEX_DEPLOYMENT to .env.local
Run `npx convex login` at any time to create an account and link this deployment.

Write your Convex functions in convex/
Give us feedback at https://convex.dev/community or support@convex.dev
View the Convex dashboard at http://127.0.0.1:6790/?d=anonymous-my-saas-app

⠋ Preparing Convex functions...
⠙ Preparing Convex functions...

  ➜ Local:    http://localhost:3000/
  ➜ Network:  use --host to expose
✔ 11:29:42 Convex functions ready! (839.54ms)
Warning: A notFoundError was encountered on the route with ID "__root__", but a notFoundComponent option was not configured...
```

**What’s Happening?**

- **Convex Initialization**: `npx convex dev --once` sets up a local Convex backend, prompting you to name your project (e.g., `my-saas-app`). It runs at `http://127.0.0.1:3210` and saves `VITE_CONVEX_URL` and `CONVEX_DEPLOYMENT` to `.env.local`. These variables connect your frontend to the Convex backend.
- **Parallel Execution**: `npm-run-all --parallel dev:convex dev:start` runs the Convex dev server (`npx convex dev`) and Vite dev server (`vinxi dev`) concurrently.
- **Vite Server**: Your app is available at `http://localhost:3000` (port may vary). Open this URL to see your app.
- **Convex Dashboard**: Visit `http://127.0.0.1:6790/?d=anonymous-my-saas-app` to manage your backend data, view logs, and test functions. Learn more about the dashboard in the [Convex Dashboard Docs](https://docs.convex.dev/dashboard).
- **Not Found Warning**: The `notFoundError` indicates TanStack Router needs a custom 404 component, which we’ll address below.

**Tip**: Run `npx convex dashboard` to open the dashboard directly, or `npx convex login` to link your local deployment to a Convex account for cloud syncing.

## Understanding the Project Structure

Key files and directories:

```
.
├── app/
│   ├── routes/             # Page routes
│   │   ├── __root.tsx      # Root layout (HTML structure, global providers)
│   │   └── index.tsx       # Homepage component ('/')
│   ├── client.tsx          # Client-side entry (hydrates SSR)
│   ├── router.tsx          # Router setup (integrates Convex/Clerk)
│   ├── routeTree.gen.ts    # Auto-generated route tree
│   └── ssr.tsx             # Server-side rendering entry
├── convex/                 # Backend logic (Convex functions, schema)
│   ├── schema.ts           # Database schema (defines tables)
│   └── ...                 # Backend functions (queries, mutations)
├── public/                 # Static assets
├── .env.local              # Environment variables (e.g., VITE_CONVEX_URL)
├── app.config.ts           # TanStack Start config
├── convex.config.ts        # Convex config
├── package.json
├── tsconfig.json           # TypeScript config
└── vite.config.ts          # Vite config
```

- **app/routes/__root.tsx**: Defines the app’s layout, including `<html>`, `<head>`, `<body>`, and common UI like headers. `<Outlet />` renders child routes.
- **app/routes/index.tsx**: Renders the `/` route (homepage).
- **convex/**: Contains backend logic. `schema.ts` defines your database structure (see [Convex Schema Docs](https://docs.convex.dev/database/schemas)). Other files will hold queries and mutations.
- **.env.local**: Stores sensitive keys like `VITE_CONVEX_URL`. Never commit this file to Git.

## Basic Routing

TanStack Start uses file-based routing via TanStack Router:

- **app/routes/index.tsx**: Uses `createFileRoute('/')` to define the homepage.
- **app/routes/__root.tsx**: Uses `createRootRouteWithContext` to set up the root layout and context (e.g., `queryClient` for TanStack Query, `convexClient` for Convex).

## Adding a Header and Footer

Let’s add a header and footer to `app/routes/__root.tsx` to provide consistent navigation and branding across all pages:

```tsx
// app/routes/__root.tsx
import { QueryClient } from "@tanstack/react-query";
import { createRootRouteWithContext, Link } from "@tanstack/react-router";
import { Outlet, ScrollRestoration } from "@tanstack/react-router";
import { Meta, Scripts } from "@tanstack/start";
import * as React from "react";
import { ConvexQueryClient } from "@convex-dev/react-query";
import { ConvexReactClient } from "convex/react";

interface MyRouterContext {
  queryClient: QueryClient;
  convexClient: ConvexReactClient;
  convexQueryClient: ConvexQueryClient;
}

export const Route = createRootRouteWithContext<MyRouterContext>()({
  head: () => ({
    meta: [
      { charSet: "utf-8" },
      { name: "viewport", content: "width=device-width, initial-scale=1" },
      { title: "My SaaS App" },
    ],
  }),
  notFoundComponent: () => (
    <div style={{ textAlign: 'center', padding: '2rem' }}>
      <h1>404 - Page Not Found</h1>
      <Link to="/">Go Home</Link>
    </div>
  ),
  component: RootComponent,
});

function RootComponent() {
  return (
    <RootDocument>
      <header
        style={{
          padding: '1rem',
          borderBottom: '1px solid #eee',
          background: '#f8f9fa',
          position: 'sticky',
          top: 0,
          zIndex: 10,
        }}
        role="banner"
      >
        <nav aria-label="Main navigation">
          <Link
            to="/"
            style={{
              marginRight: '1rem',
              fontWeight: 'bold',
              color: '#333',
              textDecoration: 'none',
            }}
            aria-label="Home page"
          >
            My SaaS App
          </Link>
        </nav>
      </header>
      <main style={{ padding: '1rem', minHeight: '80vh' }} role="main">
        <Outlet />
      </main>
      <footer
        style={{
          padding: '1rem',
          borderTop: '1px solid #eee',
          textAlign: 'center',
          background: '#f8f9fa',
        }}
        role="contentinfo"
      >
        <p>© {new Date().getFullYear()} My SaaS App. All rights reserved.</p>
      </footer>
    </RootDocument>
  );
}

function RootDocument({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <head>
        <Meta />
      </head>
      <body>
        {children}
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}
```

**Detailed Explanation of Header and Footer Code**:

- **RootComponent**: This function defines the core layout of your application, rendered for every route. It wraps the `<header>`, `<main>`, and `<footer>` in a `RootDocument` component, which provides the HTML structure (`<html>`, `<head>`, `<body>`).
  - **Purpose**: Ensures consistent UI elements (like navigation and branding) appear on all pages.
  - **Structure**: Uses semantic HTML elements (`<header>`, `<main>`, `<footer>`) for accessibility and SEO.

- **Header**:
  - **Element**: `<header role="banner">` marks the header as the primary banner of the page, improving accessibility for screen readers.
  - **Styling**:
    - `padding: '1rem'`: Adds spacing for a clean look.
    - `borderBottom: '1px solid #eee'`: Adds a subtle divider.
    - `background: '#f8f9fa'`: Uses a light gray background for visual distinction.
    - `position: 'sticky', top: 0, zIndex: 10`: Keeps the header fixed at the top during scrolling, ensuring navigation is always accessible.
  - **Navigation**:
    - `<nav aria-label="Main navigation">`: Labels the navigation for accessibility.
    - `<Link to="/" ...>My SaaS App</Link>`: Uses TanStack Router’s `Link` component for client-side navigation to the homepage (`/`).
      - **Styling**: `fontWeight: 'bold', color: '#333', textDecoration: 'none'` makes the link prominent and removes the default underline.
      - **Accessibility**: `aria-label="Home page"` provides context for screen readers.
  - **Purpose**: The header provides a consistent navigation bar, starting with a single link to the homepage. Later articles will add more links (e.g., to chat or pricing pages).

- **Main**:
  - **Element**: `<main role="main">` designates the primary content area, aiding accessibility.
  - **Styling**: `padding: '1rem', minHeight: '80vh'` ensures content is spaced and the main area takes up most of the viewport height, preventing the footer from appearing too high on short pages.
  - **Outlet**: `<Outlet />` is a TanStack Router component that renders the content of the current route (e.g., `index.tsx` for `/`).

- **Footer**:
  - **Element**: `<footer role="contentinfo">` marks the footer as supplementary information, enhancing accessibility.
  - **Styling**:
    - `padding: '1rem'`: Adds spacing.
    - `borderTop: '1px solid #eee'`: Adds a divider.
    - `textAlign: 'center'`: Centers the text.
    - `background: '#f8f9fa'`: Matches the header’s background for consistency.
  - **Content**: Displays a dynamic copyright notice using the current year (`new Date().getFullYear()`).
  - **Purpose**: Provides a professional touch and space for additional links or information in the future.

- **RootDocument**:
  - **Purpose**: Defines the HTML structure required for SSR and client-side rendering.
  - **Components**:
    - `<Meta />`: Injects metadata (e.g., `<title>`, `<meta>` tags) defined in the `head` function.
    - `<ScrollRestoration />`: Ensures scroll position is preserved during navigation.
    - `<Scripts />`: Includes JavaScript bundles for client-side interactivity.
  - **Accessibility**: Sets `lang="en"` on `<html>` for language clarity.

- **NotFoundComponent**:
  - Addresses the `notFoundError` warning by providing a custom 404 page.
  - Includes a simple message and a link back to the homepage.

- **Accessibility Considerations**:
  - Semantic elements (`header`, `main`, `footer`) and ARIA roles (`banner`, `main`, `contentinfo`) improve screen reader compatibility.
  - `aria-label` on navigation elements enhances usability for assistive technologies.
  - Styling ensures sufficient contrast (e.g., `#333` text on `#f8f9fa` background).

- **Extensibility**: The header’s `<nav>` can later include additional `<Link>` components for routes like `/chat` or `/pricing`, as shown in later articles.


## Troubleshooting Common Issues

- **Port Conflicts**: If `localhost:3000` is taken, Vite uses another port (check terminal output).
- **Convex Setup Fails**: Ensure internet connectivity. Run `npx convex dev --once` manually if needed. Check the [Convex Troubleshooting Docs](https://docs.convex.dev/troubleshooting).
- **Not Found Warning**: The `notFoundComponent` above resolves this. Alternatively, set `defaultNotFoundComponent` in `app/router.tsx`.
- **Environment Variables**: Verify `.env.local` contains `VITE_CONVEX_URL` and `CONVEX_DEPLOYMENT`. See [Convex Environment Variables](https://docs.convex.dev/deployment/environment-variables).
- **Convex Dashboard Access**: If the dashboard URL fails, run `npx convex dashboard` or check firewall settings.

Next, we’ll integrate Convex for the backend and Clerk for authentication, building on the foundation laid here!