diff --git a/examples/preact/basic/package.json b/examples/preact/basic/package.json index 379e5dd6..ecac213e 100644 --- a/examples/preact/basic/package.json +++ b/examples/preact/basic/package.json @@ -13,7 +13,7 @@ "@tanstack/devtools-event-client": "0.4.0", "@tanstack/preact-devtools": "workspace:*", "preact": "^10.28.0", - "zod": "^4.1.11" + "zod": "^4.3.5" }, "devDependencies": { "@preact/preset-vite": "^2.10.2", diff --git a/examples/react/basic/package.json b/examples/react/basic/package.json index 519d26bf..580dd203 100644 --- a/examples/react/basic/package.json +++ b/examples/react/basic/package.json @@ -19,7 +19,7 @@ "@tanstack/react-router-devtools": "^1.132.0", "react": "^19.2.0", "react-dom": "^19.2.0", - "zod": "^4.1.11" + "zod": "^4.3.5" }, "devDependencies": { "@tanstack/devtools-ui": "0.4.4", diff --git a/examples/react/bundling-repro/.cta.json b/examples/react/bundling-repro/.cta.json new file mode 100644 index 00000000..d29f28a4 --- /dev/null +++ b/examples/react/bundling-repro/.cta.json @@ -0,0 +1,21 @@ +{ + "projectName": "start-repro", + "mode": "file-router", + "typescript": true, + "packageManager": "npm", + "tailwind": true, + "addOnOptions": {}, + "git": true, + "version": 1, + "framework": "react-cra", + "chosenAddOns": [ + "biome", + "cloudflare", + "start", + "tanstack-query", + "ai", + "store", + "shadcn", + "compiler" + ] +} diff --git a/examples/react/bundling-repro/.cursorrules b/examples/react/bundling-repro/.cursorrules new file mode 100644 index 00000000..20159602 --- /dev/null +++ b/examples/react/bundling-repro/.cursorrules @@ -0,0 +1,7 @@ +# shadcn instructions + +Use the latest version of Shadcn to install new components, like this command to add a button component: + +```bash +pnpm dlx shadcn@latest add button +``` diff --git a/examples/react/bundling-repro/.wrangler/deploy/config.json b/examples/react/bundling-repro/.wrangler/deploy/config.json new file mode 100644 index 00000000..3c067226 --- /dev/null +++ b/examples/react/bundling-repro/.wrangler/deploy/config.json @@ -0,0 +1 @@ +{ "configPath": "..\\..\\dist\\server\\wrangler.json", "auxiliaryWorkers": [] } diff --git a/examples/react/bundling-repro/README.md b/examples/react/bundling-repro/README.md new file mode 100644 index 00000000..9be760e0 --- /dev/null +++ b/examples/react/bundling-repro/README.md @@ -0,0 +1,347 @@ +Welcome to your new TanStack app! + +# Getting Started + +To run this application: + +```bash +npm install +npm run dev +``` + +# Building For Production + +To build this application for production: + +```bash +npm run build +``` + +## Testing + +This project uses [Vitest](https://vitest.dev/) for testing. You can run the tests with: + +```bash +npm run test +``` + +## Styling + +This project uses [Tailwind CSS](https://tailwindcss.com/) for styling. + +## Linting & Formatting + +This project uses [Biome](https://biomejs.dev/) for linting and formatting. The following scripts are available: + +```bash +npm run lint +npm run format +npm run check +``` + +# TanStack Chat Application + +Am example chat application built with TanStack Start, TanStack Store, and Claude AI. + +## .env Updates + +```env +ANTHROPIC_API_KEY=your_anthropic_api_key +``` + +## ✨ Features + +### AI Capabilities + +- πŸ€– Powered by Claude 3.5 Sonnet +- πŸ“ Rich markdown formatting with syntax highlighting +- 🎯 Customizable system prompts for tailored AI behavior +- πŸ”„ Real-time message updates and streaming responses (coming soon) + +### User Experience + +- 🎨 Modern UI with Tailwind CSS and Lucide icons +- πŸ” Conversation management and history +- πŸ” Secure API key management +- πŸ“‹ Markdown rendering with code highlighting + +### Technical Features + +- πŸ“¦ Centralized state management with TanStack Store +- πŸ”Œ Extensible architecture for multiple AI providers +- πŸ› οΈ TypeScript for type safety + +## Architecture + +### Tech Stack + +- **Frontend Framework**: TanStack Start +- **Routing**: TanStack Router +- **State Management**: TanStack Store +- **Styling**: Tailwind CSS +- **AI Integration**: Anthropic's Claude API + +## Shadcn + +Add components using the latest version of [Shadcn](https://ui.shadcn.com/). + +```bash +pnpm dlx shadcn@latest add button +``` + +## Routing + +This project uses [TanStack Router](https://tanstack.com/router). The initial setup is a file based router. Which means that the routes are managed as files in `src/routes`. + +### Adding A Route + +To add a new route to your application just add another a new file in the `./src/routes` directory. + +TanStack will automatically generate the content of the route file for you. + +Now that you have two routes you can use a `Link` component to navigate between them. + +### Adding Links + +To use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`. + +```tsx +import { Link } from '@tanstack/react-router' +``` + +Then anywhere in your JSX you can use it like so: + +```tsx +About +``` + +This will create a link that will navigate to the `/about` route. + +More information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent). + +### Using A Layout + +In the File Based Routing setup the layout is located in `src/routes/__root.tsx`. Anything you add to the root route will appear in all the routes. The route content will appear in the JSX where you use the `` component. + +Here is an example layout that includes a header: + +```tsx +import { Outlet, createRootRoute } from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' + +import { Link } from '@tanstack/react-router' + +export const Route = createRootRoute({ + component: () => ( + <> +
+ +
+ + + + ), +}) +``` + +The `` component is not required so you can remove it if you don't want it in your layout. + +More information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts). + +## Data Fetching + +There are multiple ways to fetch data in your application. You can use TanStack Query to fetch data from a server. But you can also use the `loader` functionality built into TanStack Router to load the data for a route before it's rendered. + +For example: + +```tsx +const peopleRoute = createRoute({ + getParentRoute: () => rootRoute, + path: '/people', + loader: async () => { + const response = await fetch('https://swapi.dev/api/people') + return response.json() as Promise<{ + results: { + name: string + }[] + }> + }, + component: () => { + const data = peopleRoute.useLoaderData() + return ( +
    + {data.results.map((person) => ( +
  • {person.name}
  • + ))} +
+ ) + }, +}) +``` + +Loaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#loader-parameters). + +### React-Query + +React-Query is an excellent addition or alternative to route loading and integrating it into you application is a breeze. + +First add your dependencies: + +```bash +npm install @tanstack/react-query @tanstack/react-query-devtools +``` + +Next we'll need to create a query client and provider. We recommend putting those in `main.tsx`. + +```tsx +import { QueryClient, QueryClientProvider } from '@tanstack/react-query' + +// ... + +const queryClient = new QueryClient() + +// ... + +if (!rootElement.innerHTML) { + const root = ReactDOM.createRoot(rootElement) + + root.render( + + + , + ) +} +``` + +You can also add TanStack Query Devtools to the root route (optional). + +```tsx +import { ReactQueryDevtools } from '@tanstack/react-query-devtools' + +const rootRoute = createRootRoute({ + component: () => ( + <> + + + + + ), +}) +``` + +Now you can use `useQuery` to fetch your data. + +```tsx +import { useQuery } from '@tanstack/react-query' + +import './App.css' + +function App() { + const { data } = useQuery({ + queryKey: ['people'], + queryFn: () => + fetch('https://swapi.dev/api/people') + .then((res) => res.json()) + .then((data) => data.results as { name: string }[]), + initialData: [], + }) + + return ( +
+
    + {data.map((person) => ( +
  • {person.name}
  • + ))} +
