diff --git a/.agents/analytics.md b/.agents/analytics.md index bcd12c824..c1ba501ac 100644 --- a/.agents/analytics.md +++ b/.agents/analytics.md @@ -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 | --- @@ -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` diff --git a/package.json b/package.json index 0b0c97915..bf8dd63de 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5cd147f21..ee07259d4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -97,8 +97,8 @@ importers: specifier: ^0.9.5 version: 0.9.5(@tanstack/ai-client@0.11.3(@opentelemetry/api@1.9.1))(@tanstack/ai@0.20.1(@opentelemetry/api@1.9.1))(ws@8.21.0)(zod@4.3.6) '@tanstack/create': - specifier: ^0.68.4 - version: 0.68.4(tslib@2.8.1) + specifier: ^0.69.0 + version: 0.69.0(tslib@2.8.1) '@tanstack/highlight': specifier: ^0.0.3 version: 0.0.3 @@ -3286,8 +3286,8 @@ packages: '@opentelemetry/api': optional: true - '@tanstack/create@0.68.4': - resolution: {integrity: sha512-LJ4eGQQPxyh88rEfod21P2HSAnlNFLVxlCw5GphhTBgzQTTw1FUA86N1VnIrI8h7LofDY4LfP0X84KVavXZKxQ==} + '@tanstack/create@0.69.0': + resolution: {integrity: sha512-LVbzkTWiCK+JCKa/1Qwax0JddfJt4rxwekrDyHzhDWsSeNodJrTwbUg4eqqbQsxEWcrk0kHEJSfiTVzMnmI+kQ==} engines: {node: '>=20'} '@tanstack/devtools-client@0.0.6': @@ -9573,7 +9573,7 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 - '@tanstack/create@0.68.4(tslib@2.8.1)': + '@tanstack/create@0.69.0(tslib@2.8.1)': dependencies: ejs: 3.1.10 execa: 9.6.1 diff --git a/src/blog/netlify-partnership.md b/src/blog/netlify-partnership.md index 02e04020f..d378de0d6 100644 --- a/src/blog/netlify-partnership.md +++ b/src/blog/netlify-partnership.md @@ -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 --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. diff --git a/src/blog/openrouter-partnership.md b/src/blog/openrouter-partnership.md index f8b63798e..3d5c42d88 100644 --- a/src/blog/openrouter-partnership.md +++ b/src/blog/openrouter-partnership.md @@ -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, DeepSeek — and 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 @@ -32,7 +32,7 @@ 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: @@ -40,18 +40,17 @@ One feature I particularly love is the auto-router with fallbacks. It's dead sim 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 @@ -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. diff --git a/src/builder/api/create-worker.ts b/src/builder/api/create-worker.ts index ed622b13b..8bdd95275 100644 --- a/src/builder/api/create-worker.ts +++ b/src/builder/api/create-worker.ts @@ -21,6 +21,7 @@ type GeneratedTemplateContext = WorkerTemplateContext & { projectName: unknown typescript: unknown tailwind: unknown + blank: unknown js: unknown jsx: unknown fileRouter: unknown @@ -62,6 +63,7 @@ type WorkerAddOnPhase = WorkerAddOnManifest['phase'] type WorkerAddOnCategory = NonNullable type WorkerAddOnExclusive = NonNullable[number] type WorkerAddOnEnvVar = NonNullable[number] +type WorkerAddOnPartner = NonNullable type WorkerAddOnOptions = NonNullable type WorkerAddOnOption = WorkerAddOnOptions[string] type WorkerAddOnPackageAdditions = NonNullable< @@ -71,6 +73,10 @@ type GeneratedStringRecord = Record type GeneratedAddOnPackageAdditions = { dependencies?: GeneratedStringRecord devDependencies?: GeneratedStringRecord + engines?: GeneratedStringRecord + pnpm?: { + onlyBuiltDependencies?: Array + } scripts?: GeneratedStringRecord } type GeneratedAddOnEnvVar = Omit & { @@ -79,6 +85,9 @@ type GeneratedAddOnEnvVar = Omit & { type GeneratedAddOnOption = Omit & { type: string } +type GeneratedAddOnPartner = Omit & { + tier: string +} type GeneratedAddOnOptions = Record type GeneratedAddOnFields = { type: string @@ -87,6 +96,7 @@ type GeneratedAddOnFields = { exclusive?: Array envVars?: Array options?: GeneratedAddOnOptions + partner?: GeneratedAddOnPartner } type GeneratedAddOnManifest = Omit< WorkerAddOnManifest, @@ -96,6 +106,7 @@ type GeneratedAddOnManifest = Omit< | 'exclusive' | 'envVars' | 'options' + | 'partner' | 'packageAdditions' > & GeneratedAddOnFields & { @@ -109,6 +120,7 @@ type GeneratedAddOnMetadata = Omit< | 'exclusive' | 'envVars' | 'options' + | 'partner' | 'packageAdditions' > & GeneratedAddOnFields & { @@ -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, @@ -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: @@ -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), } @@ -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), } } @@ -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), } } @@ -376,6 +415,12 @@ function loadAddOn( } } +function defineAddOnLoaders( + loaders: Record Promise>, +) { + return loaders +} + const frameworkLoaders: Record< string, () => Promise @@ -388,7 +433,7 @@ const frameworkLoaders: Record< ), } -const reactAddOnLoaders = { +const reactAddOnLoaders = defineAddOnLoaders({ ai: loadAddOn(() => import('@tanstack/create/worker-manifest/frameworks/react/add-ons/ai'), ), @@ -512,9 +557,9 @@ const reactAddOnLoaders = { workos: loadAddOn(() => import('@tanstack/create/worker-manifest/frameworks/react/add-ons/workos'), ), -} satisfies Record Promise> +}) -const solidAddOnLoaders = { +const solidAddOnLoaders = defineAddOnLoaders({ 'better-auth': loadAddOn(() => import( '@tanstack/create/worker-manifest/frameworks/solid/add-ons/better-auth' @@ -571,7 +616,7 @@ const solidAddOnLoaders = { '@tanstack/create/worker-manifest/frameworks/solid/add-ons/tanstack-query' ), ), -} satisfies Record Promise> +}) const addOnLoaders: Record< string, diff --git a/src/builder/templates.ts b/src/builder/templates.ts index 28809a598..62c9a9faf 100644 --- a/src/builder/templates.ts +++ b/src/builder/templates.ts @@ -46,18 +46,10 @@ export const TEMPLATES: Array