Skip to content
Merged
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
7 changes: 4 additions & 3 deletions .agents/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ User changed the filter state on the partners directory.

User clicked a "get in touch", "let's chat", or "become a partner" CTA.

| Prop | Type | Notes |
| ----------- | ---- | ---------------------------------------------------------- |
| `placement` | enum | `partners_index_cta`, `library_callout`, `docs_right_rail` |
| Prop | Type | Notes |
| ----------- | ---- | ---------------------------- |
| `placement` | enum | See `PartnerPlacement` below |

---

Expand Down Expand Up @@ -191,6 +191,7 @@ User took an action on the generated result. Single event with `action` prop cov
| `ecosystem_game` | 3D ecosystem game islands |
| `partners_index_cta` | "Get in touch" mailto on `/partners` |
| `library_callout` | "Let's chat" callout per library |
| `navbar` | "Partnership Inquiry" mailto in the Support menu |

### `BuilderAction`

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"@tanstack/ai-anthropic": "^0.10.1",
"@tanstack/ai-client": "^0.11.3",
"@tanstack/ai-openai": "^0.9.5",
"@tanstack/create": "^0.68.4",
"@tanstack/create": "^0.69.0",
"@tanstack/highlight": "^0.0.3",
"@tanstack/markdown": "^0.0.11",
"@tanstack/pacer": "^0.21.1",
Expand Down
10 changes: 5 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 13 additions & 28 deletions src/blog/netlify-partnership.md
Original file line number Diff line number Diff line change
@@ -1,47 +1,32 @@
---
title: TanStack + Netlify Partnership
published: 2025-03-18
excerpt: Netlify is now the official deployment partner for TanStack Start. Their focus on speed, simplicity, and flexibility aligns perfectly with our vision for full-stack development.
excerpt: Netlify is an official hosting partner for TanStack Start, with a Vite integration for deploying full-stack apps and an official starter template.
authors:
- Tanner Linsley
---

![Netlify Header](/blog-assets/netlify-partnership/header.jpg)

