Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions examples/react/start-neon-basic/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
node_modules
package-lock.json
yarn.lock

.DS_Store
.cache
.env
.vercel
.output
.nitro
/build/
/api/
/server/build
/public/build# Sentry Config File
.env.sentry-build-plugin
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
.tanstack
4 changes: 4 additions & 0 deletions examples/react/start-neon-basic/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
**/build
**/public
pnpm-lock.yaml
routeTree.gen.ts
11 changes: 11 additions & 0 deletions examples/react/start-neon-basic/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
},
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
}
}
86 changes: 86 additions & 0 deletions examples/react/start-neon-basic/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# TanStack Start + Neon Auth Example

SSR-compatible authentication with Neon Auth and TanStack Start.

- [TanStack Router Docs](https://tanstack.com/router)
- [Neon Auth Documentation](https://neon.com/docs/neon-auth/overview)

## Features

- **Neon Auth Integration** - Complete authentication flow with Neon Auth (based on Stack Auth)
- **SSR Compatible** - Works with TanStack Start's server-side rendering
- **Auto Database Setup** - Neon Launchpad creates database connection
- **Modern UI** - Clean interface with Tailwind CSS

## Quickest (impatient) Start

```bash
npx gitpick TanStack/router/tree/main/examples/react/start-neon-basic start-neon-basic
cd start-neon-basic
npm install
npm run dev
```

## Quick Start

1. **Install dependencies:**
```bash
pnpm install
cp env.example .env
```

2. **Get your Neon Auth credentials:**
- [Neon Launchpad](https://neon.com/docs/reference/neon-launchpad) will automatically create a Neon project for you
- Claim your project when prompted (a browser tab will open automatically, and the claim URL is also saved to .env)
- Go to the "Auth" section in your project dashboard, enable Auth, and get your credentials
- Edit `.env` and replace these values with your actual credentials:

```bash
VITE_STACK_PROJECT_ID=your_actual_project_id
VITE_STACK_PUBLISHABLE_CLIENT_KEY=your_actual_publishable_key
STACK_SECRET_SERVER_KEY=your_actual_secret_key
```

3. **Run:** `pnpm dev` → Visit `http://localhost:3000`

## Environment Variables

- `VITE_STACK_PROJECT_ID` - Neon Auth project ID
- `VITE_STACK_PUBLISHABLE_CLIENT_KEY` - Neon Auth publishable key
- `STACK_SECRET_SERVER_KEY` - Neon Auth secret key (server-side only)

### Database Auto-Creation

This example uses the `@neondatabase/vite-plugin-postgres` plugin which automatically:
- Creates a Neon database connection via [Neon Launchpad](https://neon.com/docs/reference/neon-launchpad)
- Sets up the `DATABASE_URL` environment variable
- Handles database initialization

You can override this by setting your own `DATABASE_URL` in the `.env` file before running `pnpm dev`.

## How It Works

- **Auth Flow**: Login/Signup → Neon Auth → `/handler/*` callback → Redirect
- **Handler Route**: `src/routes/handler.$.tsx` (client-only, catch-all)
- **SSR Safe**: Uses `useState` + `useEffect` pattern

## Project Structure

```
src/
├── routes/
│ ├── __root.tsx # Root with StackProvider
│ ├── handler.$.tsx # Auth callbacks (client-only)
│ ├── index.tsx # Home page
│ └── _authed/ # Protected routes
├── stack.ts # Stack Auth configuration
└── utils/ # Database utilities
```

## Troubleshooting

- **404 on `/handler/sign-in`**: Ensure file is named `handler.$.tsx`
- **SSR errors**: All Stack Auth components must be client-only
- **Route conflicts**: Delete `src/routeTree.gen.ts` and restart

See [docs/AUTHENTICATION_TROUBLESHOOTING.md](./docs/AUTHENTICATION_TROUBLESHOOTING.md) for detailed solutions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Authentication Troubleshooting Guide

Quick reference for Stack Auth + TanStack Start integration issues.

## ⚠️ Critical Issues

### 1. Handler Route Naming
**❌ Wrong**: `handler.$splat.tsx`, `handler.$_.tsx`, `handler._.tsx`
**✅ Correct**: `handler.$.tsx` (with `$` symbol only)

### 2. SSR Compatibility
**❌ Wrong**: Render `StackHandler` during SSR
**✅ Correct**: Use client-only rendering with `useState` + `useEffect`

### 3. Route Creation
**❌ Wrong**: `createFileRoute("/handler/$")`
**✅ Correct**: `createFileRoute()` (no arguments)

## Working Solution

*Note: These are simplified examples. The actual code includes additional UI components, navigation, and features.*

### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.%24.tsx))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix broken link to handler route.

Use the actual file path with $ instead of URL-encoding to %24.

-### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.%24.tsx))
+### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.$.tsx))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.%24.tsx))
### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.$.tsx))
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/docs/AUTHENTICATION_TROUBLESHOOTING.md around
line 23, the handler route link is URL-encoded using %24; change the link to use
the actual dollar-sign filename segment (use handler.$.tsx) so the path reads
src/routes/handler.$.tsx instead of src/routes/handler.%24.tsx, updating the
markdown link text and target accordingly.

```tsx
import { StackHandler } from "@stackframe/react";
import { stackClientApp } from "../stack";
import { useRouter, createFileRoute } from "@tanstack/react-router";
import { useState, useEffect } from "react";

function HandlerComponent() {
const router = useRouter();
const pathname = router.state.location.pathname;
const [isClient, setIsClient] = useState(false);

useEffect(() => {
setIsClient(true);
}, []);

if (!isClient) {
return <div>Loading...</div>;
}

return <StackHandler app={stackClientApp} location={pathname} fullPage />;
}

export const Route = createFileRoute()({
component: HandlerComponent,
});
```

### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Close the markdown link parenthesis in the “Root Route” heading.

-### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx)
+### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx))
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx)
### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx))
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/docs/AUTHENTICATION_TROUBLESHOOTING.md around
line 51, the "Root Route" markdown heading opens a link parenthesis but doesn't
close it; fix by adding the missing closing parenthesis after the link path so
the heading becomes valid Markdown.

```tsx
import { StackProvider, StackTheme } from "@stackframe/react";
import { stackClientApp } from "../stack";

function RootComponent() {
return (
<StackProvider app={stackClientApp}>
<StackTheme>
<Outlet />
</StackTheme>
</StackProvider>
);
}
```
Comment on lines +53 to +65
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add missing Outlet import in the Root example.

Without it, readers copying the snippet get a TS/runtime error.

-import { StackProvider, StackTheme } from "@stackframe/react";
+import { StackProvider, StackTheme } from "@stackframe/react";
+import { Outlet } from "@tanstack/react-router";
 import { stackClientApp } from "../stack";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { StackProvider, StackTheme } from "@stackframe/react";
import { stackClientApp } from "../stack";
function RootComponent() {
return (
<StackProvider app={stackClientApp}>
<StackTheme>
<Outlet />
</StackTheme>
</StackProvider>
);
}
```
import { StackProvider, StackTheme } from "@stackframe/react";
import { Outlet } from "@tanstack/react-router";
import { stackClientApp } from "../stack";
function RootComponent() {
return (
<StackProvider app={stackClientApp}>
<StackTheme>
<Outlet />
</StackTheme>
</StackProvider>
);
}
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/docs/AUTHENTICATION_TROUBLESHOOTING.md around
lines 53 to 65 the RootComponent example uses <Outlet /> but the snippet is
missing its import; add an import for Outlet (e.g., import { Outlet } from
"react-router-dom";) at the top of the snippet so the example compiles and runs
without TS/runtime errors.


## Common Issues

| Issue | Solution |
|-------|----------|
| 404 on `/handler/sign-in` | Use `handler.$.tsx` naming, restart dev server |
| `window is not defined` | Add client-only rendering to handler route |
| Route tree errors | Delete `src/routeTree.gen.ts`, restart dev server |
| TypeScript errors | Use `createFileRoute()` with no arguments |

## Environment Variables
```env
VITE_STACK_PROJECT_ID=your_project_id
VITE_STACK_PUBLISHABLE_CLIENT_KEY=your_publishable_key
STACK_SECRET_SERVER_KEY=your_secret_key
```

## Key Points

1. **Handler route must be client-only** - never render during SSR
2. **Use `handler.$.tsx`** - this is the only working catch-all naming
3. **Single handler file** - avoid duplicate route declarations
4. **Client-only auth UI** - use `useState` + `useEffect` pattern

## What NOT to Do

- ❌ Use `handler.$splat.tsx`, `handler.$_.tsx`, or `handler._.tsx`
- ❌ Use `createFileRoute("/handler/$")`
- ❌ Render `StackHandler` during SSR
- ❌ Use Stack Auth hooks in SSR components
- ❌ Create multiple handler route files
Loading