+
+ ) +} + +export default App +``` + +You can find out everything you need to know on how to use React-Query in the [React-Query documentation](https://tanstack.com/query/latest/docs/framework/react/overview). + +## State Management + +Another common requirement for React applications is state management. There are many options for state management in React. TanStack Store provides a great starting point for your project. + +First you need to add TanStack Store as a dependency: + +```bash +npm install @tanstack/store +``` + +Now let's create a simple counter in the `src/App.tsx` file as a demonstration. + +```tsx +import { useStore } from '@tanstack/react-store' +import { Store } from '@tanstack/store' +import './App.css' + +const countStore = new Store(0) + +function App() { + const count = useStore(countStore) + return ( +
+ +
+ ) +} + +export default App +``` + +One of the many nice features of TanStack Store is the ability to derive state from other state. That derived state will update when the base state updates. + +Let's check this out by doubling the count using derived state. + +```tsx +import { useStore } from '@tanstack/react-store' +import { Store, Derived } from '@tanstack/store' +import './App.css' + +const countStore = new Store(0) + +const doubledStore = new Derived({ + fn: () => countStore.state * 2, + deps: [countStore], +}) +doubledStore.mount() + +function App() { + const count = useStore(countStore) + const doubledCount = useStore(doubledStore) + + return ( +
+ +
Doubled - {doubledCount}
+
+ ) +} + +export default App +``` + +We use the `Derived` class to create a new store that is derived from another store. The `Derived` class has a `mount` method that will start the derived store updating. + +Once we've created the derived store we can use it in the `App` component just like we would any other store using the `useStore` hook. + +You can find out everything you need to know on how to use TanStack Store in the [TanStack Store documentation](https://tanstack.com/store/latest). + +# Demo files + +Files prefixed with `demo` can be safely deleted. They are there to provide a starting point for you to play around with the features you've installed. + +# Learn More + +You can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com). diff --git a/examples/react/bundling-repro/biome.json b/examples/react/bundling-repro/biome.json new file mode 100644 index 00000000..cca08344 --- /dev/null +++ b/examples/react/bundling-repro/biome.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", + "vcs": { + "enabled": false, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "includes": [ + "**/src/**/*", + "**/.vscode/**/*", + "**/index.html", + "**/vite.config.ts", + "!**/src/routeTree.gen.ts", + "!**/src/styles.css" + ] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab" + }, + "assist": { "actions": { "source": { "organizeImports": "on" } } }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double" + } + } +} diff --git a/examples/react/bundling-repro/components.json b/examples/react/bundling-repro/components.json new file mode 100644 index 00000000..58bb3a27 --- /dev/null +++ b/examples/react/bundling-repro/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": false, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/styles.css", + "baseColor": "zinc", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} diff --git a/examples/react/bundling-repro/package.json b/examples/react/bundling-repro/package.json new file mode 100644 index 00000000..0fe54822 --- /dev/null +++ b/examples/react/bundling-repro/package.json @@ -0,0 +1,67 @@ +{ + "name": "start-repro", + "private": true, + "type": "module", + "scripts": { + "dev": "vite dev --port 3000", + "build": "vite build", + "preview": "vite preview", + "test": "vitest run", + "format": "biome format", + "lint": "biome lint", + "check": "biome check", + "deploy": "npm run build && wrangler deploy" + }, + "dependencies": { + "@cloudflare/vite-plugin": "^1.13.8", + "@tailwindcss/vite": "^4.0.6", + "@tanstack/ai": "latest", + "@tanstack/ai-anthropic": "latest", + "@tanstack/ai-client": "latest", + "@tanstack/ai-gemini": "latest", + "@tanstack/ai-ollama": "latest", + "@tanstack/ai-openai": "latest", + "@tanstack/ai-react": "latest", + "@tanstack/react-ai-devtools": "latest", + "@tanstack/react-devtools": "latest", + "@tanstack/react-query": "^5.90.1", + "@tanstack/react-query-devtools": "^5.90.1", + "@tanstack/react-router": "^1.132.0", + "@tanstack/react-router-devtools": "^1.132.0", + "@tanstack/react-router-ssr-query": "^1.131.7", + "@tanstack/react-start": "^1.132.0", + "@tanstack/react-store": "^0.8.0", + "@tanstack/router-plugin": "^1.132.0", + "@tanstack/store": "^0.8.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "highlight.js": "^11.11.1", + "lucide-react": "^0.561.0", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "streamdown": "^1.6.5", + "tailwind-merge": "^3.0.2", + "tailwindcss": "^4.0.6", + "tw-animate-css": "^1.3.6", + "vite-tsconfig-paths": "^6.0.2", + "zod": "^4.3.5" + }, + "devDependencies": { + "@biomejs/biome": "2.2.4", + "@tanstack/devtools-event-client": "latest", + "@tanstack/devtools-vite": "latest", + "@testing-library/dom": "^10.4.0", + "@testing-library/react": "^16.2.0", + "@types/node": "^22.15.2", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^5.0.4", + "babel-plugin-react-compiler": "^1.0.0", + "jsdom": "^27.0.0", + "typescript": "~5.9.2", + "vite": "^7.1.7", + "vitest": "^3.2.4", + "web-vitals": "^5.1.0", + "wrangler": "^4.40.3" + } +} diff --git a/examples/react/bundling-repro/public/example-guitar-flowers.jpg b/examples/react/bundling-repro/public/example-guitar-flowers.jpg new file mode 100644 index 00000000..a284a9ed Binary files /dev/null and b/examples/react/bundling-repro/public/example-guitar-flowers.jpg differ diff --git a/examples/react/bundling-repro/public/example-guitar-motherboard.jpg b/examples/react/bundling-repro/public/example-guitar-motherboard.jpg new file mode 100644 index 00000000..1075e7ef Binary files /dev/null and b/examples/react/bundling-repro/public/example-guitar-motherboard.jpg differ diff --git a/examples/react/bundling-repro/public/example-guitar-racing.jpg b/examples/react/bundling-repro/public/example-guitar-racing.jpg new file mode 100644 index 00000000..497ce3ae Binary files /dev/null and b/examples/react/bundling-repro/public/example-guitar-racing.jpg differ diff --git a/examples/react/bundling-repro/public/example-guitar-steamer-trunk.jpg b/examples/react/bundling-repro/public/example-guitar-steamer-trunk.jpg new file mode 100644 index 00000000..02636d82 Binary files /dev/null and b/examples/react/bundling-repro/public/example-guitar-steamer-trunk.jpg differ diff --git a/examples/react/bundling-repro/public/example-guitar-superhero.jpg b/examples/react/bundling-repro/public/example-guitar-superhero.jpg new file mode 100644 index 00000000..1f80d163 Binary files /dev/null and b/examples/react/bundling-repro/public/example-guitar-superhero.jpg differ diff --git a/examples/react/bundling-repro/public/example-guitar-traveling.jpg b/examples/react/bundling-repro/public/example-guitar-traveling.jpg new file mode 100644 index 00000000..7b355188 Binary files /dev/null and b/examples/react/bundling-repro/public/example-guitar-traveling.jpg differ diff --git a/examples/react/bundling-repro/public/example-guitar-video-games.jpg b/examples/react/bundling-repro/public/example-guitar-video-games.jpg new file mode 100644 index 00000000..090b8044 Binary files /dev/null and b/examples/react/bundling-repro/public/example-guitar-video-games.jpg differ diff --git a/examples/react/bundling-repro/public/example-ukelele-tanstack.jpg b/examples/react/bundling-repro/public/example-ukelele-tanstack.jpg new file mode 100644 index 00000000..1e8a5566 Binary files /dev/null and b/examples/react/bundling-repro/public/example-ukelele-tanstack.jpg differ diff --git a/examples/react/bundling-repro/public/favicon.ico b/examples/react/bundling-repro/public/favicon.ico new file mode 100644 index 00000000..a11777cc Binary files /dev/null and b/examples/react/bundling-repro/public/favicon.ico differ diff --git a/examples/react/bundling-repro/public/logo192.png b/examples/react/bundling-repro/public/logo192.png new file mode 100644 index 00000000..fc44b0a3 Binary files /dev/null and b/examples/react/bundling-repro/public/logo192.png differ diff --git a/examples/react/bundling-repro/public/logo512.png b/examples/react/bundling-repro/public/logo512.png new file mode 100644 index 00000000..a4e47a65 Binary files /dev/null and b/examples/react/bundling-repro/public/logo512.png differ diff --git a/examples/react/bundling-repro/public/manifest.json b/examples/react/bundling-repro/public/manifest.json new file mode 100644 index 00000000..078ef501 --- /dev/null +++ b/examples/react/bundling-repro/public/manifest.json @@ -0,0 +1,25 @@ +{ + "short_name": "TanStack App", + "name": "Create TanStack App Sample", + "icons": [ + { + "src": "favicon.ico", + "sizes": "64x64 32x32 24x24 16x16", + "type": "image/x-icon" + }, + { + "src": "logo192.png", + "type": "image/png", + "sizes": "192x192" + }, + { + "src": "logo512.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": ".", + "display": "standalone", + "theme_color": "#000000", + "background_color": "#ffffff" +} diff --git a/examples/react/bundling-repro/public/robots.txt b/examples/react/bundling-repro/public/robots.txt new file mode 100644 index 00000000..e9e57dc4 --- /dev/null +++ b/examples/react/bundling-repro/public/robots.txt @@ -0,0 +1,3 @@ +# https://www.robotstxt.org/robotstxt.html +User-agent: * +Disallow: diff --git a/examples/react/bundling-repro/public/tanstack-circle-logo.png b/examples/react/bundling-repro/public/tanstack-circle-logo.png new file mode 100644 index 00000000..9db3e67b Binary files /dev/null and b/examples/react/bundling-repro/public/tanstack-circle-logo.png differ diff --git a/examples/react/bundling-repro/public/tanstack-word-logo-white.svg b/examples/react/bundling-repro/public/tanstack-word-logo-white.svg new file mode 100644 index 00000000..b6ec5086 --- /dev/null +++ b/examples/react/bundling-repro/public/tanstack-word-logo-white.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/react/bundling-repro/src/components/Header.tsx b/examples/react/bundling-repro/src/components/Header.tsx new file mode 100644 index 00000000..98083638 --- /dev/null +++ b/examples/react/bundling-repro/src/components/Header.tsx @@ -0,0 +1,252 @@ +import { Link } from '@tanstack/react-router' + +import TanChatAIAssistant from './demo-AIAssistant.tsx' + +import { useState } from 'react' +import { + ChefHat, + ChevronDown, + ChevronRight, + Home, + ImageIcon, + Menu, + MessagesSquare, + Network, + SquareFunction, + StickyNote, + Store, + X, +} from 'lucide-react' + +export default function Header() { + const [isOpen, setIsOpen] = useState(false) + const [groupedExpanded, setGroupedExpanded] = useState< + Record + >({}) + + return ( + <> +
+ +

+ + TanStack Logo + +

+
+ + + + ) +} diff --git a/examples/react/bundling-repro/src/components/demo-AIAssistant.tsx b/examples/react/bundling-repro/src/components/demo-AIAssistant.tsx new file mode 100644 index 00000000..ee58848a --- /dev/null +++ b/examples/react/bundling-repro/src/components/demo-AIAssistant.tsx @@ -0,0 +1,159 @@ +import { useEffect, useRef, useState } from 'react' +import { useStore } from '@tanstack/react-store' +import { Store } from '@tanstack/store' + +import { Send, X, ChevronRight, BotIcon } from 'lucide-react' +import { Streamdown } from 'streamdown' + +import { useGuitarRecommendationChat } from '@/feat/demo-ai-hook' +import type { ChatMessages } from '@/feat/demo-ai-hook' + +import GuitarRecommendation from './demo-GuitarRecommendation' + +export const showAIAssistant = new Store(false) + +function Messages({ messages }: { messages: ChatMessages }) { + const messagesContainerRef = useRef(null) + + useEffect(() => { + if (messagesContainerRef.current) { + messagesContainerRef.current.scrollTop = + messagesContainerRef.current.scrollHeight + } + }, [messages]) + + if (!messages.length) { + return ( +
+ Ask me anything! I'm here to help. +
+ ) + } + + return ( +
+ {messages.map(({ id, role, parts }) => ( +
+ {parts.map((part, index) => { + if (part.type === 'text' && part.content) { + return ( +
+ {role === 'assistant' ? ( +
+ AI +
+ ) : ( +
+ Y +
+ )} +
+ {part.content} +
+
+ ) + } + if ( + part.type === 'tool-call' && + part.name === 'recommendGuitar' && + part.output + ) { + return ( +
+ +
+ ) + } + })} +
+ ))} +
+ ) +} + +export default function AIAssistant() { + const isOpen = useStore(showAIAssistant) + const { messages, sendMessage } = useGuitarRecommendationChat() + const [input, setInput] = useState('') + + return ( +
+ + + {isOpen && ( +
+
+

AI Assistant

+ +
+ + + +
+
{ + e.preventDefault() + if (input.trim()) { + sendMessage(input) + setInput('') + } + }} + > +
+