We’re excited to announce that **Netlify** is now the **Official Deployment Partner** for **TanStack Start**! Together, we’re making it easier than ever for developers to build and deploy modern, type-safe, and user-focused web applications.
**Netlify is an official hosting partner for TanStack Start.** The practical result is a supported deployment path through [`@netlify/vite-plugin-tanstack-start`](https://www.npmjs.com/package/@netlify/vite-plugin-tanstack-start), including SSR, Server Routes, Server Functions, middleware, and local Netlify platform emulation.

Netlify has earned its reputation as the ultimate deployment platform for modern web developers. Its focus on speed, simplicity, modularity, and flexibility aligns perfectly with TanStack Start’s vision for full-stack development. Here’s why Netlify stands out:
## TanStack Start on Netlify

- **No-config simplicity** – Deploy your TanStack Start apps in seconds, with zero setup hassle.
- **Serverless power** – Netlify Functions enable dynamic, real-time features effortlessly.
- **Global performance** – Fast, reliable apps served from Netlify’s global edge network.
- **Developer-first tools** – Instant previews, automated workflows, and seamless integrations make building a joy.
Netlify’s Vite integration configures TanStack Start for its full-stack runtime while keeping the usual Vite development loop. Git-based continuous deployment creates Deploy Previews for pull and merge requests. Teams can also enable branch deploys for selected or all non-production branches.

## Why Netlify?
Netlify also maintains a [full-stack TanStack Start chat template](https://github.com/netlify-templates/tanstack-template) built with TanStack Router, TanStack Store, Claude, and optional Sentry and Convex integrations. You can deploy that template directly, or create a new Netlify-ready project with the TanStack CLI:

Netlify is more than just a deployment provider. They’ve worked closely with us to ensure that deploying TanStack Start applications is not just fast, but optimized for the best possible developer experience. Whether you’re building interactive UIs, data-heavy dashboards, real-time tools, or AI-powered applications, Netlify’s platform makes the process seamless.

As part of this partnership, Netlify has also launched a **full-stack AI chatbot starter template** that showcases TanStack Start’s powerful data management capabilities alongside Netlify Functions. This template provides:

- **Real-time data handling** with TanStack Query
- **Efficient routing** with TanStack Router
- **Seamless server function integration** with Netlify
- **A production-ready deployment configuration**

To get started with the template, simply run:

```
npx create-tsrouter-app@latest <name> --template file-router --add-ons tanchat
```bash
npx @tanstack/cli create my-app --deployment netlify
```

This template is a great way to explore how TanStack Start and Netlify work together to simplify modern web development.

## What’s Next?
The CLI installs and configures the Netlify deployment target, so the generated project is ready for the Netlify CLI or a Git-connected deployment.

We’re just getting started. Expect more updates, new features, and deeper collaboration between TanStack Start and Netlify. Stay tuned for success stories, guides, and real-world examples showcasing what’s possible with this powerful combination.
## Deploy a TanStack Start app

Additionally, join us March 31 for a **special TanStack Start episode on [Netlify’s Remote Desk series](https://www.netlify.com/webinars/netlify-remote-desk/)**. We’ll dive into live demos, developer tips, and an exclusive Q&A to show how to unlock the full potential of TanStack Start on Netlify.
For a new project, use the command above and follow the generated setup. For an existing app, add the Netlify Vite plugin and deploy with `npx netlify deploy`.

**Ready to dive in?** Check out the [TanStack Start docs](/start/latest/docs/framework/react/overview), explore the deployment guides, and start building with Netlify today.
- [TanStack Start hosting guide](https://tanstack.com/start/latest/docs/framework/react/guide/hosting#netlify--official-partner)
- [Netlify’s TanStack Start template](https://github.com/netlify-templates/tanstack-template)

A huge thank you to Netlify for supporting and empowering developers. Let’s build something incredible together! 🚀
Netlify’s support helps fund TanStack’s open-source work while giving Start users a deployment path maintained by both teams.
19 changes: 9 additions & 10 deletions src/blog/openrouter-partnership.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
---
title: TanStack + OpenRouter Partnership
published: 2026-03-09
excerpt: OpenRouter is now an official TanStack sponsor. The most concrete result is already shipped — a first-class TanStack AI adapter giving you access to 300+ models from 60+ providers through a single API.
excerpt: OpenRouter is an official TanStack sponsor, with a first-class TanStack AI adapter for accessing hundreds of models through one API.
authors:
- Tanner Linsley
---

![TanStack + OpenRouter](/blog-assets/openrouter-partnership/header.png)

**OpenRouter is now an official TanStack sponsor.** And the most concrete expression of that is already shipped: [`@tanstack/ai-openrouter`](https://tanstack.com/ai/latest/docs/adapters/openrouter)a first-class TanStack AI adapter that gives you access to 300+ models from 60+ providers through a single, unified API.
**OpenRouter is now an official TanStack sponsor.** The most concrete expression of that is already shipped: [`@tanstack/ai-openrouter`](https://tanstack.com/ai/latest/docs/adapters/openrouter), a first-class TanStack AI adapter that gives you access to hundreds of models through one API.

## Why OpenRouter

When we started building TanStack AI, one of our core beliefs was that you shouldn't have to bet your integration on a single provider. The AI model landscape is moving faster than anyone can predict. The model that wins this quarter might not be the one you want next quarter, and rewriting your AI layer every time a new frontier model drops is exactly the kind of undifferentiated toil we want to help you avoid.

OpenRouter solves this cleanly. One API key. One integration. GPT-5, Claude, Gemini, Llama, Mistral, DeepSeekand whatever ships next month. When you want to try a different model, you change a string. When a provider goes down, OpenRouter routes around it automatically. That's the kind of leverage I want TanStack developers to have.
OpenRouter solves this cleanly. One API key and one integration for GPT, Claude, Gemini, Llama, Mistral, DeepSeek, and whatever ships next. When you want to try a different model, you change a string. When a provider goes down, OpenRouter can route around it. That's the kind of leverage I want TanStack developers to have.

## The Adapter

Expand All @@ -32,26 +32,25 @@ const stream = chat({
})
```

Swap the model string for any of the [300+ models on OpenRouter](https://openrouter.ai/models?utm_source=tanstack). Everything else stays the same.
Swap the model string for any model in the [OpenRouter catalog](https://openrouter.ai/models?utm_source=tanstack). Everything else stays the same.

One feature I particularly love is the auto-router with fallbacks. It's dead simple to set up and gives your app real production resilience without any retry logic of your own:

```typescript
const stream = chat({
adapter: openRouterText('openrouter/auto'),
messages,
providerOptions: {
modelOptions: {
models: [
'openai/gpt-5',
'openai/gpt-5.5',
'anthropic/claude-sonnet-4.5',
'google/gemini-3-pro-preview',
'google/gemini-3.1-pro-preview',
],
route: 'fallback',
},
})
```

If the primary model fails or gets rate-limited, OpenRouter falls through to the next one. No outage pages, no extra infrastructure.
If the primary model is unavailable, OpenRouter falls through the configured list. The fallback order stays in the request instead of becoming hand-written retry logic.

## Jack's Image Generation Demo

Expand All @@ -69,7 +68,7 @@ Our own Jack Herrington put together a demo showing off TanStack AI with the Ope

## What This Means Going Forward

OpenRouter's sponsorship of TanStack means the adapter is actively maintained, tested, and will stay in sync with both libraries as they evolve. More importantly, both teams are genuinely aligned on the same goal: give developers the most flexible AI integration possible without locking them into anything.
The sponsorship supports ongoing maintenance and testing as TanStack AI and OpenRouter evolve. More importantly, both teams are aligned on the same goal: give developers a flexible AI integration without locking them into a single model provider.

If you're building AI features with TanStack, the OpenRouter adapter is the one I'd reach for first.

Expand Down
53 changes: 49 additions & 4 deletions src/builder/api/create-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type GeneratedTemplateContext = WorkerTemplateContext & {
projectName: unknown
typescript: unknown
tailwind: unknown
blank: unknown
js: unknown
jsx: unknown
fileRouter: unknown
Expand Down Expand Up @@ -62,6 +63,7 @@ type WorkerAddOnPhase = WorkerAddOnManifest['phase']
type WorkerAddOnCategory = NonNullable<WorkerAddOnManifest['category']>
type WorkerAddOnExclusive = NonNullable<WorkerAddOnManifest['exclusive']>[number]
type WorkerAddOnEnvVar = NonNullable<WorkerAddOnManifest['envVars']>[number]
type WorkerAddOnPartner = NonNullable<WorkerAddOnManifest['partner']>
type WorkerAddOnOptions = NonNullable<WorkerAddOnManifest['options']>
type WorkerAddOnOption = WorkerAddOnOptions[string]
type WorkerAddOnPackageAdditions = NonNullable<
Expand All @@ -71,6 +73,10 @@ type GeneratedStringRecord = Record<string, string | undefined>
type GeneratedAddOnPackageAdditions = {
dependencies?: GeneratedStringRecord
devDependencies?: GeneratedStringRecord
engines?: GeneratedStringRecord
pnpm?: {
onlyBuiltDependencies?: Array<string>
}
scripts?: GeneratedStringRecord
}
type GeneratedAddOnEnvVar = Omit<WorkerAddOnEnvVar, 'file'> & {
Expand All @@ -79,6 +85,9 @@ type GeneratedAddOnEnvVar = Omit<WorkerAddOnEnvVar, 'file'> & {
type GeneratedAddOnOption = Omit<WorkerAddOnOption, 'type'> & {
type: string
}
type GeneratedAddOnPartner = Omit<WorkerAddOnPartner, 'tier'> & {
tier: string
}
type GeneratedAddOnOptions = Record<string, GeneratedAddOnOption | undefined>
type GeneratedAddOnFields = {
type: string
Expand All @@ -87,6 +96,7 @@ type GeneratedAddOnFields = {
exclusive?: Array<string>
envVars?: Array<GeneratedAddOnEnvVar>
options?: GeneratedAddOnOptions
partner?: GeneratedAddOnPartner
}
type GeneratedAddOnManifest = Omit<
WorkerAddOnManifest,
Expand All @@ -96,6 +106,7 @@ type GeneratedAddOnManifest = Omit<
| 'exclusive'
| 'envVars'
| 'options'
| 'partner'
| 'packageAdditions'
> &
GeneratedAddOnFields & {
Expand All @@ -109,6 +120,7 @@ type GeneratedAddOnMetadata = Omit<
| 'exclusive'
| 'envVars'
| 'options'
| 'partner'
| 'packageAdditions'
> &
GeneratedAddOnFields & {
Expand Down Expand Up @@ -136,6 +148,7 @@ function getGeneratedTemplateContext(
projectName: context.projectName,
typescript: context.typescript,
tailwind: context.tailwind,
blank: context.blank,
js: context.js,
jsx: context.jsx,
fileRouter: context.fileRouter,
Expand Down Expand Up @@ -238,6 +251,28 @@ function getAddOnExclusive(exclusive: string): WorkerAddOnExclusive {
}
}

function getAddOnPartnerTier(tier: string): WorkerAddOnPartner['tier'] {
switch (tier) {
case 'gold':
case 'silver':
case 'bronze':
return tier
default:
throw new Error(`Unsupported partner tier: ${tier}`)
}
}

function getAddOnPartner(
partner: GeneratedAddOnPartner | undefined,
): WorkerAddOnManifest['partner'] {
if (!partner) return undefined

return {
...partner,
tier: getAddOnPartnerTier(partner.tier),
}
}

function getAddOnEnvFile(file: string | undefined): WorkerAddOnEnvVar['file'] {
switch (file) {
case undefined:
Expand Down Expand Up @@ -310,6 +345,8 @@ function getPackageAdditions(
const normalizedPackageAdditions: WorkerAddOnPackageAdditions = {
dependencies: getStringRecord(packageAdditions.dependencies),
devDependencies: getStringRecord(packageAdditions.devDependencies),
engines: getStringRecord(packageAdditions.engines),
pnpm: packageAdditions.pnpm,
scripts: getStringRecord(packageAdditions.scripts),
}

Expand All @@ -327,6 +364,7 @@ function getAddOnMetadata(
exclusive: addOn.exclusive?.map(getAddOnExclusive),
envVars: getAddOnEnvVars(addOn.envVars),
options: getAddOnOptions(addOn.options),
partner: getAddOnPartner(addOn.partner),
packageAdditions: getPackageAdditions(addOn.packageAdditions),
}
}
Expand All @@ -340,6 +378,7 @@ function getAddOnManifest(addOn: GeneratedAddOnManifest): WorkerAddOnManifest {
exclusive: addOn.exclusive?.map(getAddOnExclusive),
envVars: getAddOnEnvVars(addOn.envVars),
options: getAddOnOptions(addOn.options),
partner: getAddOnPartner(addOn.partner),
packageAdditions: getPackageAdditions(addOn.packageAdditions),
}
}
Expand Down Expand Up @@ -376,6 +415,12 @@ function loadAddOn(
}
}

function defineAddOnLoaders(
loaders: Record<string, () => Promise<WorkerAddOnManifestModule>>,
) {
return loaders
}

const frameworkLoaders: Record<
string,
() => Promise<WorkerFrameworkManifestModule>
Expand All @@ -388,7 +433,7 @@ const frameworkLoaders: Record<
),
}

const reactAddOnLoaders = {
const reactAddOnLoaders = defineAddOnLoaders({
ai: loadAddOn(() =>
import('@tanstack/create/worker-manifest/frameworks/react/add-ons/ai'),
),
Expand Down Expand Up @@ -512,9 +557,9 @@ const reactAddOnLoaders = {
workos: loadAddOn(() =>
import('@tanstack/create/worker-manifest/frameworks/react/add-ons/workos'),
),
} satisfies Record<string, () => Promise<WorkerAddOnManifestModule>>
})

const solidAddOnLoaders = {
const solidAddOnLoaders = defineAddOnLoaders({
'better-auth': loadAddOn(() =>
import(
'@tanstack/create/worker-manifest/frameworks/solid/add-ons/better-auth'
Expand Down Expand Up @@ -571,7 +616,7 @@ const solidAddOnLoaders = {
'@tanstack/create/worker-manifest/frameworks/solid/add-ons/tanstack-query'
),
),
} satisfies Record<string, () => Promise<WorkerAddOnManifestModule>>
})

const addOnLoaders: Record<
string,
Expand Down
16 changes: 4 additions & 12 deletions src/builder/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,10 @@ export const TEMPLATES: Array<Template> = [
{
id: 'saas',
name: 'SaaS Starter',
description: 'Auth, database, monitoring',
description: 'Auth, database, and UI',
icon: Rocket,
color: '#F97316', // orange
features: [
'cloudflare',
'better-auth',
'neon',
'drizzle',
'sentry',
'shadcn',
'form',
],
features: ['cloudflare', 'better-auth', 'prisma', 'shadcn', 'form'],
},
{
id: 'ai-chat',
Expand All @@ -81,7 +73,7 @@ export const TEMPLATES: Array<Template> = [
description: 'Content-driven site',
icon: FileText,
color: '#EC4899', // pink
features: ['cloudflare', 'strapi', 'tanstack-query'],
features: ['cloudflare', 'tanstack-query'],
},
{
id: 'api-first',
Expand All @@ -97,7 +89,7 @@ export const TEMPLATES: Array<Template> = [
description: 'Live, collaborative features',
icon: Radio,
color: '#EF4444', // red
features: ['cloudflare', 'convex', 'tanstack-query'],
features: ['cloudflare', 'db', 'tanstack-query'],
},
{
id: 'i18n',
Expand Down
Loading
Loading