// Generated by scripts/generate-manifest.mjs. Do not edit by hand. import type { ManifestFrameworkDefinition } from '../manifest-types.js' type TemplateRecord = Record type TemplateAddOn = TemplateRecord & { integrations?: Array routes?: Array } type TemplateRenderContext = { [key: string]: any packageManager: any projectName: any typescript: any tailwind: any blank: any js: any jsx: any fileRouter: any codeRouter: any routerOnly: any includeExamples: any addOnEnabled: Record addOnOption: Record addOns: Array integrations: Array routes: Array getPackageManagerAddScript: (...args: Array) => string getPackageManagerRunScript: (...args: Array) => string getPackageManagerExecuteScript: (...args: Array) => string relativePath: (...args: Array) => string integrationImportContent: (...args: Array) => string integrationImportCode: (...args: Array) => string | undefined renderTemplate: (content: string) => string ignoreFile: () => never } type TemplateRenderer = (context: TemplateRenderContext) => string | undefined function __escapeXML(value: unknown) { if (value === undefined || value === null) { return '' } return String(value).replace(/[&<>'"]/g, (character) => { switch (character) { case '&': return '&' case '<': return '<' case '>': return '>' case '"': return '"' case "'": return ''' default: return character } }) } export function getManifestTemplateKey(template: string) { let hash = 0x811c9dc5 for (let i = 0; i < template.length; i++) { hash ^= template.charCodeAt(i) hash = Math.imul(hash, 0x01000193) >>> 0 } return `${hash.toString(16).padStart(8, '0')}:${template.length}` } function __render_028b78da_276(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { defineConfig, env } from 'prisma/config'\n\nexport default defineConfig({\n schema: './prisma/schema.prisma',\n migrations: {\n path: './prisma/migrations',\n seed: 'tsx prisma/seed.ts',\n },\n datasource: {\n url: env('DATABASE_URL'),\n },\n});\n ") return __output } function __render_07e79a91_473(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { drizzle } from ") if (addOnOption.drizzle.database === 'postgresql') { __append("\n'drizzle-orm/node-postgres';\n") } else if (addOnOption.drizzle.database === 'mysql') { __append("\n'drizzle-orm/mysql2';\n") } else if (addOnOption.drizzle.database === 'sqlite') { __append("\n'drizzle-orm/better-sqlite3';\n") } __append("\nimport * as schema from './schema.ts'\n\nexport const db = drizzle(process.env.DATABASE_URL!, { schema") if (addOnOption.drizzle.database === 'mysql') { __append(", mode: 'default'") } __append("});\n") return __output } function __render_0a77947a_2355(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!includeExamples) { ignoreFile(); return; } __append("\nexport default function Footer() {\n const year = new Date().getFullYear()\n\n return (\n
\n
\n

© {year} Your name here. All rights reserved.

\n

Built with TanStack Start

\n
\n
\n \n Follow TanStack on X\n \n \n \n \n \n Go to TanStack GitHub\n \n \n \n \n
\n
\n )\n}\n") return __output } function __render_0b3992a2_1709(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { ") if (fileRouter) { __append("createFileRoute") } else { __append("createRoute") } __append(" } from '@tanstack/react-router'\nimport { useStore } from '@tanstack/react-store'\n\nimport { fullName, store } from '#/lib/demo-store'\n") if (codeRouter) { __append("\nimport type { RootRoute } from '@tanstack/react-router'\n") } else { __append("\nexport const Route = createFileRoute('/demo/store')({\n component: DemoStore,\n})\n") } __append("\n\nfunction FirstName() {\n const firstName = useStore(store, (state) => state.firstName)\n return (\n \n store.setState((state) => ({ ...state, firstName: e.target.value }))\n }\n className=\"demo-input\"\n />\n )\n}\n\nfunction LastName() {\n const lastName = useStore(store, (state) => state.lastName)\n return (\n \n store.setState((state) => ({ ...state, lastName: e.target.value }))\n }\n className=\"demo-input\"\n />\n )\n}\n\nfunction FullName() {\n const fName = useStore(fullName, (state) => state)\n return (\n
\n {fName}\n
\n )\n}\n\nfunction DemoStore() {\n return (\n
\n
\n

TanStack Store

\n

Store Example

\n \n \n \n
\n
\n )\n}\n\n") if (codeRouter) { __append("\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/store',\n component: DemoStore,\n getParentRoute: () => parentRoute,\n})\n") } __append("\n") return __output } function __render_0d8cb769_2027(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { Show } from \"solid-js\";") if (routes.some(r => r.url === '/demo/better-auth')) { __append("import { Link } from \"@tanstack/solid-router\";") } __append("import { authClient } from \"../../lib/auth-client\";\n\nexport default function BetterAuthHeader() {\n const session = authClient.useSession();\n\n return (\n \n }\n >\n r.url === '/demo/better-auth')) { __append("fallback={\n \n Sign in\n \n }") } __append(">\n {(user) => (\n
\n \n \n {user().name?.charAt(0).toUpperCase() || \"U\"}\n \n
\n }\n >\n {(image) => \"\"}\n \n {\n void authClient.signOut();\n }}\n class=\"flex-1 h-9 px-4 text-sm font-medium bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-50 border border-neutral-300 dark:border-neutral-700 hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors\"\n >\n Sign out\n \n \n )}\n \n \n );\n}\n") return __output } function __render_104d2185_11220(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { ") if (fileRouter) { __append("createFileRoute") } else { __append("createRoute") } __append(" } from '@tanstack/solid-router'\nimport { createForm } from '@tanstack/solid-form'\n\nimport type { JSX } from 'solid-js/jsx-runtime'\nimport type { ValidationError } from '@tanstack/solid-form'\n\n") if (codeRouter) { __append("\nimport type { RootRoute } from '@tanstack/solid-router'\n") } else { __append("\nexport const Route = createFileRoute('/demo/form')({\n component: FormExample,\n})\n") } __append("\nfunction FieldWrapper(props: {\n children: JSX.Element\n errors: Array\n label: string\n}) {\n return (\n
\n \n {props.children}\n {props.errors.length ? (\n
{props.errors.join(', ')}
\n ) : null}\n
\n )\n}\n\nfunction FormExample() {\n const form = createForm(() => ({\n defaultValues: {\n fullName: '',\n email: '',\n address: {\n street: '',\n city: '',\n state: '',\n zipCode: '',\n country: '',\n },\n phone: '',\n },\n onSubmit: ({ value }) => {\n console.log(value)\n // Show success message\n alert('Form submitted successfully!')\n },\n }))\n\n return (\n
\n
\n
\n

TanStack Form

\n

Address Form

\n

\n Nested fields, field-level validation, and a select input.\n

\n
\n {\n e.preventDefault()\n e.stopPropagation()\n form.handleSubmit()\n }}\n class=\"space-y-6\"\n >\n {/* Full Name Field */}\n
\n {\n if (value.trim().length === 0) {\n return 'Full name is required'\n }\n if (value.length < 3) {\n return 'Name must be at least 3 characters'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* Email Field */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Email is required'\n }\n if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$/i.test(value)) {\n return 'Invalid email address'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* Street Address */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Street address is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* City, State, Zip in a row */}\n
\n {/* City */}\n {\n if (!value || value.trim().length === 0) {\n return 'City is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n\n {/* State */}\n {\n if (!value || value.trim().length === 0) {\n return 'State is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n\n {/* Zip Code */}\n {\n if (!value || value.trim().length === 0) {\n return 'Zip code is required'\n }\n if (!/^\\d{5}(-\\d{4})?$/.test(value)) {\n return 'Invalid zip code format'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* Country */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Country is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-select\"\n >\n \n \n \n \n \n \n \n \n \n \n )}\n />\n
\n\n {/* Phone Number */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Phone number is required'\n }\n if (\n !/^(\\+\\d{1,3})?\\s?\\(?\\d{3}\\)?[\\s.-]?\\d{3}[\\s.-]?\\d{4}$/.test(\n value,\n )\n ) {\n return 'Invalid phone number format'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n placeholder=\"(123) 456-7890\"\n />\n \n )}\n />\n
\n\n {/* Submit Button */}\n
\n \n {form.state.isSubmitting ? 'Submitting...' : 'Submit'}\n \n
\n \n
\n
\n )\n}\n\n") if (codeRouter) { __append("\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/form',\n component: FormExample,\n getParentRoute: () => parentRoute,\n})\n") } __append("\n") return __output } function __render_10add8c3_974(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { PrismaClient } from './generated/prisma/client.js'\n") if (addOnOption.prisma.database !== 'sqlite') { __append("\nimport { getDatabaseUrl } from './database-url.js'\n") } __append("\n\n") if (addOnOption.prisma.database === 'postgres') { __append("\nimport { PrismaPg } from '@prisma/adapter-pg';\n\nconst adapter = new PrismaPg({\n connectionString: getDatabaseUrl(),\n});") } __append("\n\n") if (addOnOption.prisma.database === 'mysql') { __append("\nimport { PrismaMariaDb } from '@prisma/adapter-mariadb';\nconst adapter = new PrismaMariaDb(getDatabaseUrl());") } __append("\n\n") if (addOnOption.prisma.database === 'sqlite') { __append("\nimport { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';\nconst adapter = new PrismaBetterSqlite3({\n url: process.env.DATABASE_URL || 'file:./dev.db'\n});") } __append("\n\ndeclare global {\n var __prisma: PrismaClient | undefined\n}\n\nexport const prisma = globalThis.__prisma || new PrismaClient({ adapter })\n\nif (process.env.NODE_ENV !== 'production') {\n globalThis.__prisma = prisma\n}\n") return __output } function __render_12c0ab3e_547(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (packageManager !== 'pnpm') { ignoreFile(); return; } __append("allowBuilds:\n esbuild: true\n lightningcss: true\n") if (addOnEnabled.sentry) { __append("'@sentry/cli': true\n") } if (addOnEnabled.cloudflare || addOnEnabled.netlify) { __append("sharp: true\n") } if (addOnEnabled.cloudflare) { __append("workerd: true\n") } if (addOnEnabled.prisma) { __append("'@prisma/engines': true\n prisma: true\n") } if (addOnEnabled.drizzle || (addOnEnabled.prisma && addOnOption.prisma.database === 'sqlite')) { __append("better-sqlite3: true\n") } return __output } function __render_17dececd_578(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!routerOnly) { ignoreFile() } __append("\nimport ReactDOM from 'react-dom/client'\nimport { RouterProvider, createRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen'\n\nconst router = createRouter({\n routeTree,\n defaultPreload: 'intent',\n scrollRestoration: true,\n})\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n\nconst rootElement = document.getElementById('app')!\n\nif (!rootElement.innerHTML) {\n const root = ReactDOM.createRoot(rootElement)\n root.render()\n}\n") return __output } function __render_1c6edce1_287(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("## Solid-UI\n\nThis installation of Solid-UI follows the manual instructions but was modified to work with Tailwind V4.\n\nTo install the components, run the following command (this install button):\n\n```bash\n") __append(getPackageManagerExecuteScript('solidui-cli@latest', ['add', 'button'])) __append("\n```\n") return __output } function __render_1c7fb2fe_1145(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"dependencies\": {\n \"prisma\": \"^7.4.2\",\n \"@prisma/client\": \"^7.4.2\"") if (addOnOption.prisma.database === 'postgres') { __append(",\n \"@prisma/adapter-pg\": \"^7.4.2\"") } if (addOnOption.prisma.database === 'mysql') { __append(",\n \"@prisma/adapter-mariadb\": \"^7.4.2\"") } if (addOnOption.prisma.database === 'sqlite') { __append(",\n \"@prisma/adapter-better-sqlite3\": \"^7.4.2\"") } __append("\n },\n \"devDependencies\": {\n \"dotenv-cli\": \"^11.0.0\",\n \"tsx\": \"^4.21.0\"\n },\n \"scripts\": {") if (addOnOption.prisma.database === 'postgres') { __append("\n \"post-cta-init\": \"") __append(getPackageManagerExecuteScript('create-db@latest', ['--user-agent', 'tanstack/tsrouter'])) __append("\",") } __append("\n \"db:generate\": \"dotenv -e .env.local -- prisma generate\",\n \"db:push\": \"dotenv -e .env.local -- prisma db push\",\n \"db:migrate\": \"dotenv -e .env.local -- prisma migrate dev\",\n \"db:studio\": \"dotenv -e .env.local -- prisma studio\",\n \"db:seed\": \"dotenv -e .env.local -- prisma db seed\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\n \"@prisma/engines\",\n \"prisma\"") if (addOnOption.prisma.database === 'sqlite') { __append(",\n \"better-sqlite3\"") } __append("\n ]\n }\n}\n") return __output } function __render_2051a6d5_789(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("# PowerSync\n\nThis project includes the PowerSync Web SDK and React hooks.\n\n## Environment\n\nSet these variables in `.env.local`:\n\n- `VITE_POWERSYNC_URL`\n- `VITE_POWERSYNC_TOKEN` for local development only\n\n## What The Add-on Includes\n\n- `src/lib/powersync/AppSchema.ts`\n- `src/lib/powersync/BackendConnector.ts`\n- `src/integrations/powersync/provider.tsx`\n- `src/routes/demo/powersync.tsx`\n\n## Next Steps\n\n1. Replace the development token flow in `src/lib/powersync/BackendConnector.ts` with your real auth flow.\n2. Update the sample schema in `src/lib/powersync/AppSchema.ts` to match your synced tables.\n3. Implement the upload logic in `uploadData()` so local mutations are written back to your backend.\n\nPowerSync setup guidance:\nhttps://docs.powersync.com/client-sdk-references/js-web\n") return __output } function __render_26a5a7ce_906(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n ") if (addOnEnabled.eslint) { __append("\"include\": [\"**/*.ts\", \"**/*.tsx\", \"eslint.config.js\", \"prettier.config.js\", \"vite.config.js\"],\n ") } else { __append("\"include\": [\"**/*.ts\", \"**/*.tsx\"],") } __append("\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"jsx\": \"react-jsx\",\n \"module\": \"ESNext\",\n \"paths\": {\n \"#/*\": [\"./src/*\"],\n \"@/*\": [\"./src/*\"]\n },\n \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n \"types\": [\"vite/client\"],\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,") if (addOnEnabled.paraglide) { __append("\n \"allowJs\": true,") } __append("\n \"verbatimModuleSyntax\": ") __append(__escapeXML(addOnEnabled['start'] ? 'false' : 'true')) __append(",\n \"noEmit\": true,\n\n /* Linting */\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUncheckedSideEffectImports\": true\n }\n}\n") return __output } function __render_27ac5638_1165(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { defineConfig } from \"vite\";\n") const showDevtools = !blank || integrations.some((integration) => integration.type === 'devtools') if (showDevtools) { __append("import { devtools } from '@tanstack/devtools-vite'\n") } if (tailwind) { __append("import tailwindcss from \"@tailwindcss/vite\"\n") } if (routerOnly) { __append("\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n") } else { __append("\nimport { tanstackStart } from \"@tanstack/solid-start/plugin/vite\";\n") } __append("\nimport solidPlugin from 'vite-plugin-solid';\n") for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { __append(integrationImportContent(integration)) __append("\n") } __append("\n\nexport default defineConfig({\n resolve: { tsconfigPaths: true },\n plugins: [") if (showDevtools) { __append("devtools(), ") } for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { __append(integrationImportCode(integration)) __append(",") } __append("\n ") if (tailwind) { __append("tailwindcss(),") } __append("\n ") if (routerOnly) { __append("tanstackRouter({ target: 'solid', autoCodeSplitting: true }),") } else { __append("tanstackStart(),") } __append("\n solidPlugin(") if (!routerOnly) { __append("{ ssr: true }") } __append("),\n ],\n})\n") return __output } function __render_2ca117a6_183(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("# Better Auth configuration\nBETTER_AUTH_URL=http://localhost:3000\nBETTER_AUTH_SECRET= # Generate a secret key: `") __append(getPackageManagerExecuteScript('@better-auth/cli', ['secret'])) __append("`\n") return __output } function __render_2cc85a1c_5032(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!includeExamples) { ignoreFile(); return; } __append("\nimport { Link } from '@tanstack/react-router'\n") for (const integration of integrations.filter((i) => i.type === 'header-user')) { __append("import ") __append(__escapeXML(integration.jsName)) __append(" from '") __append(__escapeXML(relativePath(integration.path))) __append("'\n") } __append("import ThemeToggle from './ThemeToggle'\n\n") const demoRoutes = []; for (const addOn of addOns) { for (const route of (addOn?.routes || []).filter((r) => r.url && r.name)) { demoRoutes.push({ url: route.url, name: route.name }); for (const child of route.children || []) { if (child?.url && child?.name) { demoRoutes.push({ url: child.url, name: child.name }); } } } } const userHeaders = integrations.filter((i) => i.type === 'header-user'); __append("\n\nexport default function Header() {\n return (\n
\n \n
\n )\n}\n") return __output } function __render_2f4e1dd0_4210(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { createFileRoute, useRouter } from '@tanstack/react-router'\nimport { createServerFn } from '@tanstack/react-start'\nimport { db } from '#/db/index'\nimport { desc } from 'drizzle-orm'\nimport { todos } from '#/db/schema'\n\nconst getTodos = createServerFn({\n method: 'GET',\n}).handler(async () => {\n return await db.query.todos.findMany({\n orderBy: [desc(todos.createdAt)],\n })\n})\n\nconst createTodo = createServerFn({\n method: 'POST',\n})\n .inputValidator((data: { title: string }) => data)\n .handler(async ({ data }) => {\n await db.insert(todos).values({ title: data.title })\n return { success: true }\n })\n\nexport const Route = createFileRoute('/demo/drizzle')({\n component: DemoDrizzle,\n loader: async () => await getTodos(),\n})\n\nfunction DemoDrizzle() {\n const router = useRouter()\n const todos = Route.useLoaderData()\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n const formData = new FormData(e.target as HTMLFormElement)\n const title = formData.get('title') as string\n\n if (!title) return\n\n try {\n await createTodo({ data: { title } })\n router.invalidate()\n ;(e.target as HTMLFormElement).reset()\n } catch (error) {\n console.error('Failed to create todo:', error)\n }\n }\n\n return (\n
\n
\n
\n \n \"Drizzle\n \n
\n

Database

\n

Drizzle Demo

\n
\n
\n\n

Todos

\n\n
    \n {todos.map((todo) => (\n \n
    \n {todo.title}\n #{todo.id}\n
    \n \n ))}\n {todos.length === 0 && (\n
  • \n No todos yet. Create one below!\n
  • \n )}\n
\n\n
\n \n \n Add Todo\n \n \n\n
\n

\n Powered by Drizzle ORM\n

\n

\n Next-generation ORM for Node.js & TypeScript with PostgreSQL\n

\n
\n

Setup Instructions:

\n
    \n
  1. \n Configure your{' '}\n DATABASE_URL{' '}\n in .env.local\n
  2. \n
  3. \n Run:{' '}\n \n ") __append(getPackageManagerExecuteScript('drizzle-kit', ['generate'])) __append("\n \n
  4. \n
  5. \n Run:{' '}\n \n ") __append(getPackageManagerExecuteScript('drizzle-kit', ['migrate'])) __append("\n \n
  6. \n
  7. \n Optional:{' '}\n \n ") __append(getPackageManagerExecuteScript('drizzle-kit', ['studio'])) __append("\n \n
  8. \n
\n
\n
\n
\n
\n )\n}\n") return __output } function __render_344f99a8_572(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!routerOnly) { ignoreFile() } __append("\nimport { render } from 'solid-js/web'\nimport { RouterProvider, createRouter } from '@tanstack/solid-router'\nimport { routeTree } from './routeTree.gen'\n\nconst router = createRouter({\n routeTree,\n defaultPreload: 'intent',\n defaultPreloadStaleTime: 0,\n scrollRestoration: true,\n})\n\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: typeof router\n }\n}\n\nconst rootElement = document.getElementById('app')!\n\nif (!rootElement.innerHTML) {\n render(() => , rootElement)\n}\n") return __output } function __render_3557ca92_591(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"$schema\": \"https://biomejs.dev/schemas/1.9.4/schema.json\",\n \"vcs\": {\n \"enabled\": false,\n \"clientKind\": \"git\",\n \"useIgnoreFile\": false\n },\n \"files\": {\n \"ignoreUnknown\": false,\n \"ignore\": [\"src/routeTree.gen.ts\"],\n \"include\": [\"src/*\", \".vscode/*\", \"index.html\", \"vite.config.ts\"]\n },\n \"formatter\": {\n \"enabled\": true,\n \"indentStyle\": \"tab\"\n },\n \"organizeImports\": {\n \"enabled\": true\n },\n \"linter\": {\n \"enabled\": true,\n \"rules\": {\n \"recommended\": true\n }\n },\n \"javascript\": {\n \"formatter\": {\n \"quoteStyle\": \"double\"\n }\n }\n}\n") return __output } function __render_37b41d11_8011(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { useStore } from '@tanstack/react-form'\n\nimport { useFieldContext, useFormContext } from '#/hooks/demo.form-context'\n") if (addOnEnabled.shadcn) { __append("\nimport { Button } from '#/components/ui/button'\nimport { Input } from '#/components/ui/input'\nimport { Textarea as ShadcnTextarea } from '#/components/ui/textarea'\nimport * as ShadcnSelect from '#/components/ui/select'\nimport { Slider as ShadcnSlider } from '#/components/ui/slider'\nimport { Switch as ShadcnSwitch } from '#/components/ui/switch'\nimport { Label } from '#/components/ui/label'\n\nexport function SubscribeButton({ label }: { label: string }) {\n const form = useFormContext()\n return (\n state.isSubmitting}>\n {(isSubmitting) => (\n \n )}\n \n )\n}\n\nfunction ErrorMessages({\n errors,\n}: {\n errors: Array\n}) {\n return (\n <>\n {errors.map((error) => (\n \n {typeof error === 'string' ? error : error.message}\n \n ))}\n \n )\n}\n\nexport function TextField({\n label,\n placeholder,\n}: {\n label: string\n placeholder?: string\n}) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n \n field.handleChange(e.target.value)}\n />\n {field.state.meta.isTouched && }\n
\n )\n}\n\nexport function TextArea({\n label,\n rows = 3,\n}: {\n label: string\n rows?: number\n}) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n \n field.handleChange(e.target.value)}\n />\n {field.state.meta.isTouched && }\n
\n )\n}\n\nexport function Select({\n label,\n values,\n placeholder,\n}: {\n label: string\n values: Array<{ label: string; value: string }>\n placeholder?: string\n}) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n field.handleChange(value)}\n >\n \n \n \n \n \n {label}\n {values.map((value) => (\n \n {value.label}\n \n ))}\n \n \n \n {field.state.meta.isTouched && }\n
\n )\n}\n\nexport function Slider({ label }: { label: string }) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n \n field.handleChange(value[0])}\n />\n {field.state.meta.isTouched && }\n
\n )\n}\n\nexport function Switch({ label }: { label: string }) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n
\n field.handleChange(checked)}\n />\n \n
\n {field.state.meta.isTouched && }\n
\n )\n}\n \n") } else { __append("\nexport function SubscribeButton({ label }: { label: string }) {\n const form = useFormContext()\n return (\n state.isSubmitting}>\n {(isSubmitting) => (\n \n {label}\n \n )}\n \n )\n}\n\nfunction ErrorMessages({\n errors,\n}: {\n errors: Array\n}) {\n return (\n <>\n {errors.map((error) => (\n \n {typeof error === 'string' ? error : error.message}\n \n ))}\n \n )\n}\n\nexport function TextField({\n label,\n placeholder,\n}: {\n label: string\n placeholder?: string\n}) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n \n {field.state.meta.isTouched && }\n
\n )\n}\n\nexport function TextArea({\n label,\n rows = 3,\n}: {\n label: string\n rows?: number\n}) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n \n {field.state.meta.isTouched && }\n
\n )\n}\n\nexport function Select({\n label,\n values,\n}: {\n label: string\n values: Array<{ label: string; value: string }>\n placeholder?: string\n}) {\n const field = useFieldContext()\n const errors = useStore(field.store, (state) => state.meta.errors)\n\n return (\n
\n \n field.handleChange(e.target.value)}\n className=\"demo-select\"\n >\n {values.map((value) => (\n \n ))}\n \n {field.state.meta.isTouched && }\n
\n )\n}\n") } __append("\n") return __output } function __render_3842e959_4368(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (blank) { __append("\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/')({ component: Home })\n\nfunction Home() {\n return (\n
\n

Welcome to TanStack Start

\n

\n Edit src/routes/index.tsx to get started.\n

\n
\n )\n}\n") } else if (!includeExamples) { __append("\nimport { createFileRoute } from \"@tanstack/react-router\";\n\nexport const Route = createFileRoute(\"/\")({ component: Home });\n\nfunction Home() {\n return (\n
\n

Welcome to TanStack Start

\n

\n Edit src/routes/index.tsx to get started.\n

\n
\n );\n}\n") } else { __append("\nimport { createFileRoute } from \"@tanstack/react-router\";\n\nexport const Route = createFileRoute(\"/\")({ component: App });\n\nfunction App() {\n return (\n
\n
\n
\n
\n

TanStack Start Base Template

\n

\n Start simple, ship quickly.\n

\n

\n This base starter intentionally keeps things light: two routes, clean structure,\n and the essentials you need to build from scratch.\n

\n
\n \n About This Starter\n \n \n Router Guide\n \n
\n
\n\n
\n {[\n ['Type-Safe Routing', 'Routes and links stay in sync across every page.'],\n ['Server Functions', 'Call server code from your UI without creating API boilerplate.'],\n ['Streaming by Default', 'Ship progressively rendered responses for faster experiences.'],\n ['Tailwind Native', 'Design quickly with utility-first styling and reusable tokens.'],\n ].map(([title, desc], index) => (\n \n

{title}

\n

{desc}

\n \n ))}\n
\n\n
\n

Quick Start

\n
    \n
  • \n Edit src/routes/index.tsx to customize the home page.\n
  • \n
  • \n Update src/components/Header.tsx and src/components/Footer.tsx{' '}\n for brand links.\n
  • \n
  • \n Add routes in src/routes and tweak visual tokens in src/styles.css.\n
  • \n
\n
\n
\n );\n}\n") } __append("\n") return __output } function __render_3b6dabd2_855(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n ") if (addOnEnabled.eslint) { __append("\"include\": [\"**/*.ts\", \"**/*.tsx\", \"eslint.config.js\", \"prettier.config.js\", \"vite.config.js\"],\n ") } else { __append("\"include\": [\"**/*.ts\", \"**/*.tsx\"],") } __append("\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"jsx\": \"preserve\",\n \"jsxImportSource\": \"solid-js\",\n \"module\": \"ESNext\",\n \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n \"types\": [\"vite/client\"],\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": true,\n \"noEmit\": true,\n\n /* Linting */\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUncheckedSideEffectImports\": true") if (addOnEnabled['solid-ui']) { __append(",\n \"paths\": {\n \"~/*\": [\"./src/*\"]\n }\n ") } __append("\n }\n}\n") return __output } function __render_3bba1d86_4124(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { createFileRoute, useRouter } from '@tanstack/react-router'\nimport { createServerFn } from '@tanstack/react-start'\nimport { prisma } from '#/db'\n\nconst getTodos = createServerFn({\n method: 'GET',\n}).handler(async () => {\n return await prisma.todo.findMany({\n orderBy: { createdAt: 'desc' },\n })\n})\n\nconst createTodo = createServerFn({\n method: 'POST',\n})\n .validator((data: { title: string }) => data)\n .handler(async ({ data }) => {\n return await prisma.todo.create({\n data,\n })\n })\n\nexport const Route = createFileRoute('/demo/prisma')({\n component: DemoPrisma,\n loader: async () => await getTodos(),\n})\n\nfunction DemoPrisma() {\n const router = useRouter()\n const todos = Route.useLoaderData()\n\n const handleSubmit = async (e: React.FormEvent) => {\n e.preventDefault()\n const form = e.currentTarget\n const title = new FormData(form).get('title')\n\n if (typeof title !== 'string' || !title) return\n\n try {\n await createTodo({ data: { title } })\n router.invalidate()\n form.reset()\n } catch (error) {\n console.error('Failed to create todo:', error)\n }\n }\n\n return (\n
\n
\n
\n \n \"Prisma\n \n
\n

Database

\n

Prisma Demo

\n
\n
\n\n

Todos

\n\n
    \n {todos.map((todo) => (\n \n
    \n {todo.title}\n #{todo.id}\n
    \n \n ))}\n {todos.length === 0 && (\n
  • \n No todos yet. Create one below!\n
  • \n )}\n
\n\n
\n \n \n Add Todo\n \n \n\n
\n

\n Powered by Prisma ORM\n

\n

\n Next-generation ORM for Node.js & TypeScript with ") __append(__escapeXML(addOnOption.prisma.database === 'postgres' ? 'PostgreSQL' : addOnOption.prisma.database === 'mysql' ? 'MySQL' : 'SQLite')) __append("\n

\n
\n

Setup Instructions:

\n
    \n
  1. \n Configure your{' '}\n DATABASE_URL{' '}\n in .env.local\n
  2. \n
  3. \n Run:{' '}\n \n ") __append(getPackageManagerRunScript('db:generate')) __append("\n \n
  4. \n
  5. \n Run:{' '}\n \n ") __append(getPackageManagerRunScript('db:push')) __append("\n \n
  6. \n
  7. \n Optional:{' '}\n \n ") __append(getPackageManagerRunScript('db:studio')) __append("\n \n
  8. \n
\n
\n
\n
\n
\n )\n}\n") return __output } function __render_3f5ed8bc_379(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!addOnEnabled.sentry) { ignoreFile(); return; } __append("import '../instrument.server.mjs'\nimport { wrapFetchWithSentry } from '@sentry/tanstackstart-react'\nimport handler, { createServerEntry } from '@tanstack/react-start/server-entry'\n\nexport default createServerEntry(\n wrapFetchWithSentry({\n fetch(request: Request) {\n return handler.fetch(request)\n },\n }),\n)\n") return __output } function __render_46ac6367_999(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { createFileRoute } from \"@tanstack/react-router\";\nimport logo from \"../logo.svg\";\nimport { m } from \"#/paraglide/messages\";\nimport LocaleSwitcher from \"../components/LocaleSwitcher\";\n\nexport const Route = createFileRoute(\"/demo/i18n\")({\n component: App,\n});\n\nfunction App() {\n return (\n
\n
\n \n

{m.example_message({ username: \"TanStack Router\" })}

\n \n {m.learn_router()}\n \n
\n \n
\n
\n
\n );\n}\n") return __output } function __render_49e52a1d_2177(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { useEffect, useState } from 'react'\nimport { createFileRoute } from '@tanstack/react-router'\nimport * as v from 'valibot'\n\nimport { isCustomerAccountConfigured } from '#/server/shopify/env'\nimport { startLogin } from '#/server/shopify/customer.functions'\n\nconst LoginSearch = v.object({ redirect: v.optional(v.string(), '/shop/account') })\n\nexport const Route = createFileRoute('/shop/account/login')({\n validateSearch: (search) => v.parse(LoginSearch, search),\n loader: () => ({ configured: isCustomerAccountConfigured() }),\n component: LoginRoute,\n})\n\nfunction LoginRoute() {\n const { redirect } = Route.useSearch()\n const { configured } = Route.useLoaderData()\n const [error, setError] = useState(null)\n\n useEffect(() => {\n if (!configured) return\n startLogin({ data: { redirectAfter: redirect } })\n .then(({ authorizationUrl }) => {\n window.location.assign(authorizationUrl)\n })\n .catch((err) => setError(err instanceof Error ? err.message : String(err)))\n }, [configured, redirect])\n\n if (!configured) {\n return (\n
\n

Customer accounts not configured

\n

\n Set SHOPIFY_CUSTOMER_ACCOUNT_CLIENT_ID,{' '}\n SHOPIFY_CUSTOMER_ACCOUNT_SHOP_ID, and{' '}\n SHOPIFY_SESSION_SECRET in .env.local, then\n register the redirect URI in your Shopify admin.\n

\n
\n )\n }\n\n if (error) {\n return (\n
\n

Couldn't start sign-in

\n

{error}

\n
\n )\n }\n\n return (\n

\n Redirecting to Shopify…\n

\n )\n}\n") return __output } function __render_4f27c616_2520(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { Link, createFileRoute } from '@tanstack/react-router'\n\nimport { Money } from '#/components/shop/money'\nimport { getOrders } from '#/server/shopify/customer.functions'\n\nexport const Route = createFileRoute('/shop/account/')({\n loader: () => getOrders({ data: { first: 5 } }),\n component: AccountOverview,\n})\n\nfunction AccountOverview() {\n const orders = Route.useLoaderData()\n const { customer } = Route.useRouteContext()\n\n return (\n
\n
\n

\n Welcome back\n

\n

\n {customer.firstName ?? customer.emailAddress?.emailAddress ?? 'Customer'}\n

\n
\n\n
\n
\n

Recent orders

\n \n View all\n \n
\n {orders.nodes.length === 0 ? (\n

No orders yet.

\n ) : (\n
    \n {orders.nodes.map((order) => (\n \n
    \n \n {order.name}\n \n

    \n {new Date(order.processedAt).toLocaleDateString()} ·{' '}\n {order.fulfillmentStatus ?? 'pending'}\n

    \n
    \n \n \n ))}\n
\n )}\n
\n
\n )\n}\n") return __output } function __render_5da70d0f_237(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"scripts\": {\n \"start\": \"") __append(__escapeXML(addOnEnabled.sentry ? 'node --import ./.output/server/instrument.server.mjs .output/server/index.mjs' : 'node .output/server/index.mjs')) __append("\"\n },\n \"dependencies\": {\n \"nitro\": \"3.0.260610-beta\"\n }\n}\n") return __output } function __render_66f4457e_670(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"scripts\": {\n \"build\": \"") __append(__escapeXML(addOnEnabled.vercel ? 'vite build' : 'vite build && cp instrument.server.mjs .output/server')) __append("\",\n \"dev\": \"") __append(__escapeXML(addOnEnabled.vercel ? 'dotenv -e .env.local -- vite dev --port 3000' : 'dotenv -e .env.local -- sh -c \"NODE_OPTIONS=\'--import ./instrument.server.mjs\' vite dev --port 3000\"')) __append("\",\n \"start\": \"") __append(__escapeXML(addOnEnabled.vercel ? 'node .output/server/index.mjs' : 'node --import ./.output/server/instrument.server.mjs .output/server/index.mjs')) __append("\"\n },\n \"dependencies\": {\n \"@sentry/tanstackstart-react\": \"^10.67.0\",\n \"dotenv-cli\": \"^11.0.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\n \"@sentry/cli\"\n ]\n }\n}\n") return __output } function __render_6cd21925_416(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.drizzle.database === 'postgresql') { __append("\n# Database URL for PostgreSQL\nDATABASE_URL=\"postgresql://username:password@localhost:5432/mydb\"") } else if (addOnOption.drizzle.database === 'mysql') { __append("\n# Database URL for MySQL\nDATABASE_URL=\"mysql://username:password@localhost:3306/mydb\"") } else if (addOnOption.drizzle.database === 'sqlite') { __append("\n# Database URL for SQLite\nDATABASE_URL=\"dev.db\"") } return __output } function __render_6efd6a86_1382(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { createRouter as createTanStackRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen'\n") if (addOnEnabled['tanstack-query']) { if (addOnEnabled.tRPC) { __append("\nimport type { ReactNode } from 'react'") } __append("\nimport { setupRouterSsrQueryIntegration } from \"@tanstack/react-router-ssr-query\";\nimport ") if (addOnEnabled.tRPC) { __append("TanstackQueryProvider, ") } __append("{ getContext } from './integrations/tanstack-query/root-provider'\n") } __append("\n\nexport function getRouter() {\n ") if (addOnEnabled['tanstack-query']) { __append("\n const context = getContext();\n ") } __append("\n \n const router = createTanStackRouter({\n routeTree,") if (addOnEnabled['tanstack-query']) { __append("\n context,") } else if (addOnEnabled['apollo-client']) { __append("\n context: {} as any,\n") } __append("\n scrollRestoration: true,\n defaultPreload: 'intent',\n defaultPreloadStaleTime: 0,\n ") if (addOnEnabled.tRPC) { __append("\n Wrap: (props: { children: ReactNode }) => {\n return (\n \n {props.children}\n \n );\n },\n ") } __append("\n })\n\n") if (addOnEnabled['tanstack-query']) { __append("\n setupRouterSsrQueryIntegration({ router, queryClient: context.queryClient })\n") } __append("\n\n return router\n}\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: ReturnType\n }\n}\n") return __output } function __render_7094245c_344(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!routerOnly) { ignoreFile() } __append("\n\n\n \n \n \n ") __append(__escapeXML(projectName)) __append("\n \n \n
\n \n \n\n") return __output } function __render_7124b617_1484(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("// Locale switcher refs:\n// - Paraglide docs: https://inlang.com/m/gerre34r/library-inlang-paraglideJs\n// - Router example: https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#switching-locale\nimport { getLocale, locales, setLocale } from '#/paraglide/runtime'\nimport { m } from '#/paraglide/messages'\n\nexport default function ParaglideLocaleSwitcher() {\n const currentLocale = getLocale()\n\n return (\n \n {m.current_locale({ locale: currentLocale })}\n
\n {locales.map((locale) => (\n setLocale(locale)}\n aria-pressed={locale === currentLocale}\n style={{\n cursor: 'pointer',\n padding: '0.35rem 0.75rem',\n borderRadius: '999px',\n border: '1px solid #d1d5db',\n background: locale === currentLocale ? '#0f172a' : 'transparent',\n color: locale === currentLocale ? '#f8fafc' : 'inherit',\n fontWeight: locale === currentLocale ? 700 : 500,\n letterSpacing: '0.01em',\n }}\n >\n {locale.toUpperCase()}\n \n ))}\n
\n \n )\n}\n") return __output } function __render_7480f519_277(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"scripts\": {\n \"deploy\": \"") __append(__escapeXML(packageManager)) __append(" run build && wrangler deploy\"\n },\n \"dependencies\": {\n \"@cloudflare/vite-plugin\": \"^1.26.0\"\n },\n \"devDependencies\": {\n \"wrangler\": \"^4.70.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\"sharp\", \"workerd\"]\n }\n}\n") return __output } function __render_7599e122_260(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.prisma.database === 'sqlite') { ignoreFile(); return; } __append("export function getDatabaseUrl() {\n const databaseUrl = process.env.DATABASE_URL\n\n if (!databaseUrl) {\n throw new Error('DATABASE_URL is required')\n }\n\n return databaseUrl\n}\n") return __output } function __render_761185f3_3529(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnEnabled['start']) { /** Start todos implementation */ __append("import { useCallback, useState } from 'react'\nimport { createFileRoute } from '@tanstack/react-router'\nimport { useQuery, useMutation } from '@tanstack/react-query'\n\nexport const Route = createFileRoute('/demo/tanstack-query')({\n component: TanStackQueryDemo,\n})\n\ntype Todo = {\n id: number\n name: string\n}\n\nfunction TanStackQueryDemo() {\n const { data, refetch } = useQuery({\n queryKey: ['todos'],\n queryFn: () => fetch('/demo/api/tq-todos').then((res) => res.json()),\n initialData: [],\n })\n\n const { mutate: addTodo } = useMutation({\n mutationFn: (todo: string) =>\n fetch('/demo/api/tq-todos', {\n method: 'POST',\n body: JSON.stringify(todo),\n }).then((res) => res.json()),\n onSuccess: () => refetch(),\n })\n\n const [todo, setTodo] = useState('')\n\n const submitTodo = useCallback(async () => {\n await addTodo(todo)\n setTodo('')\n }, [addTodo, todo])\n\n return (\n
\n
\n

TanStack Query

\n

TanStack Query Todos

\n
    \n {data?.map((t) => (\n
  • \n {t.name}\n
  • \n ))}\n
\n
\n setTodo(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n submitTodo()\n }\n }}\n placeholder=\"Enter a new todo...\"\n className=\"demo-input\"\n />\n \n Add todo\n \n
\n
\n
\n )\n}\n") } else { /** Simple in-memory query implementation */ __append("import { ") if (fileRouter) { __append("createFileRoute") } else { __append("createRoute") } __append(" } from '@tanstack/react-router'\nimport { useQuery } from '@tanstack/react-query'\n") if (codeRouter) { __append("\nimport type { RootRoute } from '@tanstack/react-router'\n") } else { __append("\nexport const Route = createFileRoute('/demo/tanstack-query')({\n component: TanStackQueryDemo,\n})\n") } __append("\nfunction TanStackQueryDemo() {\n const { data } = useQuery({\n queryKey: ['todos'],\n queryFn: () => Promise.resolve([\n { id: 1, name: 'Alice' },\n { id: 2, name: 'Bob' },\n { id: 3, name: 'Charlie' },\n ]),\n initialData: [],\n })\n\n return (\n
\n
\n

TanStack Query

\n

\n TanStack Query Simple Promise Handling\n

\n
    \n {data.map((todo) => (\n
  • \n {todo.name}\n
  • \n ))}\n
\n
\n
\n )\n}\n\n") if (codeRouter) { __append("\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/tanstack-query',\n component: TanStackQueryDemo,\n getParentRoute: () => parentRoute,\n})\n") } __append("\n") } __append("\n") return __output } function __render_799a9561_1681(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { defineConfig } from 'vite'\n") const showDevtools = !blank || integrations.some((integration) => integration.type === 'devtools') if (showDevtools) { __append("import { devtools } from '@tanstack/devtools-vite'\n") } if (addOnEnabled.paraglide) { __append("import { paraglideVitePlugin } from \"@inlang/paraglide-js\"\n") } if (routerOnly) { __append("\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n") } else { __append("\nimport { tanstackStart } from '@tanstack/react-start/plugin/vite';\n") } __append("\n") if (addOnEnabled.compiler) { __append("import viteReact, { reactCompilerPreset } from '@vitejs/plugin-react'\nimport babel from '@rolldown/plugin-babel'\n") } else { __append("import viteReact from '@vitejs/plugin-react'\n") } if (tailwind) { __append("import tailwindcss from \"@tailwindcss/vite\"\n") } for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { __append(integrationImportContent(integration)) __append("\n") } __append("\n\nconst config = defineConfig({\n resolve: { tsconfigPaths: true },\n plugins: [") if (showDevtools) { __append("devtools(), ") } if (addOnEnabled.paraglide) { __append("paraglideVitePlugin({\n project: './project.inlang',\n outdir: './src/paraglide',\n strategy: ['url', \"baseLocale\"],\n }), ") } for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { __append(integrationImportCode(integration)) __append(",") } __append("\n ") if (tailwind) { __append("tailwindcss(),") } __append("\n ") if (routerOnly) { __append("tanstackRouter({ target: 'react', autoCodeSplitting: true }),") } else { __append("tanstackStart(),") } __append("\n viteReact(),") if (addOnEnabled.compiler) { __append("\n babel({ presets: [reactCompilerPreset()] }),") } __append("\n ],\n})\n\nexport default config\n") return __output } function __render_7a173ee3_2276(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { createFileRoute } from '@tanstack/react-router'\n\nimport { getAddresses } from '#/server/shopify/customer.functions'\n\nexport const Route = createFileRoute('/shop/account/addresses')({\n loader: () => getAddresses(),\n component: AddressesRoute,\n})\n\nfunction AddressesRoute() {\n const { addresses, defaultAddressId } = Route.useLoaderData()\n\n if (addresses.length === 0) {\n return (\n
\n

Addresses

\n
\n No saved addresses yet. Addresses you use at checkout will appear here.\n
\n
\n )\n }\n\n return (\n
\n

Addresses

\n
    \n {addresses.map((address) => (\n \n

    \n {[address.firstName, address.lastName].filter(Boolean).join(' ')}\n {address.id === defaultAddressId && (\n \n Default\n \n )}\n

    \n

    \n {address.address1}\n {address.address2 && (\n <>\n
    \n {address.address2}\n \n )}\n
    \n {[address.city, address.zoneCode, address.zip]\n .filter(Boolean)\n .join(', ')}\n
    \n {address.territoryCode}\n

    \n {address.phoneNumber && (\n

    \n {address.phoneNumber}\n

    \n )}\n \n ))}\n
\n
\n )\n}\n") return __output } function __render_7bee555e_1013(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.drizzle.database === 'postgresql') { __append("\nimport { pgTable, serial, text, timestamp } from\n'drizzle-orm/pg-core';\n\nexport const todos = pgTable('todos', {\n id: serial().primaryKey(),\n title: text().notNull(),\n createdAt: timestamp('created_at').defaultNow(),\n});\n") } else if (addOnOption.drizzle.database === 'mysql') { __append("\nimport { mysqlTable, int, text, timestamp } from\n'drizzle-orm/mysql-core';\n\nexport const todos = mysqlTable('todos', {\n id: int().primaryKey().autoincrement(),\n title: text().notNull(),\n createdAt: timestamp('created_at', { mode: 'date' }).defaultNow(),\n});\n") } else if (addOnOption.drizzle.database === 'sqlite') { __append("\nimport { sqliteTable, integer, text } from\n'drizzle-orm/sqlite-core';\nimport { sql } from 'drizzle-orm';\n\nexport const todos = sqliteTable('todos', {\n id: integer({ mode: 'number' }).primaryKey({\nautoIncrement: true }),\n title: text().notNull(),\n createdAt: integer('created_at', { mode: 'timestamp' }).default(sql`(unixepoch())`),\n});\n") } return __output } function __render_7c920d31_1166(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport {\n deleteCookie,\n getCookie,\n setCookie,\n} from '@tanstack/react-start/server'\n\nconst SESSION_COOKIE = 'shopify_customer_session'\nconst FLIGHT_COOKIE = 'shopify_oauth_state'\n\nconst sessionCookieOptions = () => ({\n path: '/' as const,\n maxAge: 60 * 60 * 24 * 30, // 30 days\n sameSite: 'lax' as const,\n httpOnly: true,\n secure: import.meta.env.PROD,\n})\n\nconst flightCookieOptions = () => ({\n path: '/' as const,\n maxAge: 60 * 10, // 10 min\n sameSite: 'lax' as const,\n httpOnly: true,\n secure: import.meta.env.PROD,\n})\n\nexport function getSessionCookie() {\n return getCookie(SESSION_COOKIE)\n}\n\nexport function setSessionCookie(value: string) {\n setCookie(SESSION_COOKIE, value, sessionCookieOptions())\n}\n\nexport function clearSessionCookie() {\n deleteCookie(SESSION_COOKIE, { path: '/' })\n}\n\nexport function getFlightCookie() {\n return getCookie(FLIGHT_COOKIE)\n}\n\nexport function setFlightCookie(value: string) {\n setCookie(FLIGHT_COOKIE, value, flightCookieOptions())\n}\n\nexport function clearFlightCookie() {\n deleteCookie(FLIGHT_COOKIE, { path: '/' })\n}\n") return __output } function __render_7cd0e566_8867(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (blank) { __append("\n") if (tailwind) { __append("@import 'tailwindcss';\n\n") } __append("\n* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n font-family: system-ui, sans-serif;\n}\n\nmain {\n width: min(42rem, calc(100% - 2rem));\n margin: 4rem auto;\n}\n") } else if (!includeExamples) { __append("\n@import 'tailwindcss';\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody,\n#app {\n min-height: 100%;\n}\n\nbody {\n margin: 0;\n}\n") } else { __append("\n@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap');\n@import 'tailwindcss';\n\n:root {\n --sea-ink: #173a40;\n --sea-ink-soft: #416166;\n --lagoon: #4fb8b2;\n --lagoon-deep: #328f97;\n --palm: #2f6a4a;\n --sand: #e7f0e8;\n --foam: #f3faf5;\n --surface: rgba(255, 255, 255, 0.74);\n --surface-strong: rgba(255, 255, 255, 0.9);\n --line: rgba(23, 58, 64, 0.14);\n --inset-glint: rgba(255, 255, 255, 0.82);\n --kicker: rgba(47, 106, 74, 0.9);\n --bg-base: #e7f3ec;\n --header-bg: rgba(251, 255, 248, 0.84);\n --chip-bg: rgba(255, 255, 255, 0.8);\n --chip-line: rgba(47, 106, 74, 0.18);\n --link-bg-hover: rgba(255, 255, 255, 0.9);\n --hero-a: rgba(79, 184, 178, 0.36);\n --hero-b: rgba(47, 106, 74, 0.2);\n}\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody,\n#app {\n min-height: 100%;\n}\n\nbody {\n margin: 0;\n color: var(--sea-ink);\n font-family: 'Manrope', ui-sans-serif, system-ui, sans-serif;\n background-color: var(--bg-base);\n background:\n radial-gradient(1100px 620px at -8% -10%, var(--hero-a), transparent 58%),\n radial-gradient(1050px 620px at 112% -12%, var(--hero-b), transparent 62%),\n radial-gradient(720px 380px at 50% 115%, rgba(79, 184, 178, 0.1), transparent 68%),\n linear-gradient(\n 180deg,\n color-mix(in oklab, var(--sand) 68%, white) 0%,\n var(--foam) 44%,\n var(--bg-base) 100%\n );\n overflow-x: hidden;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\na {\n color: var(--lagoon-deep);\n text-decoration-color: rgba(50, 143, 151, 0.4);\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n}\n\na:hover {\n color: #246f76;\n}\n\ncode {\n font-size: 0.9em;\n border: 1px solid var(--line);\n background: color-mix(in oklab, var(--surface-strong) 82%, white 18%);\n border-radius: 7px;\n padding: 2px 7px;\n}\n\n.page-wrap {\n width: min(1080px, calc(100% - 2rem));\n margin-inline: auto;\n}\n\n.display-title {\n font-family: 'Fraunces', Georgia, serif;\n}\n\n.island-shell {\n border: 1px solid var(--line);\n background: linear-gradient(165deg, var(--surface-strong), var(--surface));\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 22px 44px rgba(30, 90, 72, 0.1),\n 0 6px 18px rgba(23, 58, 64, 0.08);\n backdrop-filter: blur(4px);\n}\n\n.feature-card {\n background: linear-gradient(\n 165deg,\n color-mix(in oklab, var(--surface-strong) 93%, white 7%),\n var(--surface)\n );\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 18px 34px rgba(30, 90, 72, 0.1),\n 0 4px 14px rgba(23, 58, 64, 0.06);\n}\n\n.demo-page {\n width: min(1080px, calc(100% - 2rem));\n margin-inline: auto;\n padding-block: clamp(2.25rem, 6vw, 4.5rem);\n color: var(--sea-ink);\n}\n\n.demo-page-wide {\n width: min(1280px, calc(100% - 2rem));\n}\n\n.demo-center {\n min-height: calc(100vh - 13rem);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.demo-panel,\n.demo-card {\n border: 1px solid var(--line);\n background: linear-gradient(165deg, var(--surface-strong), var(--surface));\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 18px 34px rgba(30, 90, 72, 0.1),\n 0 4px 14px rgba(23, 58, 64, 0.06);\n backdrop-filter: blur(4px);\n}\n\n.demo-panel {\n border-radius: 1.25rem;\n padding: clamp(1.25rem, 4vw, 2rem);\n}\n\n.demo-card {\n border-radius: 1rem;\n padding: 1rem;\n}\n\n.demo-title {\n margin: 0;\n color: var(--sea-ink);\n font-size: clamp(1.75rem, 4vw, 2.75rem);\n line-height: 1.05;\n font-weight: 800;\n letter-spacing: 0;\n}\n\n.demo-section-title {\n margin: 0;\n color: var(--sea-ink);\n font-size: 1rem;\n font-weight: 700;\n}\n\n.demo-muted {\n color: var(--sea-ink-soft);\n}\n\n.demo-input,\n.demo-select,\n.demo-textarea {\n width: 100%;\n border: 1px solid var(--line);\n border-radius: 0.75rem;\n background: color-mix(in oklab, var(--surface-strong) 88%, white 12%);\n color: var(--sea-ink);\n outline: none;\n transition: border-color 180ms ease, box-shadow 180ms ease, background-color 180ms ease;\n}\n\n.demo-input,\n.demo-select {\n padding: 0.7rem 0.9rem;\n}\n\n.demo-input-fit {\n width: auto;\n}\n\n.demo-textarea {\n min-height: 7rem;\n padding: 0.8rem 0.9rem;\n resize: vertical;\n}\n\n.demo-input:focus,\n.demo-select:focus,\n.demo-textarea:focus {\n border-color: color-mix(in oklab, var(--lagoon-deep) 58%, var(--line));\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--lagoon) 24%, transparent);\n}\n\n.demo-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n border: 1px solid color-mix(in oklab, var(--lagoon-deep) 34%, var(--line));\n border-radius: 0.85rem;\n background: color-mix(in oklab, var(--lagoon) 22%, var(--surface-strong));\n color: var(--sea-ink);\n padding: 0.72rem 1rem;\n font-size: 0.9rem;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n}\n\n.demo-button:hover {\n transform: translateY(-1px);\n background: color-mix(in oklab, var(--lagoon) 30%, var(--surface-strong));\n}\n\n.demo-button:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n transform: none;\n}\n\n.demo-button-secondary {\n border-color: var(--line);\n background: color-mix(in oklab, var(--surface-strong) 74%, transparent);\n color: var(--sea-ink-soft);\n}\n\n.demo-button-danger {\n border-color: rgba(196, 71, 71, 0.28);\n background: rgba(196, 71, 71, 0.1);\n color: #9f3030;\n}\n\n.demo-list-item {\n border: 1px solid var(--line);\n border-radius: 0.9rem;\n background: color-mix(in oklab, var(--chip-bg) 82%, transparent);\n padding: 0.9rem 1rem;\n}\n\n.demo-pill {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n border: 1px solid var(--chip-line);\n border-radius: 999px;\n background: var(--chip-bg);\n color: var(--sea-ink-soft);\n padding: 0.35rem 0.65rem;\n font-size: 0.75rem;\n font-weight: 700;\n}\n\n.demo-alert {\n border: 1px solid rgba(193, 126, 42, 0.3);\n border-radius: 1rem;\n background: rgba(193, 126, 42, 0.1);\n color: var(--sea-ink);\n padding: 1rem;\n}\n\n.demo-alert-danger {\n border-color: rgba(196, 71, 71, 0.3);\n background: rgba(196, 71, 71, 0.1);\n}\n\n.demo-code-block {\n border: 1px solid var(--line);\n border-radius: 1rem;\n background: color-mix(in oklab, var(--chip-bg) 88%, transparent);\n color: var(--sea-ink);\n padding: 1rem;\n}\n\n.demo-table-shell {\n overflow-x: auto;\n border: 1px solid var(--line);\n border-radius: 1rem;\n background: color-mix(in oklab, var(--surface-strong) 78%, transparent);\n}\n\n.demo-table {\n width: 100%;\n border-collapse: collapse;\n color: var(--sea-ink);\n}\n\n.demo-table th,\n.demo-table td {\n border-bottom: 1px solid var(--line);\n padding: 0.75rem 1rem;\n text-align: left;\n}\n\n.demo-table th {\n background: color-mix(in oklab, var(--chip-bg) 88%, transparent);\n color: var(--sea-ink);\n font-weight: 700;\n}\n\n.demo-table tr:hover td {\n background: color-mix(in oklab, var(--lagoon) 8%, transparent);\n}\n\n.site-header {\n position: sticky;\n top: 0;\n z-index: 50;\n border-bottom: 1px solid var(--line);\n background: var(--header-bg);\n backdrop-filter: blur(10px);\n}\n\n.nav-shell {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0.5rem 0.75rem;\n padding: 0.75rem 0;\n}\n\n.brand-pill {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n border-radius: 999px;\n border: 1px solid var(--chip-line);\n background: var(--chip-bg);\n padding: 0.5rem 0.875rem;\n color: var(--sea-ink);\n text-decoration: none;\n box-shadow: 0 8px 24px rgba(30, 90, 72, 0.08);\n}\n\n.brand-dot {\n height: 0.5rem;\n width: 0.5rem;\n border-radius: 999px;\n background: linear-gradient(90deg, #56c6be, #7ed3bf);\n}\n\n.island-kicker {\n letter-spacing: 0.16em;\n text-transform: uppercase;\n font-weight: 700;\n font-size: 0.69rem;\n color: var(--kicker);\n}\n\n.nav-link {\n position: relative;\n display: inline-flex;\n align-items: center;\n color: var(--sea-ink-soft);\n text-decoration: none;\n}\n\n.nav-link::after {\n content: '';\n position: absolute;\n left: 0;\n bottom: -6px;\n width: 100%;\n height: 2px;\n transform: scaleX(0);\n transform-origin: left;\n background: linear-gradient(90deg, var(--lagoon), #7ed3bf);\n transition: transform 170ms ease;\n}\n\n.nav-link:hover,\n.nav-link.is-active {\n color: var(--sea-ink);\n}\n\n.nav-link:hover::after,\n.nav-link.is-active::after {\n transform: scaleX(1);\n}\n\n.rise-in {\n animation: rise-in 700ms cubic-bezier(0.16, 1, 0.3, 1) both;\n}\n\n@keyframes rise-in {\n from {\n opacity: 0;\n transform: translateY(12px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n") } __append("\n") return __output } function __render_7ebb7969_3035(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } const showDevtools = !blank || integrations.some((integration) => integration.type === 'devtools') if (routerOnly) { __append("\nimport { Outlet, createRootRoute } from '@tanstack/solid-router'\n") if (showDevtools) { __append("\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n") } __append("\n\nimport '../styles.css'\n\nexport const Route = createRootRoute({\n component: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n <>\n \n ") if (showDevtools) { __append("") } __append("\n \n )\n}\n") } else if (!includeExamples || blank) { __append("\nimport { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/solid-router'\n") if (showDevtools) { __append("\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n") } __append("\n\n") if (addOnEnabled['solid-ui']) { __append("\nimport \"@fontsource/inter/400.css\"\n") } __append("\n\nimport { HydrationScript } from 'solid-js/web'\nimport { Suspense } from 'solid-js'\n\n") for(const addOn of addOns) { for(const init of addOn.main?.initialize || []) { __append("\n ") __append(init) __append("\n") } } __append("\n\nimport styleCss from \"../styles.css?url\";\n\nexport const Route = createRootRouteWithContext()({\n head: () => ({\n links: [{ rel: \"stylesheet\", href: styleCss }],\n }),\n shellComponent: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n \n \n \n \n \n \n \n \n ") if (showDevtools) { __append("") } __append("\n ") for(const integration of integrations.filter(i => i.type === 'layout')) { __append("\n <") __append(__escapeXML(integration.jsName)) __append(" />\n ") } __append("\n \n \n \n \n );\n}\n") } else { __append("\nimport { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/solid-router'\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n\n") if (addOnEnabled['solid-ui']) { __append("\nimport \"@fontsource/inter/400.css\"\n") } __append("\n\nimport { HydrationScript } from 'solid-js/web'\nimport { Suspense } from 'solid-js'\n\nimport Header from '../components/Header'\n") for(const addOn of addOns) { for(const init of addOn.main?.initialize || []) { __append("\n ") __append(init) __append("\n") } } __append("\n\nimport styleCss from \"../styles.css?url\";\n\nexport const Route = createRootRouteWithContext()({\n head: () => ({\n links: [{ rel: \"stylesheet\", href: styleCss }],\n }),\n shellComponent: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n \n \n \n \n \n \n \n
\n \n \n ") for(const integration of integrations.filter(i => i.type === 'layout')) { __append("\n <") __append(__escapeXML(integration.jsName)) __append(" />\n ") } __append("\n \n \n \n \n );\n}\n") } __append("\n") return __output } function __render_82c11ef7_9033(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { getCustomerEnv } from '#/server/shopify/env'\n\n/**\n * OAuth 2.1 Authorization Code + PKCE flow for the Shopify Customer Account\n * API. We're a public client (browser + Start backend), so no client secret —\n * security comes from the PKCE verifier proof.\n *\n * No usable npm client exists for Customer Account API as of 2026-05; this is\n * a hand-rolled implementation using Web APIs only (portable across Node,\n * Workers, Vercel, Netlify, Bun, Deno).\n */\n\nconst SCOPE = 'openid email customer-account-api:full'\n\ntype DiscoveryDocument = {\n authorizationEndpoint: string\n tokenEndpoint: string\n endSessionEndpoint: string\n graphqlEndpoint: string\n}\n\nlet discoveryCache: DiscoveryDocument | null = null\n\nasync function fetchDiscovery(): Promise {\n if (discoveryCache) return discoveryCache\n const env = getCustomerEnv()\n const base = `https://shopify.com/${env.SHOPIFY_CUSTOMER_ACCOUNT_SHOP_ID}`\n const [oidcRes, customerRes] = await Promise.all([\n fetch(`${base}/.well-known/openid-configuration`),\n fetch(`${base}/.well-known/customer-account-api`),\n ])\n if (!oidcRes.ok || !customerRes.ok) {\n throw new Error(\n `Customer Account discovery failed (${oidcRes.status} / ${customerRes.status}). Verify SHOPIFY_CUSTOMER_ACCOUNT_SHOP_ID.`,\n )\n }\n const oidc = (await oidcRes.json()) as {\n authorization_endpoint: string\n token_endpoint: string\n end_session_endpoint: string\n }\n const customer = (await customerRes.json()) as {\n graphql_api: { endpoint: string }\n }\n discoveryCache = {\n authorizationEndpoint: oidc.authorization_endpoint,\n tokenEndpoint: oidc.token_endpoint,\n endSessionEndpoint: oidc.end_session_endpoint,\n graphqlEndpoint: customer.graphql_api.endpoint,\n }\n return discoveryCache\n}\n\nexport async function getDiscovery() {\n return fetchDiscovery()\n}\n\n/* ─── Base64url + crypto helpers (Web Crypto only) ──────────────────────── */\n\nfunction bufferToBase64Url(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n let str = ''\n for (let i = 0; i < bytes.byteLength; i++) {\n str += String.fromCharCode(bytes[i]!)\n }\n return btoa(str).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')\n}\n\nfunction base64UrlEncode(input: string): string {\n return bufferToBase64Url(new TextEncoder().encode(input).buffer as ArrayBuffer)\n}\n\nfunction base64UrlDecode(input: string): string {\n const padded = input.replace(/-/g, '+').replace(/_/g, '/')\n const padding = (4 - (padded.length % 4)) % 4\n return atob(padded + '='.repeat(padding))\n}\n\nfunction randomBytes(n: number): Uint8Array {\n const arr = new Uint8Array(n)\n crypto.getRandomValues(arr)\n return arr\n}\n\nfunction randomString(byteLength: number): string {\n return bufferToBase64Url(randomBytes(byteLength).buffer as ArrayBuffer)\n}\n\nasync function sha256(input: string): Promise {\n return crypto.subtle.digest('SHA-256', new TextEncoder().encode(input))\n}\n\n/* ─── PKCE ──────────────────────────────────────────────────────────────── */\n\nexport type PkcePair = { codeVerifier: string; codeChallenge: string }\n\nexport async function createPkcePair(): Promise {\n const codeVerifier = randomString(48)\n const codeChallenge = bufferToBase64Url(await sha256(codeVerifier))\n return { codeVerifier, codeChallenge }\n}\n\nexport function createState(): string {\n return randomString(24)\n}\n\n/* ─── Authorize / token / logout URLs ───────────────────────────────────── */\n\nexport async function buildAuthorizationUrl(input: {\n state: string\n codeChallenge: string\n}): Promise {\n const env = getCustomerEnv()\n const discovery = await fetchDiscovery()\n const url = new URL(discovery.authorizationEndpoint)\n url.searchParams.set('client_id', env.SHOPIFY_CUSTOMER_ACCOUNT_CLIENT_ID)\n url.searchParams.set('scope', SCOPE)\n url.searchParams.set('response_type', 'code')\n url.searchParams.set('redirect_uri', env.SHOPIFY_CUSTOMER_ACCOUNT_REDIRECT_URI)\n url.searchParams.set('state', input.state)\n url.searchParams.set('code_challenge', input.codeChallenge)\n url.searchParams.set('code_challenge_method', 'S256')\n return url.toString()\n}\n\ntype TokenResponse = {\n access_token: string\n refresh_token: string\n id_token: string\n expires_in: number\n token_type: 'Bearer'\n}\n\nexport async function exchangeCodeForTokens(input: {\n code: string\n codeVerifier: string\n}): Promise {\n const env = getCustomerEnv()\n const discovery = await fetchDiscovery()\n const body = new URLSearchParams({\n grant_type: 'authorization_code',\n client_id: env.SHOPIFY_CUSTOMER_ACCOUNT_CLIENT_ID,\n code: input.code,\n redirect_uri: env.SHOPIFY_CUSTOMER_ACCOUNT_REDIRECT_URI,\n code_verifier: input.codeVerifier,\n })\n const res = await fetch(discovery.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Origin: new URL(env.SHOPIFY_CUSTOMER_ACCOUNT_REDIRECT_URI).origin,\n },\n body,\n })\n if (!res.ok) {\n throw new Error(\n `Token exchange failed: ${res.status} ${res.statusText} ${await res.text()}`,\n )\n }\n return (await res.json()) as TokenResponse\n}\n\nexport async function refreshTokens(refreshToken: string): Promise {\n const env = getCustomerEnv()\n const discovery = await fetchDiscovery()\n const body = new URLSearchParams({\n grant_type: 'refresh_token',\n client_id: env.SHOPIFY_CUSTOMER_ACCOUNT_CLIENT_ID,\n refresh_token: refreshToken,\n })\n const res = await fetch(discovery.tokenEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n Origin: new URL(env.SHOPIFY_CUSTOMER_ACCOUNT_REDIRECT_URI).origin,\n },\n body,\n })\n if (!res.ok) {\n throw new Error(`Token refresh failed: ${res.status} ${res.statusText}`)\n }\n return (await res.json()) as TokenResponse\n}\n\nexport async function buildEndSessionUrl(idToken: string): Promise {\n const env = getCustomerEnv()\n const discovery = await fetchDiscovery()\n const url = new URL(discovery.endSessionEndpoint)\n url.searchParams.set('id_token_hint', idToken)\n url.searchParams.set(\n 'post_logout_redirect_uri',\n new URL('/', env.SHOPIFY_CUSTOMER_ACCOUNT_REDIRECT_URI).toString(),\n )\n return url.toString()\n}\n\n/* ─── Signed session cookie helpers (HS256) ─────────────────────────────── */\n\nlet cachedHmacKey: CryptoKey | null = null\n\nasync function getHmacKey(): Promise {\n if (cachedHmacKey) return cachedHmacKey\n const env = getCustomerEnv()\n cachedHmacKey = await crypto.subtle.importKey(\n 'raw',\n new TextEncoder().encode(env.SHOPIFY_SESSION_SECRET),\n { name: 'HMAC', hash: 'SHA-256' },\n false,\n ['sign', 'verify'],\n )\n return cachedHmacKey\n}\n\nexport type CustomerSession = {\n accessToken: string\n refreshToken: string\n idToken: string\n expiresAt: number\n}\n\nexport async function signSession(session: CustomerSession): Promise {\n const key = await getHmacKey()\n const payload = base64UrlEncode(JSON.stringify(session))\n const sig = await crypto.subtle.sign(\n 'HMAC',\n key,\n new TextEncoder().encode(payload),\n )\n return `${payload}.${bufferToBase64Url(sig)}`\n}\n\nexport async function verifySession(\n signed: string,\n): Promise {\n const [payload, sig] = signed.split('.')\n if (!payload || !sig) return null\n const key = await getHmacKey()\n const sigBytes = Uint8Array.from(base64UrlDecode(sig), (c) => c.charCodeAt(0))\n const ok = await crypto.subtle.verify(\n 'HMAC',\n key,\n sigBytes,\n new TextEncoder().encode(payload),\n )\n if (!ok) return null\n try {\n return JSON.parse(base64UrlDecode(payload)) as CustomerSession\n } catch {\n return null\n }\n}\n\nexport function buildSession(token: TokenResponse): CustomerSession {\n return {\n accessToken: token.access_token,\n refreshToken: token.refresh_token,\n idToken: token.id_token,\n expiresAt: Date.now() + token.expires_in * 1000,\n }\n}\n\nexport type OAuthFlightState = {\n state: string\n codeVerifier: string\n redirectAfter: string\n}\n\nexport async function signFlightState(\n state: OAuthFlightState,\n): Promise {\n const key = await getHmacKey()\n const payload = base64UrlEncode(JSON.stringify(state))\n const sig = await crypto.subtle.sign(\n 'HMAC',\n key,\n new TextEncoder().encode(payload),\n )\n return `${payload}.${bufferToBase64Url(sig)}`\n}\n\nexport async function verifyFlightState(\n signed: string,\n): Promise {\n const [payload, sig] = signed.split('.')\n if (!payload || !sig) return null\n const key = await getHmacKey()\n const sigBytes = Uint8Array.from(base64UrlDecode(sig), (c) => c.charCodeAt(0))\n const ok = await crypto.subtle.verify(\n 'HMAC',\n key,\n sigBytes,\n new TextEncoder().encode(payload),\n )\n if (!ok) return null\n try {\n return JSON.parse(base64UrlDecode(payload)) as OAuthFlightState\n } catch {\n return null\n }\n}\n") return __output } function __render_82de873d_4164(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (blank) { __append("\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({ component: Home })\n\nfunction Home() {\n return (\n
\n

Welcome to TanStack Start

\n

\n Edit src/routes/index.tsx to get started.\n

\n
\n )\n}\n") } else if (!includeExamples) { __append("\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({ component: Home })\n\nfunction Home() {\n return (\n
\n

Welcome to TanStack Start

\n

\n Edit src/routes/index.tsx to get started.\n

\n
\n )\n}\n") } else { __append("\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({ component: App })\n\nfunction App() {\n return (\n
\n
\n
\n
\n

TanStack Start Base Template

\n

\n Start simple, ship quickly.\n

\n

\n This base starter intentionally keeps things light: two routes, clean structure,\n and the essentials you need to build from scratch.\n

\n
\n \n About This Starter\n \n \n Router Guide\n \n
\n
\n\n
\n {[\n ['Type-Safe Routing', 'Routes and links stay in sync across every page.'],\n ['Server Functions', 'Call server code from your UI without creating API boilerplate.'],\n ['Streaming by Default', 'Ship progressively rendered responses for faster experiences.'],\n ['Tailwind Native', 'Design quickly with utility-first styling and reusable tokens.'],\n ].map(([title, desc], index) => (\n \n

{title}

\n

{desc}

\n \n ))}\n
\n\n
\n

Quick Start

\n
    \n
  • Edit src/routes/index.tsx to customize the home page.
  • \n
  • \n Update src/components/Header.tsx for navigation and product links.\n
  • \n
  • \n Add routes in src/routes and tweak visual tokens in src/styles.css.\n
  • \n
\n
\n
\n )\n}\n") } __append("\n") return __output } function __render_8a237555_262(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (packageManager !== 'pnpm') { ignoreFile(); return; } __append("allowBuilds:\n esbuild: true\n lightningcss: true\n") if (addOnEnabled.cloudflare || addOnEnabled.netlify) { __append("sharp: true\n") } if (addOnEnabled.cloudflare) { __append("workerd: true\n") } return __output } function __render_8c7cf38c_960(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"dependencies\": {\n \"drizzle-orm\": \"^0.45.1\",\n \"drizzle-kit\": \"^0.31.9\"") if (addOnOption.drizzle.database === 'postgresql') { __append(",\n \"pg\": \"^8.16.3\"") } if (addOnOption.drizzle.database === 'mysql') { __append(",\n \"mysql2\": \"^3.15.3\"") } if (addOnOption.drizzle.database === 'sqlite') { __append(",\n \"better-sqlite3\": \"^12.6.2\"") } __append("\n },\n \"devDependencies\": {\n \"dotenv\": \"^17.3.1\",\n \"tsx\": \"^4.21.0\"") if (addOnOption.drizzle.database === 'postgresql') { __append(",\n \"@types/pg\": \"^8.15.6\"") } if (addOnOption.drizzle.database === 'sqlite') { __append(",\n \"@types/better-sqlite3\": \"^7.6.0\"") } __append("\n },\n \"scripts\": {\n \"db:generate\": \"drizzle-kit generate\",\n \"db:migrate\": \"drizzle-kit migrate\",\n \"db:push\": \"drizzle-kit push\",\n \"db:pull\": \"drizzle-kit pull\",\n \"db:studio\": \"drizzle-kit studio\"\n }") if (addOnOption.drizzle.database === 'sqlite') { __append(",\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\"better-sqlite3\"]\n }\n ") } __append("\n}\n") return __output } function __render_900686bd_340(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("generator client {\n provider = \"prisma-client\"\n output = \"../src/generated/prisma\"\n}\n\ndatasource db {\n provider = \"") __append(__escapeXML(addOnOption.prisma.database === "postgres" ? "postgresql" : addOnOption.prisma.database)) __append("\"\n}\n\nmodel Todo {\n id Int @id @default(autoincrement())\n title String\n createdAt DateTime @default(now())\n}") return __output } function __render_9812bfc2_253(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"scripts\": {\n \"deploy\": \"") __append(__escapeXML(packageManager)) __append(" run build && wrangler deploy\"\n },\n \"devDependencies\": {\n \"@cloudflare/vite-plugin\": \"^1.26.0\",\n \"wrangler\": \"^4.70.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\"sharp\", \"workerd\"]\n }\n}\n") return __output } function __render_9900fb69_731(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (routerOnly || (!addOnEnabled.clerk && !addOnEnabled.workos)) { ignoreFile(); return; } __append("\nimport { createCsrfMiddleware, createStart } from '@tanstack/react-start'\n") if (addOnEnabled.clerk) { __append("\nimport { clerkMiddleware } from '@clerk/tanstack-react-start/server'\n") } __append("\n") if (addOnEnabled.workos) { __append("\nimport { authkitMiddleware } from '@workos/authkit-tanstack-react-start'\n") } __append("\n\nconst csrfMiddleware = createCsrfMiddleware({\n filter: (context) => context.handlerType === 'serverFn',\n})\n\nexport const startInstance = createStart(() => ({\n requestMiddleware: [\n csrfMiddleware,\n ") if (addOnEnabled.clerk) { __append("clerkMiddleware(),") } __append("\n ") if (addOnEnabled.workos) { __append("authkitMiddleware(),") } __append("\n ],\n}))\n") return __output } function __render_994aa035_1674(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { ") if (fileRouter) { __append("createFileRoute") } else { __append("createRoute") } __append(" } from '@tanstack/solid-router'\nimport { useStore } from '@tanstack/solid-store'\n\nimport { fullName, store } from '../lib/demo-store'\n\n") if (codeRouter) { __append("\nimport type { RootRoute } from '@tanstack/solid-router'\n") } else { __append("\nexport const Route = createFileRoute('/demo/store')({\n component: DemoStore,\n})\n") } __append("\n\nfunction FirstName() {\n const firstName = useStore(store, (state) => state.firstName)\n return (\n \n store.setState((state) => ({ ...state, firstName: e.target.value }))\n }\n class=\"demo-input\"\n />\n )\n}\n\nfunction LastName() {\n const lastName = useStore(store, (state) => state.lastName)\n return (\n \n store.setState((state) => ({ ...state, lastName: e.target.value }))\n }\n class=\"demo-input\"\n />\n )\n}\n\nfunction FullName() {\n const fName = useStore(fullName)\n return (\n
\n {fName()}\n
\n )\n}\n\nfunction DemoStore() {\n return (\n
\n
\n

TanStack Store

\n

Store Example

\n \n \n \n
\n
\n )\n}\n\n") if (codeRouter) { __append("\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/store',\n component: DemoStore,\n getParentRoute: () => parentRoute,\n})\n") } __append("\n \n") return __output } function __render_9bd461a9_1368(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { Link } from '@tanstack/react-router'\n\nconst ITEMS = [\n { to: '/shop/account', label: 'Overview', exact: true },\n { to: '/shop/account/orders', label: 'Orders', exact: false },\n { to: '/shop/account/addresses', label: 'Addresses', exact: false },\n] as const\n\nexport function AccountNav() {\n return (\n \n )\n}\n") return __output } function __render_9e9ea353_307(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("## Setting up Convex\n\n- Set the `VITE_CONVEX_URL` and `CONVEX_DEPLOYMENT` environment variables in your `.env.local`. (Or run `") __append(getPackageManagerExecuteScript('convex', ['init'])) __append("` to set them automatically.)\n- Run `") __append(getPackageManagerExecuteScript('convex', ['dev'])) __append("` to start the Convex server.\n") return __output } function __render_9ead2ab8_1870(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { Link, createFileRoute } from '@tanstack/react-router'\n\nimport { Money } from '#/components/shop/money'\nimport { getOrders } from '#/server/shopify/customer.functions'\n\nexport const Route = createFileRoute('/shop/account/orders/')({\n loader: () => getOrders({ data: { first: 50 } }),\n component: OrdersRoute,\n})\n\nfunction OrdersRoute() {\n const orders = Route.useLoaderData()\n if (orders.nodes.length === 0) {\n return (\n
\n

No orders yet.

\n
\n )\n }\n return (\n
\n

Orders

\n
    \n {orders.nodes.map((order) => (\n
  • \n
    \n \n {order.name}\n \n

    \n {new Date(order.processedAt).toLocaleDateString()} ·{' '}\n {order.fulfillmentStatus ?? 'pending'} ·{' '}\n {order.financialStatus ?? '—'}\n

    \n
    \n \n
  • \n ))}\n
\n
\n )\n}\n") return __output } function __render_a1f5176f_1766(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { authClient } from \"#/lib/auth-client\";") if (routes.some(r => r.url === '/demo/better-auth')) { __append("import { Link } from \"@tanstack/react-router\";") } __append("export default function BetterAuthHeader() {\n const { data: session, isPending } = authClient.useSession();\n\n if (isPending) {\n return (\n
\n );\n }\n\n if (session?.user) {\n return (\n
\n {session.user.image ? (\n \"\"\n ) : (\n
\n \n {session.user.name?.charAt(0).toUpperCase() || \"U\"}\n \n
\n )}\n {\n void authClient.signOut()\n }}\n className=\"flex-1 h-9 px-4 text-sm font-medium bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-50 border border-neutral-300 dark:border-neutral-700 hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors\"\n >\n Sign out\n \n
\n );\n }") if (routes.some(r => r.url === '/demo/better-auth')) { __append("return (\n \n Sign in\n \n );") } else { __append("return null;") } __append("}\n") return __output } function __render_a44a9c73_635(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { createRouter as createTanStackRouter } from '@tanstack/solid-router'\nimport { routeTree } from './routeTree.gen'\n") if (addOnEnabled['tanstack-query']) { __append("\nimport { getContext } from './integrations/tanstack-query/provider'\n") } __append("\n\nexport function getRouter() {\n const router = createTanStackRouter({\n routeTree,\n") if (addOnEnabled['tanstack-query']) { __append("\n context: getContext(),\n") } __append("\n scrollRestoration: true,\n defaultPreload: 'intent',\n defaultPreloadStaleTime: 0,\n })\n\n return router\n}\n\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: ReturnType\n }\n}\n") return __output } function __render_aa240753_2616(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!includeExamples) { ignoreFile(); return; } __append("\nimport { Link } from '@tanstack/solid-router'\n") for(const integration of integrations.filter(i => i.type === 'header-user')) { __append("\nimport ") __append(__escapeXML(integration.jsName)) __append(" from '") __append(__escapeXML(relativePath(integration.path))) __append("'\n") } __append("\n\n") const demoRoutes = []; for (const addOn of addOns) { for (const route of (addOn?.routes || []).filter((r) => r.url && r.name)) { demoRoutes.push({ url: route.url, name: route.name }); for (const child of route.children || []) { if (child?.url && child?.name) { demoRoutes.push({ url: child.url, name: child.name }); } } } } const userHeaders = integrations.filter((i) => i.type === 'header-user'); __append("\n\nexport default function Header() {\n return (\n
\n \n
\n )\n}\n") return __output } function __render_abf34a43_2309(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!includeExamples) { ignoreFile(); return; } __append("\nimport { useEffect, useState } from 'react'\n\ntype ThemeMode = 'light' | 'dark' | 'auto'\n\nfunction getInitialMode(): ThemeMode {\n if (typeof window === 'undefined') {\n return 'auto'\n }\n\n const stored = window.localStorage.getItem('theme')\n if (stored === 'light' || stored === 'dark' || stored === 'auto') {\n return stored\n }\n\n return 'auto'\n}\n\nfunction applyThemeMode(mode: ThemeMode) {\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches\n const resolved = mode === 'auto' ? (prefersDark ? 'dark' : 'light') : mode\n\n document.documentElement.classList.remove('light', 'dark')\n document.documentElement.classList.add(resolved)\n\n if (mode === 'auto') {\n document.documentElement.removeAttribute('data-theme')\n } else {\n document.documentElement.setAttribute('data-theme', mode)\n }\n\n document.documentElement.style.colorScheme = resolved\n}\n\nexport default function ThemeToggle() {\n const [mode, setMode] = useState('auto')\n\n useEffect(() => {\n const initialMode = getInitialMode()\n setMode(initialMode)\n applyThemeMode(initialMode)\n }, [])\n\n useEffect(() => {\n if (mode !== 'auto') {\n return\n }\n\n const media = window.matchMedia('(prefers-color-scheme: dark)')\n const onChange = () => applyThemeMode('auto')\n\n media.addEventListener('change', onChange)\n return () => {\n media.removeEventListener('change', onChange)\n }\n }, [mode])\n\n function toggleMode() {\n const nextMode: ThemeMode =\n mode === 'light' ? 'dark' : mode === 'dark' ? 'auto' : 'light'\n setMode(nextMode)\n applyThemeMode(nextMode)\n window.localStorage.setItem('theme', nextMode)\n }\n\n const label =\n mode === 'auto'\n ? 'Theme mode: auto (system). Click to switch to light mode.'\n : `Theme mode: ${mode}. Click to switch mode.`\n\n return (\n \n {mode === 'auto' ? 'Auto' : mode === 'dark' ? 'Dark' : 'Light'}\n \n )\n}\n") return __output } function __render_af6293e3_1343(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { PrismaClient } from \"../src/generated/prisma/client.js\";\n") if (addOnOption.prisma.database !== 'sqlite') { __append("\nimport { getDatabaseUrl } from '../src/database-url.js'\n") } __append("\n\n") if (addOnOption.prisma.database === 'postgres') { __append("\nimport { PrismaPg } from '@prisma/adapter-pg';\n\nconst adapter = new PrismaPg({\n connectionString: getDatabaseUrl(),\n});") } __append("\n\n") if (addOnOption.prisma.database === 'mysql') { __append("\nimport { PrismaMariaDb } from '@prisma/adapter-mariadb';\nconst adapter = new PrismaMariaDb(getDatabaseUrl());") } __append("\n\n") if (addOnOption.prisma.database === 'sqlite') { __append("\nimport { PrismaBetterSqlite3 } from '@prisma/adapter-better-sqlite3';\nconst adapter = new PrismaBetterSqlite3({\n url: process.env.DATABASE_URL || 'file:./dev.db'\n});") } __append("\n\nconst prisma = new PrismaClient({adapter})\n\nasync function main() {\n console.log('🌱 Seeding database...')\n\n // Clear existing todos\n await prisma.todo.deleteMany()\n\n // Create example todos\n const todos = await prisma.todo.createMany({\n data: [\n { title: 'Buy groceries' },\n { title: 'Read a book' },\n { title: 'Workout' },\n ],\n })\n\n console.log(`✅ Created ${todos.count} todos`)\n}\n\nmain()\n .catch((e) => {\n console.error('❌ Error seeding database:', e)\n process.exit(1)\n })\n .finally(async () => {\n await prisma.$disconnect()\n })\n") return __output } function __render_b1a58b51_856(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (!includeExamples) { ignoreFile(); return; } __append("\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/about')({\n component: About,\n})\n\nfunction About() {\n return (\n
\n
\n

About

\n

\n A small starter with room to grow.\n

\n

\n TanStack Start gives you type-safe routing, server functions, and modern SSR defaults.\n Use this as a clean foundation, then layer in your own routes, styling, and add-ons.\n

\n
\n
\n )\n}\n") return __output } function __render_b5df988d_418(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.prisma.database === 'postgres') { __append("\n# Database URL for PostgreSQL\nDATABASE_URL=\"postgresql://username:password@localhost:5432/mydb\"") } else if (addOnOption.prisma.database === 'mysql') { __append("\n# Database URL for MySQL\nDATABASE_URL=\"mysql://username:password@localhost:3306/mydb\"") } else if (addOnOption.prisma.database === 'sqlite') { __append("\n# Database URL for SQLite\nDATABASE_URL=\"file:./dev.db\"") } return __output } function __render_b7239c88_690(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { useQuery } from '@tanstack/react-query'\n\nimport { getCustomer } from '#/server/shopify/customer.functions'\nimport type { CustomerProfile } from '#/lib/shopify/customer-queries'\n\nexport const CUSTOMER_QUERY_KEY = ['shopify', 'customer'] as const\n\nexport function useCustomer() {\n const query = useQuery({\n queryKey: CUSTOMER_QUERY_KEY,\n queryFn: () => getCustomer(),\n staleTime: 60_000,\n })\n return {\n customer: query.data ?? null,\n isLoading: query.isLoading,\n isFetching: query.isFetching,\n isError: query.isError,\n error: query.error,\n }\n}\n") return __output } function __render_c759666f_5091(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { createServerFn } from '@tanstack/react-start'\nimport { setResponseHeaders } from '@tanstack/react-start/server'\nimport * as v from 'valibot'\n\nimport {\n customerFetch,\n getCustomerSession,\n requireCustomerSession,\n} from '#/server/shopify/customer-client'\nimport {\n clearFlightCookie,\n clearSessionCookie,\n getFlightCookie,\n setFlightCookie,\n setSessionCookie,\n} from '#/server/shopify/customer-cookies'\nimport {\n buildAuthorizationUrl,\n buildEndSessionUrl,\n buildSession,\n createPkcePair,\n createState,\n exchangeCodeForTokens,\n signFlightState,\n signSession,\n verifyFlightState,\n} from '#/server/shopify/oauth'\nimport {\n CUSTOMER_ADDRESSES_QUERY,\n CUSTOMER_QUERY,\n ORDER_QUERY,\n ORDERS_QUERY,\n type CustomerAddress,\n type CustomerAddressesQueryResult,\n type CustomerProfile,\n type CustomerQueryResult,\n type OrderDetail,\n type OrderListItem,\n type OrderQueryResult,\n type OrdersQueryResult,\n} from '#/lib/shopify/customer-queries'\n\nfunction setPrivateHeaders() {\n setResponseHeaders(\n new Headers({ 'Cache-Control': 'private, no-store, must-revalidate' }),\n )\n}\n\nexport const startLogin = createServerFn({ method: 'POST' })\n .inputValidator(\n v.object({\n redirectAfter: v.optional(v.string(), '/shop/account'),\n }),\n )\n .handler(async ({ data }): Promise<{ authorizationUrl: string }> => {\n setPrivateHeaders()\n const { codeVerifier, codeChallenge } = await createPkcePair()\n const state = createState()\n const flight = await signFlightState({\n state,\n codeVerifier,\n redirectAfter: data.redirectAfter,\n })\n setFlightCookie(flight)\n const authorizationUrl = await buildAuthorizationUrl({ state, codeChallenge })\n return { authorizationUrl }\n })\n\nexport const completeLogin = createServerFn({ method: 'POST' })\n .inputValidator(v.object({ code: v.string(), state: v.string() }))\n .handler(async ({ data }): Promise<{ redirectAfter: string }> => {\n setPrivateHeaders()\n const flightCookie = getFlightCookie()\n if (!flightCookie) throw new Error('OAuth flight cookie missing or expired.')\n const flight = await verifyFlightState(flightCookie)\n if (!flight) throw new Error('OAuth flight cookie failed verification.')\n if (flight.state !== data.state) throw new Error('OAuth state mismatch.')\n\n const tokens = await exchangeCodeForTokens({\n code: data.code,\n codeVerifier: flight.codeVerifier,\n })\n const session = buildSession(tokens)\n setSessionCookie(await signSession(session))\n clearFlightCookie()\n return { redirectAfter: flight.redirectAfter }\n })\n\nexport const logout = createServerFn({ method: 'POST' }).handler(\n async (): Promise<{ endSessionUrl: string | null }> => {\n setPrivateHeaders()\n const session = await getCustomerSession()\n clearSessionCookie()\n if (!session) return { endSessionUrl: null }\n return { endSessionUrl: await buildEndSessionUrl(session.idToken) }\n },\n)\n\nexport const getCustomer = createServerFn({ method: 'GET' }).handler(\n async (): Promise => {\n setPrivateHeaders()\n const session = await getCustomerSession()\n if (!session) return null\n const result = await customerFetch({\n query: CUSTOMER_QUERY,\n session,\n })\n return result.customer\n },\n)\n\nexport const getOrders = createServerFn({ method: 'POST' })\n .inputValidator(\n v.object({\n first: v.optional(v.pipe(v.number(), v.integer(), v.minValue(1)), 20),\n after: v.optional(v.nullable(v.string())),\n }),\n )\n .handler(\n async ({\n data,\n }): Promise<{\n nodes: Array\n pageInfo: { hasNextPage: boolean; endCursor: string | null }\n }> => {\n setPrivateHeaders()\n const session = await requireCustomerSession()\n const result = await customerFetch({\n query: ORDERS_QUERY,\n variables: { first: data.first, after: data.after ?? null },\n session,\n })\n return result.customer.orders\n },\n )\n\nexport const getOrder = createServerFn({ method: 'POST' })\n .inputValidator(v.object({ id: v.string() }))\n .handler(async ({ data }): Promise => {\n setPrivateHeaders()\n const session = await requireCustomerSession()\n const result = await customerFetch({\n query: ORDER_QUERY,\n variables: { id: data.id },\n session,\n })\n return result.order\n })\n\nexport const getAddresses = createServerFn({ method: 'GET' }).handler(\n async (): Promise<{\n addresses: Array\n defaultAddressId: string | null\n }> => {\n setPrivateHeaders()\n const session = await requireCustomerSession()\n const result = await customerFetch({\n query: CUSTOMER_ADDRESSES_QUERY,\n variables: { first: 50 },\n session,\n })\n return {\n addresses: result.customer.addresses.nodes,\n defaultAddressId: result.customer.defaultAddress?.id ?? null,\n }\n },\n)\n") return __output } function __render_c7ddd624_10891(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (blank) { __append("\n") if (tailwind) { __append("@import \"tailwindcss\";\n\n") } __append("\n* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n font-family: system-ui, sans-serif;\n}\n\nmain {\n width: min(42rem, calc(100% - 2rem));\n margin: 4rem auto;\n}\n") } else if (!includeExamples) { __append("\n@import \"tailwindcss\";\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody,\n#app {\n min-height: 100%;\n}\n\nbody {\n margin: 0;\n}\n") } else { __append("\n@import url(\"https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap\");\n@import \"tailwindcss\";\n@plugin \"@tailwindcss/typography\";\n\n@theme {\n --font-sans: \"Manrope\", ui-sans-serif, system-ui, sans-serif;\n}\n\n:root {\n --sea-ink: #173a40;\n --sea-ink-soft: #416166;\n --lagoon: #4fb8b2;\n --lagoon-deep: #328f97;\n --palm: #2f6a4a;\n --sand: #e7f0e8;\n --foam: #f3faf5;\n --surface: rgba(255, 255, 255, 0.74);\n --surface-strong: rgba(255, 255, 255, 0.9);\n --line: rgba(23, 58, 64, 0.14);\n --inset-glint: rgba(255, 255, 255, 0.82);\n --kicker: rgba(47, 106, 74, 0.9);\n --bg-base: #e7f3ec;\n --header-bg: rgba(251, 255, 248, 0.84);\n --chip-bg: rgba(255, 255, 255, 0.8);\n --chip-line: rgba(47, 106, 74, 0.18);\n --link-bg-hover: rgba(255, 255, 255, 0.9);\n --hero-a: rgba(79, 184, 178, 0.36);\n --hero-b: rgba(47, 106, 74, 0.2);\n}\n\n:root[data-theme=\"dark\"] {\n --sea-ink: #d7ece8;\n --sea-ink-soft: #afcdc8;\n --lagoon: #60d7cf;\n --lagoon-deep: #8de5db;\n --palm: #6ec89a;\n --sand: #0f1a1e;\n --foam: #101d22;\n --surface: rgba(16, 30, 34, 0.8);\n --surface-strong: rgba(15, 27, 31, 0.92);\n --line: rgba(141, 229, 219, 0.18);\n --inset-glint: rgba(194, 247, 238, 0.14);\n --kicker: #b8efe5;\n --bg-base: #0a1418;\n --header-bg: rgba(10, 20, 24, 0.8);\n --chip-bg: rgba(13, 28, 32, 0.9);\n --chip-line: rgba(141, 229, 219, 0.24);\n --link-bg-hover: rgba(24, 44, 49, 0.8);\n --hero-a: rgba(96, 215, 207, 0.18);\n --hero-b: rgba(110, 200, 154, 0.12);\n}\n\n@media (prefers-color-scheme: dark) {\n :root:not([data-theme=\"light\"]) {\n --sea-ink: #d7ece8;\n --sea-ink-soft: #afcdc8;\n --lagoon: #60d7cf;\n --lagoon-deep: #8de5db;\n --palm: #6ec89a;\n --sand: #0f1a1e;\n --foam: #101d22;\n --surface: rgba(16, 30, 34, 0.8);\n --surface-strong: rgba(15, 27, 31, 0.92);\n --line: rgba(141, 229, 219, 0.18);\n --inset-glint: rgba(194, 247, 238, 0.14);\n --kicker: #b8efe5;\n --bg-base: #0a1418;\n --header-bg: rgba(10, 20, 24, 0.8);\n --chip-bg: rgba(13, 28, 32, 0.9);\n --chip-line: rgba(141, 229, 219, 0.24);\n --link-bg-hover: rgba(24, 44, 49, 0.8);\n --hero-a: rgba(96, 215, 207, 0.18);\n --hero-b: rgba(110, 200, 154, 0.12);\n }\n}\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody,\n#app {\n min-height: 100%;\n}\n\nbody {\n margin: 0;\n color: var(--sea-ink);\n font-family: var(--font-sans);\n background-color: var(--bg-base);\n background:\n radial-gradient(1100px 620px at -8% -10%, var(--hero-a), transparent 58%),\n radial-gradient(1050px 620px at 112% -12%, var(--hero-b), transparent 62%),\n radial-gradient(720px 380px at 50% 115%, rgba(79, 184, 178, 0.1), transparent 68%),\n linear-gradient(180deg, color-mix(in oklab, var(--sand) 68%, white) 0%, var(--foam) 44%, var(--bg-base) 100%);\n overflow-x: hidden;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\nbody::before {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: -1;\n opacity: 0.28;\n background:\n radial-gradient(circle at 20% 15%, rgba(255, 255, 255, 0.8), transparent 34%),\n radial-gradient(circle at 78% 26%, rgba(79, 184, 178, 0.2), transparent 42%),\n radial-gradient(circle at 42% 82%, rgba(47, 106, 74, 0.14), transparent 36%);\n}\n\nbody::after {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: -1;\n opacity: 0.14;\n background-image:\n linear-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px),\n linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px);\n background-size: 28px 28px;\n mask-image: radial-gradient(circle at 50% 30%, black, transparent 78%);\n}\n\na {\n color: var(--lagoon-deep);\n text-decoration-color: rgba(50, 143, 151, 0.4);\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n}\n\na:hover {\n color: #246f76;\n}\n\ncode {\n font-size: 0.9em;\n border: 1px solid var(--line);\n background: color-mix(in oklab, var(--surface-strong) 82%, white 18%);\n border-radius: 7px;\n padding: 2px 7px;\n}\n\npre code {\n border: 0;\n background: transparent;\n padding: 0;\n border-radius: 0;\n font-size: inherit;\n color: inherit;\n}\n\n.page-wrap {\n width: min(1080px, calc(100% - 2rem));\n margin-inline: auto;\n}\n\n.display-title {\n font-family: \"Fraunces\", Georgia, serif;\n}\n\n.island-shell {\n border: 1px solid var(--line);\n background: linear-gradient(165deg, var(--surface-strong), var(--surface));\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 22px 44px rgba(30, 90, 72, 0.1),\n 0 6px 18px rgba(23, 58, 64, 0.08);\n backdrop-filter: blur(4px);\n}\n\n.feature-card {\n background: linear-gradient(165deg, color-mix(in oklab, var(--surface-strong) 93%, white 7%), var(--surface));\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 18px 34px rgba(30, 90, 72, 0.1),\n 0 4px 14px rgba(23, 58, 64, 0.06);\n}\n\n.feature-card:hover {\n transform: translateY(-2px);\n border-color: color-mix(in oklab, var(--lagoon-deep) 35%, var(--line));\n}\n\n.demo-page {\n width: min(1080px, calc(100% - 2rem));\n margin-inline: auto;\n padding-block: clamp(2.25rem, 6vw, 4.5rem);\n color: var(--sea-ink);\n}\n\n.demo-page-wide {\n width: min(1280px, calc(100% - 2rem));\n}\n\n.demo-center {\n min-height: calc(100vh - 13rem);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.demo-panel,\n.demo-card {\n border: 1px solid var(--line);\n background: linear-gradient(165deg, var(--surface-strong), var(--surface));\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 18px 34px rgba(30, 90, 72, 0.1),\n 0 4px 14px rgba(23, 58, 64, 0.06);\n backdrop-filter: blur(4px);\n}\n\n.demo-panel {\n border-radius: 1.25rem;\n padding: clamp(1.25rem, 4vw, 2rem);\n}\n\n.demo-card {\n border-radius: 1rem;\n padding: 1rem;\n}\n\n.demo-title {\n margin: 0;\n color: var(--sea-ink);\n font-size: clamp(1.75rem, 4vw, 2.75rem);\n line-height: 1.05;\n font-weight: 800;\n letter-spacing: 0;\n}\n\n.demo-section-title {\n margin: 0;\n color: var(--sea-ink);\n font-size: 1rem;\n font-weight: 700;\n}\n\n.demo-muted {\n color: var(--sea-ink-soft);\n}\n\n.demo-input,\n.demo-select,\n.demo-textarea {\n width: 100%;\n border: 1px solid var(--line);\n border-radius: 0.75rem;\n background: color-mix(in oklab, var(--surface-strong) 88%, white 12%);\n color: var(--sea-ink);\n outline: none;\n transition: border-color 180ms ease, box-shadow 180ms ease, background-color 180ms ease;\n}\n\n.demo-input,\n.demo-select {\n padding: 0.7rem 0.9rem;\n}\n\n.demo-input-fit {\n width: auto;\n}\n\n.demo-textarea {\n min-height: 7rem;\n padding: 0.8rem 0.9rem;\n resize: vertical;\n}\n\n.demo-input:focus,\n.demo-select:focus,\n.demo-textarea:focus {\n border-color: color-mix(in oklab, var(--lagoon-deep) 58%, var(--line));\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--lagoon) 24%, transparent);\n}\n\n.demo-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n border: 1px solid color-mix(in oklab, var(--lagoon-deep) 34%, var(--line));\n border-radius: 0.85rem;\n background: color-mix(in oklab, var(--lagoon) 22%, var(--surface-strong));\n color: var(--sea-ink);\n padding: 0.72rem 1rem;\n font-size: 0.9rem;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n}\n\n.demo-button:hover {\n transform: translateY(-1px);\n background: color-mix(in oklab, var(--lagoon) 30%, var(--surface-strong));\n}\n\n.demo-button:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n transform: none;\n}\n\n.demo-button-secondary {\n border-color: var(--line);\n background: color-mix(in oklab, var(--surface-strong) 74%, transparent);\n color: var(--sea-ink-soft);\n}\n\n.demo-button-danger {\n border-color: rgba(196, 71, 71, 0.28);\n background: rgba(196, 71, 71, 0.1);\n color: #9f3030;\n}\n\n.demo-list-item {\n border: 1px solid var(--line);\n border-radius: 0.9rem;\n background: color-mix(in oklab, var(--chip-bg) 82%, transparent);\n padding: 0.9rem 1rem;\n}\n\n.demo-pill {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n border: 1px solid var(--chip-line);\n border-radius: 999px;\n background: var(--chip-bg);\n color: var(--sea-ink-soft);\n padding: 0.35rem 0.65rem;\n font-size: 0.75rem;\n font-weight: 700;\n}\n\n.demo-alert {\n border: 1px solid rgba(193, 126, 42, 0.3);\n border-radius: 1rem;\n background: rgba(193, 126, 42, 0.1);\n color: var(--sea-ink);\n padding: 1rem;\n}\n\n.demo-alert-danger {\n border-color: rgba(196, 71, 71, 0.3);\n background: rgba(196, 71, 71, 0.1);\n}\n\n.demo-code-block {\n border: 1px solid var(--line);\n border-radius: 1rem;\n background: color-mix(in oklab, var(--chip-bg) 88%, transparent);\n color: var(--sea-ink);\n padding: 1rem;\n}\n\n.demo-table-shell {\n overflow-x: auto;\n border: 1px solid var(--line);\n border-radius: 1rem;\n background: color-mix(in oklab, var(--surface-strong) 78%, transparent);\n}\n\n.demo-table {\n width: 100%;\n border-collapse: collapse;\n color: var(--sea-ink);\n}\n\n.demo-table th,\n.demo-table td {\n border-bottom: 1px solid var(--line);\n padding: 0.75rem 1rem;\n text-align: left;\n}\n\n.demo-table th {\n background: color-mix(in oklab, var(--chip-bg) 88%, transparent);\n color: var(--sea-ink);\n font-weight: 700;\n}\n\n.demo-table tr:hover td {\n background: color-mix(in oklab, var(--lagoon) 8%, transparent);\n}\n\nbutton,\n.island-shell,\na {\n transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease,\n transform 180ms ease;\n}\n\n.island-kicker {\n letter-spacing: 0.16em;\n text-transform: uppercase;\n font-weight: 700;\n font-size: 0.69rem;\n color: var(--kicker);\n}\n\n.nav-link {\n position: relative;\n display: inline-flex;\n align-items: center;\n text-decoration: none;\n color: var(--sea-ink-soft);\n}\n\n.nav-link::after {\n content: \"\";\n position: absolute;\n left: 0;\n bottom: -6px;\n width: 100%;\n height: 2px;\n transform: scaleX(0);\n transform-origin: left;\n background: linear-gradient(90deg, var(--lagoon), #7ed3bf);\n transition: transform 170ms ease;\n}\n\n.nav-link:hover,\n.nav-link.is-active {\n color: var(--sea-ink);\n}\n\n.nav-link:hover::after,\n.nav-link.is-active::after {\n transform: scaleX(1);\n}\n\n@media (max-width: 640px) {\n .nav-link::after {\n bottom: -4px;\n }\n}\n\n.site-footer {\n border-top: 1px solid var(--line);\n background: color-mix(in oklab, var(--header-bg) 84%, transparent 16%);\n}\n\n.rise-in {\n animation: rise-in 700ms cubic-bezier(0.16, 1, 0.3, 1) both;\n}\n\n@keyframes rise-in {\n from {\n opacity: 0;\n transform: translateY(12px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n") } __append("\n") return __output } function __render_c8ba0e71_2085(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { ") if (fileRouter) { __append("createFileRoute") } else { __append("createRoute") } __append(" } from '@tanstack/react-router'\nimport { z } from 'zod'\n\nimport { useAppForm } from '#/hooks/demo.form'\n\n") if (codeRouter) { __append("\n import type { RootRoute } from '@tanstack/react-router'\n") } else { __append("\nexport const Route = createFileRoute('/demo/form')({\n component: SimpleForm,\n})\n") } __append("\n\nconst schema = z.object({\n title: z.string().min(1, 'Title is required'),\n description: z.string().min(1, 'Description is required'),\n})\n\nfunction SimpleForm() {\n const form = useAppForm({\n defaultValues: {\n title: '',\n description: '',\n },\n validators: {\n onBlur: schema,\n },\n onSubmit: ({ value }) => {\n console.log(value)\n // Show success message\n alert('Form submitted successfully!')\n },\n })\n\n return (\n
\n
\n
\n

TanStack Form

\n

Simple Form

\n

\n A small validated form using the generated field helpers.\n

\n
\n {\n e.preventDefault()\n e.stopPropagation()\n form.handleSubmit()\n }}\n className=\"space-y-6\"\n >\n \n {(field) => }\n \n\n \n {(field) => }\n \n\n
\n \n \n \n
\n \n
\n
\n )\n}\n\n") if (codeRouter) { __append("\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/form/simple',\n component: SimpleForm,\n getParentRoute: () => parentRoute,\n})\n") } __append("\n") return __output } function __render_ca835134_2683(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport {\n buildSession,\n getDiscovery,\n refreshTokens,\n signSession,\n verifySession,\n type CustomerSession,\n} from '#/server/shopify/oauth'\nimport {\n clearSessionCookie,\n getSessionCookie,\n setSessionCookie,\n} from '#/server/shopify/customer-cookies'\n\nconst REFRESH_LEEWAY_MS = 60_000 // refresh if expiring within 60s\n\nclass CustomerAuthError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'CustomerAuthError'\n }\n}\n\n/**\n * Read the current customer session, refreshing tokens if they're close to\n * expiry. Returns null when the user is not signed in.\n */\nexport async function getCustomerSession(): Promise {\n const cookie = getSessionCookie()\n if (!cookie) return null\n\n const session = await verifySession(cookie)\n if (!session) {\n clearSessionCookie()\n return null\n }\n\n if (session.expiresAt - REFRESH_LEEWAY_MS > Date.now()) {\n return session\n }\n\n // Token expired or near-expiry — refresh.\n try {\n const refreshed = await refreshTokens(session.refreshToken)\n const next = buildSession(refreshed)\n setSessionCookie(await signSession(next))\n return next\n } catch {\n clearSessionCookie()\n return null\n }\n}\n\nexport async function requireCustomerSession(): Promise {\n const session = await getCustomerSession()\n if (!session) throw new CustomerAuthError('Not signed in')\n return session\n}\n\ntype CustomerFetchInput = {\n query: string\n variables?: TVariables\n session: CustomerSession\n}\n\ntype CustomerResponse = {\n data?: TData\n errors?: Array<{ message: string }>\n}\n\n/**\n * Authenticated GraphQL fetch against the Customer Account API. Caller must\n * pass a verified session (obtained via getCustomerSession).\n */\nexport async function customerFetch>(\n input: CustomerFetchInput,\n): Promise {\n const discovery = await getDiscovery()\n const res = await fetch(discovery.graphqlEndpoint, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: input.session.accessToken,\n },\n body: JSON.stringify({ query: input.query, variables: input.variables }),\n })\n if (!res.ok) {\n throw new Error(\n `Customer Account API error: ${res.status} ${res.statusText}`,\n )\n }\n const json = (await res.json()) as CustomerResponse\n if (json.errors?.length) {\n throw new Error(json.errors.map((e) => e.message).join('\\n'))\n }\n if (!json.data) throw new Error('Customer Account API returned no data.')\n return json.data\n}\n") return __output } function __render_cfd368cd_11500(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import React from 'react'\nimport { ") if (fileRouter) { __append("createFileRoute") } else { __append("createRoute") } __append(" } from '@tanstack/react-router'\nimport {\n flexRender,\n getCoreRowModel,\n getFilteredRowModel,\n getPaginationRowModel,\n getSortedRowModel,\n sortingFns,\n useReactTable,\n} from '@tanstack/react-table'\nimport { compareItems, rankItem } from '@tanstack/match-sorter-utils'\n\nimport { makeData } from '#/data/demo-table-data'\n\nimport type {\n Column,\n ColumnDef,\n ColumnFiltersState,\n FilterFn,\n SortingFn,\n} from '@tanstack/react-table'\nimport type { RankingInfo } from '@tanstack/match-sorter-utils'\n") if (codeRouter) { __append("\nimport type { RootRoute } from '@tanstack/react-router'\n") } __append("\nimport type { Person } from '#/data/demo-table-data'\n") if (fileRouter) { __append("\nexport const Route = createFileRoute('/demo/table')({\n component: TableDemo,\n})\n") } __append("\n\ndeclare module '@tanstack/react-table' {\n interface FilterFns {\n fuzzy: FilterFn\n }\n interface FilterMeta {\n itemRank: RankingInfo\n }\n}\n\n// Define a custom fuzzy filter function that will apply ranking info to rows (using match-sorter utils)\nconst fuzzyFilter: FilterFn = (row, columnId, value, addMeta) => {\n // Rank the item\n const itemRank = rankItem(row.getValue(columnId), value)\n\n // Store the itemRank info\n addMeta({\n itemRank,\n })\n\n // Return if the item should be filtered in/out\n return itemRank.passed\n}\n\n// Define a custom fuzzy sort function that will sort by rank if the row has ranking information\nconst fuzzySort: SortingFn = (rowA, rowB, columnId) => {\n let dir = 0\n\n // Only sort by rank if the column has ranking information\n if (rowA.columnFiltersMeta[columnId]) {\n dir = compareItems(\n rowA.columnFiltersMeta[columnId]?.itemRank!,\n rowB.columnFiltersMeta[columnId]?.itemRank!,\n )\n }\n\n // Provide an alphanumeric fallback for when the item ranks are equal\n return dir === 0 ? sortingFns.alphanumeric(rowA, rowB, columnId) : dir\n}\n\nfunction TableDemo() {\n const rerender = React.useReducer(() => ({}), {})[1]\n\n const [columnFilters, setColumnFilters] = React.useState(\n [],\n )\n const [globalFilter, setGlobalFilter] = React.useState('')\n\n const columns = React.useMemo[]>(\n () => [\n {\n accessorKey: 'id',\n filterFn: 'equalsString', //note: normal non-fuzzy filter column - exact match required\n },\n {\n accessorKey: 'firstName',\n cell: (info) => info.getValue(),\n filterFn: 'includesStringSensitive', //note: normal non-fuzzy filter column - case sensitive\n },\n {\n accessorFn: (row) => row.lastName,\n id: 'lastName',\n cell: (info) => info.getValue(),\n header: () => Last Name,\n filterFn: 'includesString', //note: normal non-fuzzy filter column - case insensitive\n },\n {\n accessorFn: (row) => `${row.firstName} ${row.lastName}`,\n id: 'fullName',\n header: 'Full Name',\n cell: (info) => info.getValue(),\n filterFn: 'fuzzy', //using our custom fuzzy filter function\n // filterFn: fuzzyFilter, //or just define with the function\n sortingFn: fuzzySort, //sort by fuzzy rank (falls back to alphanumeric)\n },\n ],\n [],\n )\n\n const [data, setData] = React.useState(() => makeData(5_000))\n const refreshData = () => setData((_old) => makeData(50_000)) //stress test\n\n const table = useReactTable({\n data,\n columns,\n filterFns: {\n fuzzy: fuzzyFilter, //define as a filter function that can be used in column definitions\n },\n state: {\n columnFilters,\n globalFilter,\n },\n onColumnFiltersChange: setColumnFilters,\n onGlobalFilterChange: setGlobalFilter,\n globalFilterFn: 'fuzzy', //apply fuzzy filter to the global filter (most common use case for fuzzy filter)\n getCoreRowModel: getCoreRowModel(),\n getFilteredRowModel: getFilteredRowModel(), //client side filtering\n getSortedRowModel: getSortedRowModel(),\n getPaginationRowModel: getPaginationRowModel(),\n debugTable: true,\n debugHeaders: true,\n debugColumns: false,\n })\n\n //apply the fuzzy sort if the fullName column is being filtered\n React.useEffect(() => {\n if (table.getState().columnFilters[0]?.id === 'fullName') {\n if (table.getState().sorting[0]?.id !== 'fullName') {\n table.setSorting([{ id: 'fullName', desc: false }])\n }\n }\n }, [table.getState().columnFilters[0]?.id])\n\n return (\n
\n
\n

TanStack Table

\n

Table Demo

\n setGlobalFilter(String(value))}\n className=\"demo-input\"\n placeholder=\"Search all columns...\"\n />\n
\n
\n
\n \n \n {table.getHeaderGroups().map((headerGroup) => (\n \n {headerGroup.headers.map((header) => {\n return (\n \n {header.isPlaceholder ? null : (\n <>\n \n {flexRender(\n header.column.columnDef.header,\n header.getContext(),\n )}\n {{\n asc: ' 🔼',\n desc: ' 🔽',\n }[header.column.getIsSorted() as string] ?? null}\n \n {header.column.getCanFilter() ? (\n
\n \n
\n ) : null}\n \n )}\n \n )\n })}\n
\n ))}\n \n \n {table.getRowModel().rows.map((row) => {\n return (\n \n {row.getVisibleCells().map((cell) => {\n return (\n \n )\n })}\n \n )\n })}\n \n
\n {flexRender(\n cell.column.columnDef.cell,\n cell.getContext(),\n )}\n
\n
\n
\n
\n table.setPageIndex(0)}\n disabled={!table.getCanPreviousPage()}\n >\n {'<<'}\n \n table.previousPage()}\n disabled={!table.getCanPreviousPage()}\n >\n {'<'}\n \n table.nextPage()}\n disabled={!table.getCanNextPage()}\n >\n {'>'}\n \n table.setPageIndex(table.getPageCount() - 1)}\n disabled={!table.getCanNextPage()}\n >\n {'>>'}\n \n \n
Page
\n \n {table.getState().pagination.pageIndex + 1} of{' '}\n {table.getPageCount()}\n \n
\n \n | Go to page:\n {\n const page = e.target.value ? Number(e.target.value) - 1 : 0\n table.setPageIndex(page)\n }}\n className=\"demo-input demo-input-fit py-1\"\n />\n \n {\n table.setPageSize(Number(e.target.value))\n }}\n className=\"demo-select demo-input-fit py-1\"\n >\n {[10, 20, 30, 40, 50].map((pageSize) => (\n \n ))}\n \n
\n
\n {table.getPrePaginationRowModel().rows.length} Rows\n
\n
\n rerender()}\n className=\"demo-button\"\n >\n Force Rerender\n \n refreshData()}\n className=\"demo-button\"\n >\n Refresh Data\n \n
\n
\n        {JSON.stringify(\n          {\n            columnFilters: table.getState().columnFilters,\n            globalFilter: table.getState().globalFilter,\n          },\n          null,\n          2,\n        )}\n      
\n
\n )\n}\n\nfunction Filter({ column }: { column: Column }) {\n const columnFilterValue = column.getFilterValue()\n\n return (\n column.setFilterValue(value)}\n placeholder={`Search...`}\n className=\"demo-input py-1\"\n />\n )\n}\n\n// A typical debounced input react component\nfunction DebouncedInput({\n value: initialValue,\n onChange,\n debounce = 500,\n ...props\n}: {\n value: string | number\n onChange: (value: string | number) => void\n debounce?: number\n} & Omit, 'onChange'>) {\n const [value, setValue] = React.useState(initialValue)\n\n React.useEffect(() => {\n setValue(initialValue)\n }, [initialValue])\n\n React.useEffect(() => {\n const timeout = setTimeout(() => {\n onChange(value)\n }, debounce)\n\n return () => clearTimeout(timeout)\n }, [value])\n\n return (\n setValue(e.target.value)}\n />\n )\n}\n\n") if (codeRouter) { __append("\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/table',\n component: TableDemo,\n getParentRoute: () => parentRoute,\n})\n") } __append("\n") return __output } function __render_d28e3088_1729(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnEnabled.tRPC) { __append("\nimport type { ReactNode } from 'react'\nimport { QueryClient } from '@tanstack/react-query'\nimport superjson from \"superjson\";\nimport { createTRPCClient, httpBatchStreamLink } from \"@trpc/client\";\nimport { createTRPCOptionsProxy } from \"@trpc/tanstack-react-query\";\n\nimport type { TRPCRouter } from \"#/integrations/trpc/router\";\nimport { TRPCProvider } from '#/integrations/trpc/react'\n\nfunction getUrl() {\n const base = (() => {\n if (typeof window !== \"undefined\") return \"\";\n return `http://localhost:${process.env.PORT ?? 3000}`;\n })();\n return `${base}/api/trpc`;\n}\n\nexport const trpcClient = createTRPCClient({\n links: [\n httpBatchStreamLink({\n transformer: superjson,\n url: getUrl(),\n }),\n ],\n});\n\nexport function getContext() {\n const queryClient = new QueryClient({\n defaultOptions: {\n dehydrate: { serializeData: superjson.serialize },\n hydrate: { deserializeData: superjson.deserialize },\n },\n });\n\n const serverHelpers = createTRPCOptionsProxy({\n client: trpcClient,\n queryClient: queryClient,\n });\n const context = {\n queryClient,\n trpc: serverHelpers,\n }\n\n return context\n}\n\nexport default function TanstackQueryProvider({\n children,\n context,\n}: {\n children: ReactNode,\n context: ReturnType\n}) {\n const { queryClient } = context\n\n return (\n \n {children}\n \n )\n}\n") } else { __append("\nimport { QueryClient } from '@tanstack/react-query'\n\nexport function getContext() {\n const queryClient = new QueryClient();\n\n return {\n queryClient,\n }\n}\nexport default function TanstackQueryProvider() {}\n") } __append("\n") return __output } function __render_d323788b_6508(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (blank) { __append("# ") __append(__escapeXML(projectName)) __append("\n\nA minimal TanStack Start app with one route and ") __append(__escapeXML(tailwind ? 'Tailwind CSS' : 'plain CSS')) __append(".\n\n```bash\n") __append(__escapeXML(packageManager)) __append(" install\n") __append(__escapeXML(getPackageManagerRunScript('dev'))) __append("\n```\n\nEdit `src/routes/index.tsx` to get started. Add route files under\n`src/routes`; TanStack Router updates `src/routeTree.gen.ts` for you.\n\nBuild the production app with:\n\n```bash\n") __append(__escapeXML(getPackageManagerRunScript('build'))) __append("\n```\n") for(const addon of addOns.filter(addon => addon.readme)) { __append("\n") __append(addon.readmeIsEjs ? renderTemplate(addon.readme) : addon.readme) __append("\n") } __append("\n") } else { __append("Welcome to your new TanStack Start app!\n\n# Getting Started\n\nTo run this application:\n\n```bash\n") __append(__escapeXML(packageManager)) __append(" install\n") __append(__escapeXML(getPackageManagerRunScript('dev'))) __append("\n```\n\n# Building For Production\n\nTo build this application for production:\n\n```bash\n") __append(__escapeXML(getPackageManagerRunScript('build'))) __append("\n```\n\n## Styling\n\nThis project uses [Tailwind CSS](https://tailwindcss.com/) for styling.\n\n### Removing Tailwind CSS\n\nIf you prefer not to use Tailwind CSS:\n\n1. Remove the demo pages in `src/routes/demo/`\n2. Replace the Tailwind import in `src/styles.css` with your own styles\n3. Remove `tailwindcss()` from the plugins array in `vite.config.ts`\n4. Remove `@tailwindcss/vite` and `tailwindcss` from `package.json`\n") if (addOnEnabled.biome || addOnEnabled.eslint) { __append("\n## Linting & Formatting\n") if (addOnEnabled.biome) { __append("\nThis project uses [Biome](https://biomejs.dev/) for linting and formatting. The following scripts are available:\n") } __append("\n") if (addOnEnabled.eslint) { __append("\nThis project uses [eslint](https://eslint.org/) and [prettier](https://prettier.io/) for linting and formatting. Eslint is configured using [tanstack/eslint-config](https://tanstack.com/config/latest/docs/eslint). The following scripts are available:\n") } __append("\n```bash\n") __append(__escapeXML(getPackageManagerRunScript('lint'))) __append("\n") __append(__escapeXML(getPackageManagerRunScript('format'))) __append("\n") __append(__escapeXML(getPackageManagerRunScript('check'))) __append("\n```\n") } __append("\n") for(const addon of addOns.filter(addon => addon.readme)) { __append("\n") __append(addon.readmeIsEjs ? renderTemplate(addon.readme) : addon.readme) __append("\n") } __append("\n\n## Routing\n\nThis project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`.\n\n### Adding A Route\n\nTo add a new route to your application just add a new file in the `./src/routes` directory.\n\nTanStack will automatically generate the content of the route file for you.\n\nNow that you have two routes you can use a `Link` component to navigate between them.\n\n### Adding Links\n\nTo use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/react-router`.\n\n```tsx\nimport { Link } from \"@tanstack/react-router\";\n```\n\nThen anywhere in your JSX you can use it like so:\n\n```tsx\nAbout\n```\n\nThis will create a link that will navigate to the `/about` route.\n\nMore information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/react/api/router/linkComponent).\n\n### Using A Layout\n\nIn 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 render `{children}` in the `shellComponent`.\n\nHere is an example layout that includes a header:\n\n```tsx\nimport { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router'\n\nexport const Route = createRootRoute({\n head: () => ({\n meta: [\n { charSet: 'utf-8' },\n { name: 'viewport', content: 'width=device-width, initial-scale=1' },\n { title: 'My App' },\n ],\n }),\n shellComponent: ({ children }) => (\n \n \n \n \n \n
\n \n
\n {children}\n \n \n \n ),\n})\n```\n\nMore information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/react/guide/routing-concepts#layouts).\n\n## Server Functions\n\nTanStack Start provides server functions that allow you to write server-side code that seamlessly integrates with your client components.\n\n```tsx\nimport { createServerFn } from '@tanstack/react-start'\n\nconst getServerTime = createServerFn({\n method: 'GET',\n}).handler(async () => {\n return new Date().toISOString()\n})\n\n// Use in a component\nfunction MyComponent() {\n const [time, setTime] = useState('')\n \n useEffect(() => {\n getServerTime().then(setTime)\n }, [])\n \n return
Server time: {time}
\n}\n```\n\n## API Routes\n\nYou can create API routes by using the `server` property in your route definitions:\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\nimport { json } from '@tanstack/react-start'\n\nexport const Route = createFileRoute('/api/hello')({\n server: {\n handlers: {\n GET: () => json({ message: 'Hello, World!' }),\n },\n },\n})\n```\n\n## Data Fetching\n\nThere 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.\n\nFor example:\n\n```tsx\nimport { createFileRoute } from '@tanstack/react-router'\n\nexport const Route = createFileRoute('/people')({\n loader: async () => {\n const response = await fetch('https://swapi.dev/api/people')\n return response.json()\n },\n component: PeopleComponent,\n})\n\nfunction PeopleComponent() {\n const data = Route.useLoaderData()\n return (\n
    \n {data.results.map((person) => (\n
  • {person.name}
  • \n ))}\n
\n )\n}\n```\n\nLoaders 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).\n\n") if (includeExamples) { __append("\n# Demo files\n\nFiles 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.\n") } __append("\n\n# Learn More\n\nYou can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).\n\nFor TanStack Start specific documentation, visit [TanStack Start](https://tanstack.com/start).\n") } return __output } function __render_d83ec895_4432(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { createFileRoute, notFound } from '@tanstack/react-router'\n\nimport { Money } from '#/components/shop/money'\nimport { ShopImage } from '#/components/shop/shop-image'\nimport { getOrder } from '#/server/shopify/customer.functions'\n\nexport const Route = createFileRoute('/shop/account/orders/$id')({\n loader: async ({ params }) => {\n const order = await getOrder({ data: { id: decodeURIComponent(params.id) } })\n if (!order) throw notFound()\n return { order }\n },\n component: OrderDetailRoute,\n})\n\nfunction OrderDetailRoute() {\n const { order } = Route.useLoaderData()\n\n return (\n
\n
\n

{order.name}

\n

\n Placed {new Date(order.processedAt).toLocaleDateString()} ·{' '}\n {order.fulfillmentStatus ?? 'pending'}\n

\n
\n\n
    \n {order.lineItems.nodes.map((line) => (\n
  • \n \n
    \n

    {line.title}

    \n {line.variantTitle && (\n

    \n {line.variantTitle}\n

    \n )}\n

    \n Qty {line.quantity}\n

    \n
    \n \n
  • \n ))}\n
\n\n
\n {order.subtotal && (\n
\n Subtotal\n \n
\n )}\n {order.totalShipping && (\n
\n Shipping\n \n
\n )}\n {order.totalTax && (\n
\n Tax\n \n
\n )}\n
\n Total\n \n
\n
\n\n {order.shippingAddress && (\n
\n

Shipping address

\n

\n {[order.shippingAddress.firstName, order.shippingAddress.lastName]\n .filter(Boolean)\n .join(' ')}\n
\n {order.shippingAddress.address1}\n {order.shippingAddress.address2 && (\n <>\n
\n {order.shippingAddress.address2}\n \n )}\n
\n {[\n order.shippingAddress.city,\n order.shippingAddress.zoneCode,\n order.shippingAddress.zip,\n ]\n .filter(Boolean)\n .join(', ')}\n
\n {order.shippingAddress.territoryCode}\n

\n
\n )}\n
\n )\n}\n") return __output } function __render_db92ca28_5148(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (blank) { __append("# ") __append(__escapeXML(projectName)) __append("\n\nA minimal TanStack Start app with one route and ") __append(__escapeXML(tailwind ? 'Tailwind CSS' : 'plain CSS')) __append(".\n\n```bash\n") __append(__escapeXML(packageManager)) __append(" install\n") __append(__escapeXML(getPackageManagerRunScript('dev'))) __append("\n```\n\nEdit `src/routes/index.tsx` to get started. Add route files under\n`src/routes`; TanStack Router updates `src/routeTree.gen.ts` for you.\n\nBuild the production app with:\n\n```bash\n") __append(__escapeXML(getPackageManagerRunScript('build'))) __append("\n```\n") for(const addon of addOns.filter(addon => addon.readme)) { __append("\n") __append(addon.readmeIsEjs ? renderTemplate(addon.readme) : addon.readme) __append("\n") } __append("\n") } else { __append("Welcome to your new TanStack Start app!\n\n# Getting Started\n\nTo run this application:\n\n```bash\n") __append(__escapeXML(packageManager)) __append(" install\n") __append(__escapeXML(getPackageManagerRunScript('dev'))) __append("\n```\n\n# Building For Production\n\nTo build this application for production:\n\n```bash\n") __append(__escapeXML(getPackageManagerRunScript('build'))) __append("\n```\n\n## Styling\n\nThis project uses [Tailwind CSS](https://tailwindcss.com/) for styling.\n\n### Removing Tailwind CSS\n\nIf you prefer not to use Tailwind CSS:\n\n1. Remove the demo pages in `src/routes/demo/`\n2. Replace the Tailwind import in `src/styles.css` with your own styles\n3. Remove `tailwindcss()` from the plugins array in `vite.config.ts`\n4. Remove `@tailwindcss/vite` and `tailwindcss` from `package.json`\n\n") for(const addon of addOns.filter(addon => addon.readme)) { __append("\n") __append(addon.readmeIsEjs ? renderTemplate(addon.readme) : addon.readme) __append("\n") } __append("\n\n## Routing\n\nThis project uses [TanStack Router](https://tanstack.com/router) with file-based routing. Routes are managed as files in `src/routes`.\n\n### Adding A Route\n\nTo add a new route to your application just add a new file in the `./src/routes` directory.\n\nTanStack will automatically generate the content of the route file for you.\n\nNow that you have two routes you can use a `Link` component to navigate between them.\n\n### Adding Links\n\nTo use SPA (Single Page Application) navigation you will need to import the `Link` component from `@tanstack/solid-router`.\n\n```tsx\nimport { Link } from \"@tanstack/solid-router\";\n```\n\nThen anywhere in your JSX you can use it like so:\n\n```tsx\nAbout\n```\n\nThis will create a link that will navigate to the `/about` route.\n\nMore information on the `Link` component can be found in the [Link documentation](https://tanstack.com/router/v1/docs/framework/solid/api/router/linkComponent).\n\n### Using A Layout\n\nIn 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.\n\nMore information on layouts can be found in the [Layouts documentation](https://tanstack.com/router/latest/docs/framework/solid/guide/routing-concepts#layouts).\n\n## Server Functions\n\nTanStack Start provides server functions that allow you to write server-side code that seamlessly integrates with your client components.\n\n```tsx\nimport { createServerFn } from '@tanstack/solid-start'\n\nconst getServerTime = createServerFn({\n method: 'GET',\n}).handler(async () => {\n return new Date().toISOString()\n})\n```\n\n## Data Fetching\n\nThere 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.\n\nFor example:\n\n```tsx\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/people')({\n loader: async () => {\n const response = await fetch('https://swapi.dev/api/people')\n return response.json()\n },\n component: PeopleComponent,\n})\n\nfunction PeopleComponent() {\n const data = Route.useLoaderData()\n return (\n
    \n \n {(person) =>
  • {person.name}
  • }\n
    \n
\n )\n}\n```\n\nLoaders simplify your data fetching logic dramatically. Check out more information in the [Loader documentation](https://tanstack.com/router/latest/docs/framework/solid/guide/data-loading#loader-parameters).\n\n") if (includeExamples) { __append("\n# Demo files\n\nFiles 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.\n") } __append("\n\n") if (addOnEnabled.biome || addOnEnabled.eslint) { __append("\n## Linting & Formatting\n") if (addOnEnabled.biome) { __append("\nThis project uses [Biome](https://biomejs.dev/) for linting and formatting. The following scripts are available:\n") } __append("\n") if (addOnEnabled.eslint) { __append("\nThis project uses [eslint](https://eslint.org/) and [prettier](https://prettier.io/) for linting and formatting. Eslint is configured using [tanstack/eslint-config](https://tanstack.com/config/latest/docs/eslint). The following scripts are available:\n") } __append("\n```bash\n") __append(__escapeXML(getPackageManagerRunScript('lint'))) __append("\n") __append(__escapeXML(getPackageManagerRunScript('format'))) __append("\n") __append(__escapeXML(getPackageManagerRunScript('check'))) __append("\n```\n") } __append("\n\n# Learn More\n\nYou can learn more about all of the offerings from TanStack in the [TanStack documentation](https://tanstack.com).\n\nFor TanStack Start specific documentation, visit [TanStack Start](https://tanstack.com/start).\n") } return __output } function __render_de796a29_811(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"files.watcherExclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"search.exclude\": {\n \"**/routeTree.gen.ts\": true\n },\n \"files.readonlyInclude\": {\n \"**/routeTree.gen.ts\": true\n }") if (addOnEnabled.biome) { __append(",\n \"[javascript]\": {\n \"editor.defaultFormatter\": \"biomejs.biome\"\n },\n \"[javascriptreact]\": {\n \"editor.defaultFormatter\": \"biomejs.biome\"\n },\n \"[typescript]\": {\n \"editor.defaultFormatter\": \"biomejs.biome\"\n },\n \"[typescriptreact]\": {\n \"editor.defaultFormatter\": \"biomejs.biome\"\n },\n \"[json]\": {\n \"editor.defaultFormatter\": \"biomejs.biome\"\n },\n \"[jsonc]\": {\n \"editor.defaultFormatter\": \"biomejs.biome\"\n },\n \"[css]\": {\n \"editor.defaultFormatter\": \"biomejs.biome\"\n },\n \"editor.codeActionsOnSave\": {\n \"source.organizeImports.biome\": \"explicit\"\n }") } __append("\n}\n") return __output } function __render_e404fd5e_429(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("services:\n - type: web\n runtime: node\n name: tanstack-start-app\n buildCommand: ") __append(__escapeXML(packageManager)) __append(" install && ") __append(__escapeXML(getPackageManagerRunScript('build'))) __append("\n startCommand: ") __append(__escapeXML(getPackageManagerRunScript('start'))) __append("\n envVars:\n") if (packageManager === 'bun') { __append("- key: BUN_VERSION\n value: '1.3.14'\n") } __append("- key: NITRO_PRESET\n value: render-com\n - key: HOST\n value: 0.0.0.0\n") return __output } function __render_e44c6598_134(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("{\n \"dependencies\": {\n \"@journeyapps/wa-sqlite\": \"^1.2.6\",\n \"@powersync/react\": \"^1.7.4\",\n \"@powersync/web\": \"^1.26.1\"\n }\n}\n") return __output } function __render_e5b15f91_6353(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { ") if (fileRouter) { __append("createFileRoute") } else { __append("createRoute") } __append(" } from '@tanstack/react-router'\n\nimport { useAppForm } from '#/hooks/demo.form'\n\n") if (codeRouter) { __append("\nimport type { RootRoute } from '@tanstack/react-router'\n") } else { __append("\nexport const Route = createFileRoute('/demo/form')({\n component: AddressForm,\n})\n") } __append("\n\nfunction AddressForm() {\n const form = useAppForm({\n defaultValues: {\n fullName: '',\n email: '',\n address: {\n street: '',\n city: '',\n state: '',\n zipCode: '',\n country: '',\n },\n phone: '',\n },\n validators: {\n onBlur: ({ value }) => {\n const errors = {\n fields: {},\n } as {\n fields: Record\n }\n if (value.fullName.trim().length === 0) {\n errors.fields.fullName = 'Full name is required'\n }\n return errors\n },\n },\n onSubmit: ({ value }) => {\n console.log(value)\n // Show success message\n alert('Form submitted successfully!')\n },\n })\n\n return (\n
\n
\n
\n

TanStack Form

\n

Address Form

\n

\n Nested fields, field-level validation, and a select input.\n

\n
\n {\n e.preventDefault()\n e.stopPropagation()\n form.handleSubmit()\n }}\n className=\"space-y-6\"\n >\n \n {(field) => }\n \n\n {\n if (!value || value.trim().length === 0) {\n return 'Email is required'\n }\n if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$/i.test(value)) {\n return 'Invalid email address'\n }\n return undefined\n },\n }}\n >\n {(field) => }\n \n\n {\n if (!value || value.trim().length === 0) {\n return 'Street address is required'\n }\n return undefined\n },\n }}\n >\n {(field) => }\n \n\n
\n {\n if (!value || value.trim().length === 0) {\n return 'City is required'\n }\n return undefined\n },\n }}\n >\n {(field) => }\n \n {\n if (!value || value.trim().length === 0) {\n return 'State is required'\n }\n return undefined\n },\n }}\n >\n {(field) => }\n \n {\n if (!value || value.trim().length === 0) {\n return 'Zip code is required'\n }\n if (!/^\\d{5}(-\\d{4})?$/.test(value)) {\n return 'Invalid zip code format'\n }\n return undefined\n },\n }}\n >\n {(field) => }\n \n
\n\n {\n if (!value || value.trim().length === 0) {\n return 'Country is required'\n }\n return undefined\n },\n }}\n >\n {(field) => (\n \n )}\n \n\n {\n if (!value || value.trim().length === 0) {\n return 'Phone number is required'\n }\n if (\n !/^(\\+\\d{1,3})?\\s?\\(?\\d{3}\\)?[\\s.-]?\\d{3}[\\s.-]?\\d{4}$/.test(\n value,\n )\n ) {\n return 'Invalid phone number format'\n }\n return undefined\n },\n }}\n >\n {(field) => (\n \n )}\n \n\n
\n \n \n \n
\n \n
\n
\n )\n}\n\n\n") if (codeRouter) { __append("\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/form/address',\n component: AddressForm,\n getParentRoute: () => parentRoute,\n})\n") } __append("\n") return __output } function __render_e9563c39_582(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { createFileRoute, redirect } from '@tanstack/react-router'\n\nimport { logout } from '#/server/shopify/customer.functions'\n\nexport const Route = createFileRoute('/shop/account/logout')({\n beforeLoad: async () => {\n const { endSessionUrl } = await logout()\n if (endSessionUrl) {\n // Bounce through Shopify's end-session endpoint to clear their session too.\n throw redirect({ href: endSessionUrl })\n }\n throw redirect({ to: '/shop' })\n },\n component: () => null,\n})\n") return __output } function __render_ee45a002_1600(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } if (addOnOption.shopify.customerAccount !== 'enabled') { ignoreFile(); return; } __append("\nimport { createFileRoute, redirect } from '@tanstack/react-router'\nimport * as v from 'valibot'\n\nimport { completeLogin } from '#/server/shopify/customer.functions'\n\nconst CallbackSearch = v.object({\n code: v.optional(v.string()),\n state: v.optional(v.string()),\n error: v.optional(v.string()),\n error_description: v.optional(v.string()),\n})\n\nexport const Route = createFileRoute('/shop/account/callback')({\n validateSearch: (search) => v.parse(CallbackSearch, search),\n beforeLoad: async ({ search }) => {\n if (search.error || !search.code || !search.state) {\n return { error: search.error_description ?? search.error ?? 'Missing OAuth code/state.' }\n }\n try {\n const { redirectAfter } = await completeLogin({\n data: { code: search.code, state: search.state },\n })\n throw redirect({ to: redirectAfter || '/shop/account' })\n } catch (err) {\n if (err && typeof err === 'object' && 'isRedirect' in err) throw err\n return { error: err instanceof Error ? err.message : String(err) }\n }\n },\n loader: ({ context }) => ({ error: context.error }),\n component: CallbackRoute,\n})\n\nfunction CallbackRoute() {\n const { error } = Route.useLoaderData()\n if (error) {\n return (\n
\n

Sign-in failed

\n

{error}

\n
\n )\n }\n return null\n}\n") return __output } function __render_ee98c04f_316(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("import { config } from \"dotenv\";\nimport { defineConfig } from 'drizzle-kit';\n\nconfig({ path: ['.env.local', '.env'] });\n\nexport default defineConfig({\n out: \"./drizzle\",\n schema: \"./src/db/schema.ts\",\n dialect: '") __append(__escapeXML(addOnOption.drizzle.database)) __append("',\n dbCredentials: {\n url: process.env.DATABASE_URL,\n },\n});\n") return __output } function __render_f1192baf_807(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } __append("## Setting up Better Auth\n\n1. Generate and set the `BETTER_AUTH_SECRET` environment variable in your `.env.local`:\n\n ```bash\n ") __append(getPackageManagerExecuteScript('@better-auth/cli', ['secret'])) __append("\n ```\n\n2. Visit the [Better Auth documentation](https://www.better-auth.com) to unlock the full potential of authentication in your app.\n\n### Adding a Database (Optional)\n\nBetter Auth can work in stateless mode, but to persist user data, add a database:\n\n```typescript\n// src/lib/auth.ts\nimport { betterAuth } from \"better-auth\";\nimport { Pool } from \"pg\";\n\nexport const auth = betterAuth({\n database: new Pool({\n connectionString: process.env.DATABASE_URL,\n }),\n // ... rest of config\n});\n```\n\nThen run migrations:\n\n```bash\n") __append(getPackageManagerExecuteScript('@better-auth/cli', ['migrate'])) __append("\n```\n") return __output } function __render_f4bbbbc6_8672(context: TemplateRenderContext) { const { packageManager, projectName, typescript, tailwind, blank, js, jsx, fileRouter, codeRouter, routerOnly, includeExamples, addOnEnabled, addOnOption, addOns, integrations, routes, getPackageManagerAddScript, getPackageManagerRunScript, getPackageManagerExecuteScript, relativePath, integrationImportContent, integrationImportCode, renderTemplate, ignoreFile, } = context let __output = '' const __append = (value: unknown) => { if (value !== undefined && value !== null) { __output += String(value) } } const showDevtools = !blank || integrations.some((integration) => integration.type === 'devtools') if (routerOnly) { __append("\nimport { Outlet, createRootRoute } from '@tanstack/react-router'\n") if (showDevtools) { __append("\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'\nimport { TanStackDevtools } from '@tanstack/react-devtools'\n") } __append("\n\nimport '../styles.css'\n\nexport const Route = createRootRoute({\n component: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n <>\n \n ") if (showDevtools) { __append(",\n },\n ]}\n />") } __append("\n \n )\n}\n") } else if (!includeExamples || blank) { __append("\n") let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; __append("\nimport {\n HeadContent, Scripts, ") if (hasContext) { __append("createRootRouteWithContext") } else { __append("createRootRoute") } __append(" } from '@tanstack/react-router'\n") if (showDevtools) { __append("import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';\nimport { TanStackDevtools } from '@tanstack/react-devtools'\n") } __append("\n") for(const integration of integrations.filter(i => i.type === 'layout' || i.type === 'provider' || i.type === 'devtools')) { __append("\nimport ") __append(__escapeXML(integration.jsName)) __append(" from '") __append(__escapeXML(relativePath(integration.path, true))) __append("'\n") } if (addOnEnabled.paraglide) { __append("\nimport { getLocale } from '#/paraglide/runtime'\n") } __append("\nimport appCss from '../styles.css?url'\n") if (addOnEnabled["apollo-client"]) { __append("\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\n") } __append("\n") if (addOnEnabled["tanstack-query"]) { __append("\nimport type { QueryClient } from '@tanstack/react-query'\n") if (addOnEnabled.tRPC) { __append("\nimport type { TRPCRouter } from '#/integrations/trpc/router'\nimport type { TRPCOptionsProxy } from '@trpc/tanstack-react-query'\n") } __append("\n") } __append("\n") if (hasContext) { __append("\ninterface MyRouterContext ") if (addOnEnabled["apollo-client"]) { __append(" extends ApolloClientIntegration.RouterContext ") } __append("{\n") if (addOnEnabled["tanstack-query"]) { __append("\n queryClient: QueryClient\n ") if (addOnEnabled.tRPC) { __append("\n trpc: TRPCOptionsProxy\n ") } __append("\n") } __append("\n}") } __append("\n\nexport const Route = ") if (hasContext) { __append("createRootRouteWithContext()") } else { __append("createRootRoute") } __append("({\n") if (addOnEnabled.paraglide) { __append("\n beforeLoad: async () => {\n // Other redirect strategies are possible; see\n // https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#offline-redirect\n if (typeof document !== 'undefined') {\n document.documentElement.setAttribute('lang', getLocale())\n }\n },\n") } __append("\n head: () => ({\n meta: [\n {\n charSet: 'utf-8',\n },\n {\n name: 'viewport',\n content: 'width=device-width, initial-scale=1',\n },\n {\n title: 'TanStack Start Starter',\n },\n ],\n links: [\n {\n rel: 'stylesheet',\n href: appCss,\n },\n ],\n }),\n shellComponent: RootDocument\n})\n\nfunction RootDocument({ children }: { children: React.ReactNode }) {\n return (\n ") if (addOnEnabled.paraglide) { __append("") } else { __append("") } __append("\n \n \n \n \n ") for(const integration of integrations.filter(i => i.type === 'provider')) { __append("<") __append(__escapeXML(integration.jsName)) __append(">\n ") } __append("{children}\n ") if (showDevtools) { __append(",\n },\n ") for(const integration of integrations.filter(i => i.type === 'devtools')) { __append(__escapeXML(integration.jsName)) __append(",") } __append("\n ]}\n />") } __append("\n ") for(const integration of integrations.filter(i => i.type === 'layout')) { __append("<") __append(__escapeXML(integration.jsName)) __append(" />\n ") } for(const integration of integrations.filter(i => i.type === 'provider').reverse()) { __append("\n ") } __append("\n \n \n )\n}\n") } else { __append("\n") let hasContext = addOnEnabled["apollo-client"] || addOnEnabled["tanstack-query"]; __append("\nimport {\n HeadContent, Scripts, ") if (hasContext) { __append("createRootRouteWithContext") } else { __append("createRootRoute") } __append(" } from '@tanstack/react-router'\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';\nimport { TanStackDevtools } from '@tanstack/react-devtools'\nimport Footer from '../components/Footer'\nimport Header from '../components/Header'\n") for(const integration of integrations.filter(i => i.type === 'layout' || i.type === 'provider' || i.type === 'devtools')) { __append("\nimport ") __append(__escapeXML(integration.jsName)) __append(" from '") __append(__escapeXML(relativePath(integration.path, true))) __append("'\n") } if (addOnEnabled.paraglide) { __append("\nimport { getLocale } from '#/paraglide/runtime'\n") } __append("\nimport appCss from '../styles.css?url'\n") if (addOnEnabled["apollo-client"]) { __append("\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\n") } __append("\n") if (addOnEnabled["tanstack-query"]) { __append("\nimport type { QueryClient } from '@tanstack/react-query'\n") if (addOnEnabled.tRPC) { __append("\nimport type { TRPCRouter } from '#/integrations/trpc/router'\nimport type { TRPCOptionsProxy } from '@trpc/tanstack-react-query'\n") } __append("\n") } __append("\n") if (hasContext) { __append("\ninterface MyRouterContext ") if (addOnEnabled["apollo-client"]) { __append(" extends ApolloClientIntegration.RouterContext ") } __append("{\n") if (addOnEnabled["tanstack-query"]) { __append("\n queryClient: QueryClient\n ") if (addOnEnabled.tRPC) { __append("\n trpc: TRPCOptionsProxy\n ") } __append("\n") } __append("\n}") } __append("\n\nconst THEME_INIT_SCRIPT = `(function(){try{var stored=window.localStorage.getItem('theme');var mode=(stored==='light'||stored==='dark'||stored==='auto')?stored:'auto';var prefersDark=window.matchMedia('(prefers-color-scheme: dark)').matches;var resolved=mode==='auto'?(prefersDark?'dark':'light'):mode;var root=document.documentElement;root.classList.remove('light','dark');root.classList.add(resolved);if(mode==='auto'){root.removeAttribute('data-theme')}else{root.setAttribute('data-theme',mode)}root.style.colorScheme=resolved;}catch(e){}})();`\n\nexport const Route = ") if (hasContext) { __append("createRootRouteWithContext()") } else { __append("createRootRoute") } __append("({\n") if (addOnEnabled.paraglide) { __append("\n beforeLoad: async () => {\n // Other redirect strategies are possible; see\n // https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#offline-redirect\n if (typeof document !== 'undefined') {\n document.documentElement.setAttribute('lang', getLocale())\n }\n },\n") } __append("\n head: () => ({\n meta: [\n {\n charSet: 'utf-8',\n },\n {\n name: 'viewport',\n content: 'width=device-width, initial-scale=1',\n },\n {\n title: 'TanStack Start Starter',\n },\n ],\n links: [\n {\n rel: 'stylesheet',\n href: appCss,\n },\n ],\n }),\n shellComponent: RootDocument\n})\n\nfunction RootDocument({ children }: { children: React.ReactNode }) {\n return (\n ") if (addOnEnabled.paraglide) { __append("") } else { __append("") } __append("\n \n \n \n\n", "package.json": "{\n \"name\": \"\",\n \"private\": true,\n \"type\": \"module\",\n \"imports\": {\n \"#/*\": \"./src/*\"\n },\n \"scripts\": {\n \"dev\": \"vite dev --port 3000\",\n \"generate-routes\": \"tsr generate\",\n \"build\": \"vite build\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@tanstack/react-router\": \"latest\",\n \"@tanstack/react-start\": \"latest\",\n \"react\": \"^19.2.0\",\n \"react-dom\": \"^19.2.0\"\n },\n \"devDependencies\": {\n \"@vitejs/plugin-react\": \"^6.0.1\",\n \"vite\": \"^8.0.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\n \"esbuild\",\n \"lightningcss\"\n ]\n }\n}\n", "pnpm-workspace.yaml.ejs": "<% if (packageManager !== 'pnpm') { ignoreFile(); return; } -%>\nallowBuilds:\n esbuild: true\n lightningcss: true\n<% if (addOnEnabled.sentry) { -%>\n '@sentry/cli': true\n<% } -%>\n<% if (addOnEnabled.cloudflare || addOnEnabled.netlify) { -%>\n sharp: true\n<% } -%>\n<% if (addOnEnabled.cloudflare) { -%>\n workerd: true\n<% } -%>\n<% if (addOnEnabled.prisma) { -%>\n '@prisma/engines': true\n prisma: true\n<% } -%>\n<% if (addOnEnabled.drizzle || (addOnEnabled.prisma && addOnOption.prisma.database === 'sqlite')) { -%>\n better-sqlite3: true\n<% } -%>\n", "src/components/Footer.tsx.ejs": "<% if (!includeExamples) { ignoreFile(); return; } %>\nexport default function Footer() {\n const year = new Date().getFullYear()\n\n return (\n
\n
\n

© {year} Your name here. All rights reserved.

\n

Built with TanStack Start

\n
\n
\n \n Follow TanStack on X\n \n \n \n \n \n Go to TanStack GitHub\n \n \n \n \n
\n
\n )\n}\n", "src/components/Header.tsx.ejs": "<% if (!includeExamples) { ignoreFile(); return; } %>\nimport { Link } from '@tanstack/react-router'\n<% for (const integration of integrations.filter((i) => i.type === 'header-user')) { %>import <%= integration.jsName %> from '<%= relativePath(integration.path) %>'\n<% } %>import ThemeToggle from './ThemeToggle'\n\n<%\nconst demoRoutes = [];\nfor (const addOn of addOns) {\n for (const route of (addOn?.routes || []).filter((r) => r.url && r.name)) {\n demoRoutes.push({ url: route.url, name: route.name });\n for (const child of route.children || []) {\n if (child?.url && child?.name) {\n demoRoutes.push({ url: child.url, name: child.name });\n }\n }\n }\n}\nconst userHeaders = integrations.filter((i) => i.type === 'header-user');\n%>\n\nexport default function Header() {\n return (\n
\n \n
\n )\n}\n", "src/components/ThemeToggle.tsx.ejs": "<% if (!includeExamples) { ignoreFile(); return; } %>\nimport { useEffect, useState } from 'react'\n\ntype ThemeMode = 'light' | 'dark' | 'auto'\n\nfunction getInitialMode(): ThemeMode {\n if (typeof window === 'undefined') {\n return 'auto'\n }\n\n const stored = window.localStorage.getItem('theme')\n if (stored === 'light' || stored === 'dark' || stored === 'auto') {\n return stored\n }\n\n return 'auto'\n}\n\nfunction applyThemeMode(mode: ThemeMode) {\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches\n const resolved = mode === 'auto' ? (prefersDark ? 'dark' : 'light') : mode\n\n document.documentElement.classList.remove('light', 'dark')\n document.documentElement.classList.add(resolved)\n\n if (mode === 'auto') {\n document.documentElement.removeAttribute('data-theme')\n } else {\n document.documentElement.setAttribute('data-theme', mode)\n }\n\n document.documentElement.style.colorScheme = resolved\n}\n\nexport default function ThemeToggle() {\n const [mode, setMode] = useState('auto')\n\n useEffect(() => {\n const initialMode = getInitialMode()\n setMode(initialMode)\n applyThemeMode(initialMode)\n }, [])\n\n useEffect(() => {\n if (mode !== 'auto') {\n return\n }\n\n const media = window.matchMedia('(prefers-color-scheme: dark)')\n const onChange = () => applyThemeMode('auto')\n\n media.addEventListener('change', onChange)\n return () => {\n media.removeEventListener('change', onChange)\n }\n }, [mode])\n\n function toggleMode() {\n const nextMode: ThemeMode =\n mode === 'light' ? 'dark' : mode === 'dark' ? 'auto' : 'light'\n setMode(nextMode)\n applyThemeMode(nextMode)\n window.localStorage.setItem('theme', nextMode)\n }\n\n const label =\n mode === 'auto'\n ? 'Theme mode: auto (system). Click to switch to light mode.'\n : `Theme mode: ${mode}. Click to switch mode.`\n\n return (\n \n {mode === 'auto' ? 'Auto' : mode === 'dark' ? 'Dark' : 'Light'}\n \n )\n}\n", "src/main.tsx.ejs": "<% if (!routerOnly) { ignoreFile() } %>\nimport ReactDOM from 'react-dom/client'\nimport { RouterProvider, createRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen'\n\nconst router = createRouter({\n routeTree,\n defaultPreload: 'intent',\n scrollRestoration: true,\n})\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: typeof router\n }\n}\n\nconst rootElement = document.getElementById('app')!\n\nif (!rootElement.innerHTML) {\n const root = ReactDOM.createRoot(rootElement)\n root.render()\n}\n", "src/router.tsx.ejs": "import { createRouter as createTanStackRouter } from '@tanstack/react-router'\nimport { routeTree } from './routeTree.gen'\n<% if (addOnEnabled['tanstack-query']) { %><% if (addOnEnabled.tRPC) { %>\nimport type { ReactNode } from 'react'<% } %>\nimport { setupRouterSsrQueryIntegration } from \"@tanstack/react-router-ssr-query\";\nimport <% if (addOnEnabled.tRPC) { %>TanstackQueryProvider, <% } %>{ getContext } from './integrations/tanstack-query/root-provider'\n<% } %>\n\nexport function getRouter() {\n <% if (addOnEnabled['tanstack-query']) { %>\n const context = getContext();\n <% } %>\n \n const router = createTanStackRouter({\n routeTree,<% if (addOnEnabled['tanstack-query']) { %>\n context,<% } else if (addOnEnabled['apollo-client']) { %>\n context: {} as any,\n<% } %>\n scrollRestoration: true,\n defaultPreload: 'intent',\n defaultPreloadStaleTime: 0,\n <% if (addOnEnabled.tRPC) { %>\n Wrap: (props: { children: ReactNode }) => {\n return (\n \n {props.children}\n \n );\n },\n <% } %>\n })\n\n<% if (addOnEnabled['tanstack-query']) { %>\n setupRouterSsrQueryIntegration({ router, queryClient: context.queryClient })\n<% } %>\n\n return router\n}\n\ndeclare module '@tanstack/react-router' {\n interface Register {\n router: ReturnType\n }\n}\n", "src/routes/__root.tsx.ejs": "<% const showDevtools = !blank || integrations.some((integration) => integration.type === 'devtools') -%>\n<% if (routerOnly) { %>\nimport { Outlet, createRootRoute } from '@tanstack/react-router'\n<% if (showDevtools) { %>\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools'\nimport { TanStackDevtools } from '@tanstack/react-devtools'\n<% } %>\n\nimport '../styles.css'\n\nexport const Route = createRootRoute({\n component: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n <>\n \n <% if (showDevtools) { %>,\n },\n ]}\n /><% } %>\n \n )\n}\n<% } else if (!includeExamples || blank) { %>\n<% let hasContext = addOnEnabled[\"apollo-client\"] || addOnEnabled[\"tanstack-query\"]; %>\nimport {\n HeadContent, Scripts, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'\n<% if (showDevtools) { %>import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';\nimport { TanStackDevtools } from '@tanstack/react-devtools'\n<% } %>\n<% for(const integration of integrations.filter(i => i.type === 'layout' || i.type === 'provider' || i.type === 'devtools')) { %>\nimport <%= integration.jsName %> from '<%= relativePath(integration.path, true) %>'\n<% } %><% if (addOnEnabled.paraglide) { %>\nimport { getLocale } from '#/paraglide/runtime'\n<% } %>\nimport appCss from '../styles.css?url'\n<% if (addOnEnabled[\"apollo-client\"]) { %>\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\n<% } %>\n<% if (addOnEnabled[\"tanstack-query\"]) { %>\nimport type { QueryClient } from '@tanstack/react-query'\n<% if (addOnEnabled.tRPC) { %>\nimport type { TRPCRouter } from '#/integrations/trpc/router'\nimport type { TRPCOptionsProxy } from '@trpc/tanstack-react-query'\n<% } %>\n<% } %>\n<% if (hasContext) { %>\ninterface MyRouterContext <% if (addOnEnabled[\"apollo-client\"]) {%> extends ApolloClientIntegration.RouterContext <%} %>{\n<% if (addOnEnabled[\"tanstack-query\"]) { %>\n queryClient: QueryClient\n <% if (addOnEnabled.tRPC) { %>\n trpc: TRPCOptionsProxy\n <% } %>\n<% } %>\n}<% } %>\n\nexport const Route = <% if (hasContext) { %>createRootRouteWithContext()<% } else { %>createRootRoute<% } %>({\n<% if (addOnEnabled.paraglide) { %>\n beforeLoad: async () => {\n // Other redirect strategies are possible; see\n // https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#offline-redirect\n if (typeof document !== 'undefined') {\n document.documentElement.setAttribute('lang', getLocale())\n }\n },\n<% } %>\n head: () => ({\n meta: [\n {\n charSet: 'utf-8',\n },\n {\n name: 'viewport',\n content: 'width=device-width, initial-scale=1',\n },\n {\n title: 'TanStack Start Starter',\n },\n ],\n links: [\n {\n rel: 'stylesheet',\n href: appCss,\n },\n ],\n }),\n shellComponent: RootDocument\n})\n\nfunction RootDocument({ children }: { children: React.ReactNode }) {\n return (\n <% if (addOnEnabled.paraglide) { %><% } else { %><% } %>\n \n \n \n \n <% for(const integration of integrations.filter(i => i.type === 'provider')) { %><<%= integration.jsName %>>\n <% } %>{children}\n <% if (showDevtools) { %>,\n },\n <% for(const integration of integrations.filter(i => i.type === 'devtools')) { %><%= integration.jsName %>,<% } %>\n ]}\n /><% } %>\n <% for(const integration of integrations.filter(i => i.type === 'layout')) { %><<%= integration.jsName %> />\n <% } %><% for(const integration of integrations.filter(i => i.type === 'provider').reverse()) { %>>\n <% } %>\n \n \n )\n}\n<% } else { %>\n<% let hasContext = addOnEnabled[\"apollo-client\"] || addOnEnabled[\"tanstack-query\"]; %>\nimport {\n HeadContent, Scripts, <% if (hasContext) { %>createRootRouteWithContext<% } else { %>createRootRoute<% } %> } from '@tanstack/react-router'\nimport { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools';\nimport { TanStackDevtools } from '@tanstack/react-devtools'\nimport Footer from '../components/Footer'\nimport Header from '../components/Header'\n<% for(const integration of integrations.filter(i => i.type === 'layout' || i.type === 'provider' || i.type === 'devtools')) { %>\nimport <%= integration.jsName %> from '<%= relativePath(integration.path, true) %>'\n<% } %><% if (addOnEnabled.paraglide) { %>\nimport { getLocale } from '#/paraglide/runtime'\n<% } %>\nimport appCss from '../styles.css?url'\n<% if (addOnEnabled[\"apollo-client\"]) { %>\nimport type { ApolloClientIntegration } from \"@apollo/client-integration-tanstack-start\";\n<% } %>\n<% if (addOnEnabled[\"tanstack-query\"]) { %>\nimport type { QueryClient } from '@tanstack/react-query'\n<% if (addOnEnabled.tRPC) { %>\nimport type { TRPCRouter } from '#/integrations/trpc/router'\nimport type { TRPCOptionsProxy } from '@trpc/tanstack-react-query'\n<% } %>\n<% } %>\n<% if (hasContext) { %>\ninterface MyRouterContext <% if (addOnEnabled[\"apollo-client\"]) {%> extends ApolloClientIntegration.RouterContext <%} %>{\n<% if (addOnEnabled[\"tanstack-query\"]) { %>\n queryClient: QueryClient\n <% if (addOnEnabled.tRPC) { %>\n trpc: TRPCOptionsProxy\n <% } %>\n<% } %>\n}<% } %>\n\nconst THEME_INIT_SCRIPT = `(function(){try{var stored=window.localStorage.getItem('theme');var mode=(stored==='light'||stored==='dark'||stored==='auto')?stored:'auto';var prefersDark=window.matchMedia('(prefers-color-scheme: dark)').matches;var resolved=mode==='auto'?(prefersDark?'dark':'light'):mode;var root=document.documentElement;root.classList.remove('light','dark');root.classList.add(resolved);if(mode==='auto'){root.removeAttribute('data-theme')}else{root.setAttribute('data-theme',mode)}root.style.colorScheme=resolved;}catch(e){}})();`\n\nexport const Route = <% if (hasContext) { %>createRootRouteWithContext()<% } else { %>createRootRoute<% } %>({\n<% if (addOnEnabled.paraglide) { %>\n beforeLoad: async () => {\n // Other redirect strategies are possible; see\n // https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#offline-redirect\n if (typeof document !== 'undefined') {\n document.documentElement.setAttribute('lang', getLocale())\n }\n },\n<% } %>\n head: () => ({\n meta: [\n {\n charSet: 'utf-8',\n },\n {\n name: 'viewport',\n content: 'width=device-width, initial-scale=1',\n },\n {\n title: 'TanStack Start Starter',\n },\n ],\n links: [\n {\n rel: 'stylesheet',\n href: appCss,\n },\n ],\n }),\n shellComponent: RootDocument\n})\n\nfunction RootDocument({ children }: { children: React.ReactNode }) {\n return (\n <% if (addOnEnabled.paraglide) { %><% } else { %><% } %>\n \n \n \n\n", "package.json": "{\n \"name\": \"app-ts\",\n \"private\": true,\n \"type\": \"module\",\n \"scripts\": {\n \"dev\": \"vite dev --port 3000\",\n \"generate-routes\": \"tsr generate\",\n \"build\": \"vite build\",\n \"start\": \"node .output/server/index.mjs\",\n \"preview\": \"vite preview\"\n },\n \"dependencies\": {\n \"@tanstack/solid-router\": \"latest\",\n \"@tanstack/solid-start\": \"latest\",\n \"solid-js\": \"^1.9.12\"\n },\n \"devDependencies\": {\n \"vite-plugin-solid\": \"^2.11.12\",\n \"vite\": \"^8.0.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\n \"esbuild\",\n \"lightningcss\"\n ]\n }\n}\n", "pnpm-workspace.yaml.ejs": "<% if (packageManager !== 'pnpm') { ignoreFile(); return; } -%>\nallowBuilds:\n esbuild: true\n lightningcss: true\n<% if (addOnEnabled.cloudflare || addOnEnabled.netlify) { -%>\n sharp: true\n<% } -%>\n<% if (addOnEnabled.cloudflare) { -%>\n workerd: true\n<% } -%>\n", "src/components/Header.tsx.ejs": "<% if (!includeExamples) { ignoreFile(); return; } %>\nimport { Link } from '@tanstack/solid-router'\n<% for(const integration of integrations.filter(i => i.type === 'header-user')) { %>\nimport <%= integration.jsName %> from '<%= relativePath(integration.path) %>'\n<% } %>\n\n<%\nconst demoRoutes = [];\nfor (const addOn of addOns) {\n for (const route of (addOn?.routes || []).filter((r) => r.url && r.name)) {\n demoRoutes.push({ url: route.url, name: route.name });\n for (const child of route.children || []) {\n if (child?.url && child?.name) {\n demoRoutes.push({ url: child.url, name: child.name });\n }\n }\n }\n}\nconst userHeaders = integrations.filter((i) => i.type === 'header-user');\n%>\n\nexport default function Header() {\n return (\n
\n \n
\n )\n}\n", "src/main.tsx.ejs": "<% if (!routerOnly) { ignoreFile() } %>\nimport { render } from 'solid-js/web'\nimport { RouterProvider, createRouter } from '@tanstack/solid-router'\nimport { routeTree } from './routeTree.gen'\n\nconst router = createRouter({\n routeTree,\n defaultPreload: 'intent',\n defaultPreloadStaleTime: 0,\n scrollRestoration: true,\n})\n\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: typeof router\n }\n}\n\nconst rootElement = document.getElementById('app')!\n\nif (!rootElement.innerHTML) {\n render(() => , rootElement)\n}\n", "src/router.tsx.ejs": "import { createRouter as createTanStackRouter } from '@tanstack/solid-router'\nimport { routeTree } from './routeTree.gen'\n<% if (addOnEnabled['tanstack-query']) { %>\nimport { getContext } from './integrations/tanstack-query/provider'\n<% } %>\n\nexport function getRouter() {\n const router = createTanStackRouter({\n routeTree,\n<% if (addOnEnabled['tanstack-query']) { %>\n context: getContext(),\n<% } %>\n scrollRestoration: true,\n defaultPreload: 'intent',\n defaultPreloadStaleTime: 0,\n })\n\n return router\n}\n\ndeclare module '@tanstack/solid-router' {\n interface Register {\n router: ReturnType\n }\n}\n", "src/routes/__root.tsx.ejs": "<% const showDevtools = !blank || integrations.some((integration) => integration.type === 'devtools') -%>\n<% if (routerOnly) { %>\nimport { Outlet, createRootRoute } from '@tanstack/solid-router'\n<% if (showDevtools) { %>\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n<% } %>\n\nimport '../styles.css'\n\nexport const Route = createRootRoute({\n component: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n <>\n \n <% if (showDevtools) { %><% } %>\n \n )\n}\n<% } else if (!includeExamples || blank) { %>\nimport { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/solid-router'\n<% if (showDevtools) { %>\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n<% } %>\n\n<% if (addOnEnabled['solid-ui']) { %>\nimport \"@fontsource/inter/400.css\"\n<% } %>\n\nimport { HydrationScript } from 'solid-js/web'\nimport { Suspense } from 'solid-js'\n\n<% for(const addOn of addOns) {\n for(const init of addOn.main?.initialize || []) { %>\n <%- init %>\n<% } } %>\n\nimport styleCss from \"../styles.css?url\";\n\nexport const Route = createRootRouteWithContext()({\n head: () => ({\n links: [{ rel: \"stylesheet\", href: styleCss }],\n }),\n shellComponent: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n \n \n \n \n \n \n \n \n <% if (showDevtools) { %><% } %>\n <% for(const integration of integrations.filter(i => i.type === 'layout')) { %>\n <<%= integration.jsName %> />\n <% } %>\n \n \n \n \n );\n}\n<% } else { %>\nimport { HeadContent, Outlet, Scripts, createRootRouteWithContext } from '@tanstack/solid-router'\nimport { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'\n\n<% if (addOnEnabled['solid-ui']) { %>\nimport \"@fontsource/inter/400.css\"\n<% } %>\n\nimport { HydrationScript } from 'solid-js/web'\nimport { Suspense } from 'solid-js'\n\nimport Header from '../components/Header'\n<% for(const addOn of addOns) {\n for(const init of addOn.main?.initialize || []) { %>\n <%- init %>\n<% } } %>\n\nimport styleCss from \"../styles.css?url\";\n\nexport const Route = createRootRouteWithContext()({\n head: () => ({\n links: [{ rel: \"stylesheet\", href: styleCss }],\n }),\n shellComponent: RootComponent,\n})\n\nfunction RootComponent() {\n return (\n \n \n \n \n \n \n \n
\n \n \n <% for(const integration of integrations.filter(i => i.type === 'layout')) { %>\n <<%= integration.jsName %> />\n <% } %>\n \n \n \n \n );\n}\n<% } %>\n", "src/routes/about.tsx.ejs": "<% if (!includeExamples) { ignoreFile(); return; } %>\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/about')({\n component: About,\n})\n\nfunction About() {\n return (\n
\n
\n

About

\n

\n A small starter with room to grow.\n

\n

\n TanStack Start gives you type-safe routing, server functions, and modern SSR defaults.\n Use this as a clean foundation, then layer in your own routes, styling, and add-ons.\n

\n
\n
\n )\n}\n", "src/routes/index.tsx.ejs": "<% if (blank) { %>\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({ component: Home })\n\nfunction Home() {\n return (\n
\n

Welcome to TanStack Start

\n

\n Edit src/routes/index.tsx to get started.\n

\n
\n )\n}\n<% } else if (!includeExamples) { %>\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({ component: Home })\n\nfunction Home() {\n return (\n
\n

Welcome to TanStack Start

\n

\n Edit src/routes/index.tsx to get started.\n

\n
\n )\n}\n<% } else { %>\nimport { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/')({ component: App })\n\nfunction App() {\n return (\n
\n
\n
\n
\n

TanStack Start Base Template

\n

\n Start simple, ship quickly.\n

\n

\n This base starter intentionally keeps things light: two routes, clean structure,\n and the essentials you need to build from scratch.\n

\n
\n \n About This Starter\n \n \n Router Guide\n \n
\n
\n\n
\n {[\n ['Type-Safe Routing', 'Routes and links stay in sync across every page.'],\n ['Server Functions', 'Call server code from your UI without creating API boilerplate.'],\n ['Streaming by Default', 'Ship progressively rendered responses for faster experiences.'],\n ['Tailwind Native', 'Design quickly with utility-first styling and reusable tokens.'],\n ].map(([title, desc], index) => (\n \n

{title}

\n

{desc}

\n \n ))}\n
\n\n
\n

Quick Start

\n
    \n
  • Edit src/routes/index.tsx to customize the home page.
  • \n
  • \n Update src/components/Header.tsx for navigation and product links.\n
  • \n
  • \n Add routes in src/routes and tweak visual tokens in src/styles.css.\n
  • \n
\n
\n
\n )\n}\n<% } %>\n", "src/styles.css.ejs": "<% if (blank) { %>\n<% if (tailwind) { %>@import 'tailwindcss';\n\n<% } %>\n* {\n box-sizing: border-box;\n}\n\nbody {\n margin: 0;\n font-family: system-ui, sans-serif;\n}\n\nmain {\n width: min(42rem, calc(100% - 2rem));\n margin: 4rem auto;\n}\n<% } else if (!includeExamples) { %>\n@import 'tailwindcss';\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody,\n#app {\n min-height: 100%;\n}\n\nbody {\n margin: 0;\n}\n<% } else { %>\n@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap');\n@import 'tailwindcss';\n\n:root {\n --sea-ink: #173a40;\n --sea-ink-soft: #416166;\n --lagoon: #4fb8b2;\n --lagoon-deep: #328f97;\n --palm: #2f6a4a;\n --sand: #e7f0e8;\n --foam: #f3faf5;\n --surface: rgba(255, 255, 255, 0.74);\n --surface-strong: rgba(255, 255, 255, 0.9);\n --line: rgba(23, 58, 64, 0.14);\n --inset-glint: rgba(255, 255, 255, 0.82);\n --kicker: rgba(47, 106, 74, 0.9);\n --bg-base: #e7f3ec;\n --header-bg: rgba(251, 255, 248, 0.84);\n --chip-bg: rgba(255, 255, 255, 0.8);\n --chip-line: rgba(47, 106, 74, 0.18);\n --link-bg-hover: rgba(255, 255, 255, 0.9);\n --hero-a: rgba(79, 184, 178, 0.36);\n --hero-b: rgba(47, 106, 74, 0.2);\n}\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody,\n#app {\n min-height: 100%;\n}\n\nbody {\n margin: 0;\n color: var(--sea-ink);\n font-family: 'Manrope', ui-sans-serif, system-ui, sans-serif;\n background-color: var(--bg-base);\n background:\n radial-gradient(1100px 620px at -8% -10%, var(--hero-a), transparent 58%),\n radial-gradient(1050px 620px at 112% -12%, var(--hero-b), transparent 62%),\n radial-gradient(720px 380px at 50% 115%, rgba(79, 184, 178, 0.1), transparent 68%),\n linear-gradient(\n 180deg,\n color-mix(in oklab, var(--sand) 68%, white) 0%,\n var(--foam) 44%,\n var(--bg-base) 100%\n );\n overflow-x: hidden;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\na {\n color: var(--lagoon-deep);\n text-decoration-color: rgba(50, 143, 151, 0.4);\n text-decoration-thickness: 1px;\n text-underline-offset: 2px;\n}\n\na:hover {\n color: #246f76;\n}\n\ncode {\n font-size: 0.9em;\n border: 1px solid var(--line);\n background: color-mix(in oklab, var(--surface-strong) 82%, white 18%);\n border-radius: 7px;\n padding: 2px 7px;\n}\n\n.page-wrap {\n width: min(1080px, calc(100% - 2rem));\n margin-inline: auto;\n}\n\n.display-title {\n font-family: 'Fraunces', Georgia, serif;\n}\n\n.island-shell {\n border: 1px solid var(--line);\n background: linear-gradient(165deg, var(--surface-strong), var(--surface));\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 22px 44px rgba(30, 90, 72, 0.1),\n 0 6px 18px rgba(23, 58, 64, 0.08);\n backdrop-filter: blur(4px);\n}\n\n.feature-card {\n background: linear-gradient(\n 165deg,\n color-mix(in oklab, var(--surface-strong) 93%, white 7%),\n var(--surface)\n );\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 18px 34px rgba(30, 90, 72, 0.1),\n 0 4px 14px rgba(23, 58, 64, 0.06);\n}\n\n.demo-page {\n width: min(1080px, calc(100% - 2rem));\n margin-inline: auto;\n padding-block: clamp(2.25rem, 6vw, 4.5rem);\n color: var(--sea-ink);\n}\n\n.demo-page-wide {\n width: min(1280px, calc(100% - 2rem));\n}\n\n.demo-center {\n min-height: calc(100vh - 13rem);\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.demo-panel,\n.demo-card {\n border: 1px solid var(--line);\n background: linear-gradient(165deg, var(--surface-strong), var(--surface));\n box-shadow:\n 0 1px 0 var(--inset-glint) inset,\n 0 18px 34px rgba(30, 90, 72, 0.1),\n 0 4px 14px rgba(23, 58, 64, 0.06);\n backdrop-filter: blur(4px);\n}\n\n.demo-panel {\n border-radius: 1.25rem;\n padding: clamp(1.25rem, 4vw, 2rem);\n}\n\n.demo-card {\n border-radius: 1rem;\n padding: 1rem;\n}\n\n.demo-title {\n margin: 0;\n color: var(--sea-ink);\n font-size: clamp(1.75rem, 4vw, 2.75rem);\n line-height: 1.05;\n font-weight: 800;\n letter-spacing: 0;\n}\n\n.demo-section-title {\n margin: 0;\n color: var(--sea-ink);\n font-size: 1rem;\n font-weight: 700;\n}\n\n.demo-muted {\n color: var(--sea-ink-soft);\n}\n\n.demo-input,\n.demo-select,\n.demo-textarea {\n width: 100%;\n border: 1px solid var(--line);\n border-radius: 0.75rem;\n background: color-mix(in oklab, var(--surface-strong) 88%, white 12%);\n color: var(--sea-ink);\n outline: none;\n transition: border-color 180ms ease, box-shadow 180ms ease, background-color 180ms ease;\n}\n\n.demo-input,\n.demo-select {\n padding: 0.7rem 0.9rem;\n}\n\n.demo-input-fit {\n width: auto;\n}\n\n.demo-textarea {\n min-height: 7rem;\n padding: 0.8rem 0.9rem;\n resize: vertical;\n}\n\n.demo-input:focus,\n.demo-select:focus,\n.demo-textarea:focus {\n border-color: color-mix(in oklab, var(--lagoon-deep) 58%, var(--line));\n box-shadow: 0 0 0 3px color-mix(in oklab, var(--lagoon) 24%, transparent);\n}\n\n.demo-button {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: 0.5rem;\n border: 1px solid color-mix(in oklab, var(--lagoon-deep) 34%, var(--line));\n border-radius: 0.85rem;\n background: color-mix(in oklab, var(--lagoon) 22%, var(--surface-strong));\n color: var(--sea-ink);\n padding: 0.72rem 1rem;\n font-size: 0.9rem;\n font-weight: 700;\n line-height: 1;\n cursor: pointer;\n}\n\n.demo-button:hover {\n transform: translateY(-1px);\n background: color-mix(in oklab, var(--lagoon) 30%, var(--surface-strong));\n}\n\n.demo-button:disabled {\n cursor: not-allowed;\n opacity: 0.55;\n transform: none;\n}\n\n.demo-button-secondary {\n border-color: var(--line);\n background: color-mix(in oklab, var(--surface-strong) 74%, transparent);\n color: var(--sea-ink-soft);\n}\n\n.demo-button-danger {\n border-color: rgba(196, 71, 71, 0.28);\n background: rgba(196, 71, 71, 0.1);\n color: #9f3030;\n}\n\n.demo-list-item {\n border: 1px solid var(--line);\n border-radius: 0.9rem;\n background: color-mix(in oklab, var(--chip-bg) 82%, transparent);\n padding: 0.9rem 1rem;\n}\n\n.demo-pill {\n display: inline-flex;\n align-items: center;\n gap: 0.35rem;\n border: 1px solid var(--chip-line);\n border-radius: 999px;\n background: var(--chip-bg);\n color: var(--sea-ink-soft);\n padding: 0.35rem 0.65rem;\n font-size: 0.75rem;\n font-weight: 700;\n}\n\n.demo-alert {\n border: 1px solid rgba(193, 126, 42, 0.3);\n border-radius: 1rem;\n background: rgba(193, 126, 42, 0.1);\n color: var(--sea-ink);\n padding: 1rem;\n}\n\n.demo-alert-danger {\n border-color: rgba(196, 71, 71, 0.3);\n background: rgba(196, 71, 71, 0.1);\n}\n\n.demo-code-block {\n border: 1px solid var(--line);\n border-radius: 1rem;\n background: color-mix(in oklab, var(--chip-bg) 88%, transparent);\n color: var(--sea-ink);\n padding: 1rem;\n}\n\n.demo-table-shell {\n overflow-x: auto;\n border: 1px solid var(--line);\n border-radius: 1rem;\n background: color-mix(in oklab, var(--surface-strong) 78%, transparent);\n}\n\n.demo-table {\n width: 100%;\n border-collapse: collapse;\n color: var(--sea-ink);\n}\n\n.demo-table th,\n.demo-table td {\n border-bottom: 1px solid var(--line);\n padding: 0.75rem 1rem;\n text-align: left;\n}\n\n.demo-table th {\n background: color-mix(in oklab, var(--chip-bg) 88%, transparent);\n color: var(--sea-ink);\n font-weight: 700;\n}\n\n.demo-table tr:hover td {\n background: color-mix(in oklab, var(--lagoon) 8%, transparent);\n}\n\n.site-header {\n position: sticky;\n top: 0;\n z-index: 50;\n border-bottom: 1px solid var(--line);\n background: var(--header-bg);\n backdrop-filter: blur(10px);\n}\n\n.nav-shell {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 0.5rem 0.75rem;\n padding: 0.75rem 0;\n}\n\n.brand-pill {\n display: inline-flex;\n align-items: center;\n gap: 0.5rem;\n border-radius: 999px;\n border: 1px solid var(--chip-line);\n background: var(--chip-bg);\n padding: 0.5rem 0.875rem;\n color: var(--sea-ink);\n text-decoration: none;\n box-shadow: 0 8px 24px rgba(30, 90, 72, 0.08);\n}\n\n.brand-dot {\n height: 0.5rem;\n width: 0.5rem;\n border-radius: 999px;\n background: linear-gradient(90deg, #56c6be, #7ed3bf);\n}\n\n.island-kicker {\n letter-spacing: 0.16em;\n text-transform: uppercase;\n font-weight: 700;\n font-size: 0.69rem;\n color: var(--kicker);\n}\n\n.nav-link {\n position: relative;\n display: inline-flex;\n align-items: center;\n color: var(--sea-ink-soft);\n text-decoration: none;\n}\n\n.nav-link::after {\n content: '';\n position: absolute;\n left: 0;\n bottom: -6px;\n width: 100%;\n height: 2px;\n transform: scaleX(0);\n transform-origin: left;\n background: linear-gradient(90deg, var(--lagoon), #7ed3bf);\n transition: transform 170ms ease;\n}\n\n.nav-link:hover,\n.nav-link.is-active {\n color: var(--sea-ink);\n}\n\n.nav-link:hover::after,\n.nav-link.is-active::after {\n transform: scaleX(1);\n}\n\n.rise-in {\n animation: rise-in 700ms cubic-bezier(0.16, 1, 0.3, 1) both;\n}\n\n@keyframes rise-in {\n from {\n opacity: 0;\n transform: translateY(12px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n<% } %>\n", "tsconfig.json.ejs": "{\n <% if (addOnEnabled.eslint) { %>\"include\": [\"**/*.ts\", \"**/*.tsx\", \"eslint.config.js\", \"prettier.config.js\", \"vite.config.js\"],\n <% } else { %>\"include\": [\"**/*.ts\", \"**/*.tsx\"],<% } %>\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"jsx\": \"preserve\",\n \"jsxImportSource\": \"solid-js\",\n \"module\": \"ESNext\",\n \"lib\": [\"ES2022\", \"DOM\", \"DOM.Iterable\"],\n \"types\": [\"vite/client\"],\n\n /* Bundler mode */\n \"moduleResolution\": \"bundler\",\n \"allowImportingTsExtensions\": true,\n \"verbatimModuleSyntax\": true,\n \"noEmit\": true,\n\n /* Linting */\n \"skipLibCheck\": true,\n \"strict\": true,\n \"noUnusedLocals\": true,\n \"noUnusedParameters\": true,\n \"noFallthroughCasesInSwitch\": true,\n \"noUncheckedSideEffectImports\": true<% if (addOnEnabled['solid-ui']) { %>,\n \"paths\": {\n \"~/*\": [\"./src/*\"]\n }\n <% } %>\n }\n}\n", "tsr.config.json": "{\n \"target\": \"solid\"\n}\n", "vite.config.ts.ejs": "import { defineConfig } from \"vite\";\n<% const showDevtools = !blank || integrations.some((integration) => integration.type === 'devtools') -%>\n<% if (showDevtools) { -%>\nimport { devtools } from '@tanstack/devtools-vite'\n<% } -%>\n<% if (tailwind) { -%>\nimport tailwindcss from \"@tailwindcss/vite\"\n<% } -%>\n<% if (routerOnly) { %>\nimport { tanstackRouter } from '@tanstack/router-plugin/vite'\n<% } else { %>\nimport { tanstackStart } from \"@tanstack/solid-start/plugin/vite\";\n<% } %>\nimport solidPlugin from 'vite-plugin-solid';\n<% for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { %><%- integrationImportContent(integration) %>\n<% } %>\n\nexport default defineConfig({\n resolve: { tsconfigPaths: true },\n plugins: [<% if (showDevtools) { %>devtools(), <% } %><% for(const integration of integrations.filter(i => i.type === 'vite-plugin')) { %><%- integrationImportCode(integration) %>,<% } %>\n <% if (tailwind) { %>tailwindcss(),<% } %>\n <% if (routerOnly) { %>tanstackRouter({ target: 'solid', autoCodeSplitting: true }),<% } else { %>tanstackStart(),<% } %>\n solidPlugin(<% if (!routerOnly) { %>{ ssr: true }<% } %>),\n ],\n})\n" }, "basePackageJSON": { "name": "app-ts", "private": true, "type": "module", "scripts": { "dev": "vite dev --port 3000", "generate-routes": "tsr generate", "build": "vite build", "start": "node .output/server/index.mjs", "preview": "vite preview" }, "dependencies": { "@tanstack/solid-router": "latest", "@tanstack/solid-start": "latest", "solid-js": "^1.9.12" }, "devDependencies": { "vite-plugin-solid": "^2.11.12", "vite": "^8.0.0" }, "pnpm": { "onlyBuiltDependencies": [ "esbuild", "lightningcss" ] } }, "optionalPackages": { "typescript": { "devDependencies": { "typescript": "^6.0.2" } }, "tailwindcss": { "dependencies": { "@tailwindcss/vite": "^4.1.18", "tailwindcss": "^4.1.18" } }, "devtools": { "dependencies": { "@tanstack/solid-router-devtools": "latest" }, "devDependencies": { "@tanstack/devtools-vite": "latest" } }, "file-router": { "devDependencies": { "@tanstack/router-cli": "^1.133.21" } } }, "addOns": [ { "name": "Better Auth", "description": "Self-hosted user accounts and sessions (open source, full control).", "phase": "add-on", "type": "add-on", "category": "auth", "exclusive": [ "auth" ], "color": "#000000", "link": "https://www.better-auth.com/", "modes": [ "file-router" ], "routes": [ { "url": "/demo/better-auth", "name": "Better Auth", "path": "src/routes/demo.better-auth.tsx", "jsName": "BetterAuthDemo" } ], "integrations": [ { "type": "header-user", "jsName": "BetterAuthHeader", "path": "src/integrations/better-auth/header-user.tsx" } ], "id": "better-auth", "version": "0.0.0", "packageAdditions": { "dependencies": { "better-auth": "^1.5.3" } }, "readme": "## Setting up Better Auth\n\n1. Generate and set the `BETTER_AUTH_SECRET` environment variable in your `.env.local`:\n\n ```bash\n <%- getPackageManagerExecuteScript('@better-auth/cli', ['secret']) %>\n ```\n\n2. Visit the [Better Auth documentation](https://www.better-auth.com) to unlock the full potential of authentication in your app.\n\n### Adding a Database (Optional)\n\nBetter Auth can work in stateless mode, but to persist user data, add a database:\n\n```typescript\n// src/lib/auth.ts\nimport { betterAuth } from \"better-auth\";\nimport { Pool } from \"pg\";\n\nexport const auth = betterAuth({\n database: new Pool({\n connectionString: process.env.DATABASE_URL,\n }),\n // ... rest of config\n});\n```\n\nThen run migrations:\n\n```bash\n<%- getPackageManagerExecuteScript('@better-auth/cli', ['migrate']) %>\n```\n", "readmeIsEjs": true, "files": { "_dot_env.local.append.ejs": "# Better Auth configuration\nBETTER_AUTH_URL=http://localhost:3000\nBETTER_AUTH_SECRET= # Generate a secret key: `<%- getPackageManagerExecuteScript('@better-auth/cli', ['secret']) %>`\n", "src/integrations/better-auth/header-user.tsx.ejs": "import { Show } from \"solid-js\";\n<%_ if (routes.some(r => r.url === '/demo/better-auth')) { _%>\nimport { Link } from \"@tanstack/solid-router\";\n<%_ } _%>\nimport { authClient } from \"../../lib/auth-client\";\n\nexport default function BetterAuthHeader() {\n const session = authClient.useSession();\n\n return (\n \n }\n >\n r.url === '/demo/better-auth')) { _%>\n fallback={\n \n Sign in\n \n }\n<%_ } _%>\n >\n {(user) => (\n
\n \n \n {user().name?.charAt(0).toUpperCase() || \"U\"}\n \n
\n }\n >\n {(image) => \"\"}\n \n {\n void authClient.signOut();\n }}\n class=\"flex-1 h-9 px-4 text-sm font-medium bg-white dark:bg-neutral-900 text-neutral-900 dark:text-neutral-50 border border-neutral-300 dark:border-neutral-700 hover:bg-neutral-50 dark:hover:bg-neutral-800 transition-colors\"\n >\n Sign out\n \n
\n )}\n \n \n );\n}\n", "src/lib/auth-client.ts": "import { createAuthClient } from 'better-auth/solid'\n\nexport const authClient = createAuthClient()\n", "src/lib/auth.ts": "import { betterAuth } from 'better-auth'\nimport { tanstackStartCookies } from 'better-auth/tanstack-start'\n\nexport const auth = betterAuth({\n emailAndPassword: {\n enabled: true,\n },\n plugins: [tanstackStartCookies()],\n})\n", "src/routes/api/auth/$.ts": "import { createFileRoute } from '@tanstack/solid-router'\nimport { auth } from '../../../lib/auth'\n\nexport const Route = createFileRoute('/api/auth/$')({\n server: {\n handlers: {\n GET: ({ request }) => auth.handler(request),\n POST: ({ request }) => auth.handler(request),\n },\n },\n})\n", "src/routes/demo.better-auth.tsx": "import { createFileRoute } from '@tanstack/solid-router'\nimport { createSignal, Show } from 'solid-js'\nimport { authClient } from '../lib/auth-client'\n\nexport const Route = createFileRoute('/demo/better-auth')({\n component: BetterAuthDemo,\n})\n\nfunction BetterAuthDemo() {\n const session = authClient.useSession()\n const [isSignUp, setIsSignUp] = createSignal(false)\n const [email, setEmail] = createSignal('')\n const [password, setPassword] = createSignal('')\n const [name, setName] = createSignal('')\n const [error, setError] = createSignal('')\n const [loading, setLoading] = createSignal(false)\n\n const handleSubmit = async (e: Event) => {\n e.preventDefault()\n setError('')\n setLoading(true)\n\n try {\n if (isSignUp()) {\n const result = await authClient.signUp.email({\n email: email(),\n password: password(),\n name: name(),\n })\n if (result.error) {\n setError(result.error.message || 'Sign up failed')\n }\n } else {\n const result = await authClient.signIn.email({\n email: email(),\n password: password(),\n })\n if (result.error) {\n setError(result.error.message || 'Sign in failed')\n }\n }\n } catch (err) {\n setError('An unexpected error occurred')\n } finally {\n setLoading(false)\n }\n }\n\n return (\n \n
\n \n }\n >\n \n
\n

Better Auth

\n

\n {isSignUp() ? 'Create an account' : 'Sign in'}\n

\n

\n {isSignUp()\n ? 'Enter your information to create an account'\n : 'Enter your email below to login to your account'}\n

\n\n
\n \n
\n \n setName(e.currentTarget.value)}\n class=\"demo-input\"\n required\n />\n
\n
\n\n
\n \n setEmail(e.currentTarget.value)}\n class=\"demo-input\"\n required\n />\n
\n\n
\n \n Password\n \n setPassword(e.currentTarget.value)}\n class=\"demo-input\"\n required\n minLength={8}\n />\n
\n\n \n
\n

{error()}

\n
\n
\n\n \n \n \n Please wait\n \n }\n >\n {isSignUp() ? 'Create account' : 'Sign in'}\n \n \n \n\n
\n {\n setIsSignUp(!isSignUp())\n setError('')\n }}\n class=\"demo-muted text-sm transition-colors hover:text-[var(--sea-ink)]\"\n >\n {isSignUp()\n ? 'Already have an account? Sign in'\n : \"Don't have an account? Sign up\"}\n \n
\n\n

\n Built with{' '}\n \n BETTER-AUTH\n \n .\n

\n
\n \n }\n >\n {(user) => (\n
\n
\n
\n

Better Auth

\n

Welcome back

\n

\n You're signed in as {user().email}\n

\n
\n\n
\n \n \n {user().name?.charAt(0).toUpperCase() || 'U'}\n \n
\n }\n >\n {(image) => \"\"}\n \n
\n

{user().name}

\n

\n {user().email}\n

\n
\n
\n\n {\n void authClient.signOut()\n }}\n class=\"demo-button demo-button-secondary w-full\"\n >\n Sign out\n \n\n

\n Built with{' '}\n \n BETTER-AUTH\n \n .\n

\n \n \n )}\n \n \n )\n}\n" }, "deletedFiles": [], "smallLogo": "\n\n\n\n\n\n\n\n\n" }, { "name": "Convex", "description": "Reactive document database with real-time queries and serverless functions.", "link": "https://convex.dev", "phase": "add-on", "type": "add-on", "category": "database", "exclusive": [ "database", "orm" ], "color": "#EE342F", "modes": [ "file-router" ], "routes": [ { "url": "/demo/convex", "name": "Convex", "path": "src/routes/demo.convex.tsx", "jsName": "ConvexDemo" } ], "integrations": [ { "type": "provider", "path": "src/integrations/convex/provider.tsx", "jsName": "ConvexProvider" } ], "id": "convex", "version": "0.0.0", "packageAdditions": { "dependencies": { "convex": "^1.32.0", "convex-solidjs": "^0.0.3", "lucide-solid": "^0.577.0" } }, "readme": "## Setting up Convex\n\n- Set the `VITE_CONVEX_URL` and `CONVEX_DEPLOYMENT` environment variables in your `.env.local`. (Or run `<%- getPackageManagerExecuteScript('convex', ['init']) %>` to set them automatically.)\n- Run `<%- getPackageManagerExecuteScript('convex', ['dev']) %>` to start the Convex server.\n", "readmeIsEjs": true, "files": { "_dot_cursorrules.append": "This document serves as some special instructions when working with Convex.\n\n# Schemas\n\nWhen designing the schema please see this page on built in System fields and data types available: https://docs.convex.dev/database/types\n\nHere are some specifics that are often mishandled:\n\n## v (https://docs.convex.dev/api/modules/values#v)\n\nThe validator builder.\n\nThis builder allows you to build validators for Convex values.\n\nValidators can be used in schema definitions and as input validators for Convex functions.\n\nType declaration\nName\tType\nid\t(tableName: TableName) => VId, \"required\">\nnull\t() => VNull\nnumber\t() => VFloat64\nfloat64\t() => VFloat64\nbigint\t() => VInt64\nint64\t() => VInt64\nboolean\t() => VBoolean\nstring\t() => VString\nbytes\t() => VBytes\nliteral\t(literal: T) => VLiteral\narray\t(element: T) => VArray\nobject\t(fields: T) => VObject, undefined> } & { [Property in string | number | symbol]: Infer }>, T, \"required\", { [Property in string | number | symbol]: Property | `${Property & string}.${T[Property][\"fieldPaths\"]}` }[keyof T] & string>\nrecord\t(keys: Key, values: Value) => VRecord, Value[\"type\"]>, Key, Value, \"required\", string>\nunion\t(...members: T) => VUnion\nany\t() => VAny\noptional\t(value: T) => VOptional\n\n## System fields (https://docs.convex.dev/database/types#system-fields)\n\nEvery document in Convex has two automatically-generated system fields:\n\n_id: The document ID of the document.\n_creationTime: The time this document was created, in milliseconds since the Unix epoch.\n\nYou do not need to add indices as these are added automatically.\n\n## Example Schema\n\nThis is an example of a well crafted schema.\n\n```ts\nimport { defineSchema, defineTable } from \"convex/server\";\nimport { v } from \"convex/values\";\n\nexport default defineSchema(\n {\n users: defineTable({\n name: v.string(),\n }),\n \n sessions: defineTable({\n userId: v.id(\"users\"),\n sessionId: v.string(),\n }).index(\"sessionId\", [\"sessionId\"]),\n \n threads: defineTable({\n uuid: v.string(),\n summary: v.optional(v.string()),\n summarizer: v.optional(v.id(\"_scheduled_functions\")),\n }).index(\"uuid\", [\"uuid\"]),\n\n messages: defineTable({\n message: v.string(),\n threadId: v.id(\"threads\"),\n author: v.union(\n v.object({\n role: v.literal(\"system\"),\n }),\n v.object({\n role: v.literal(\"assistant\"),\n context: v.array(v.id(\"messages\")),\n model: v.optional(v.string()),\n }),\n v.object({\n role: v.literal(\"user\"),\n userId: v.id(\"users\"),\n }),\n ),\n })\n .index(\"threadId\", [\"threadId\"]),\n },\n);\n```\n\nSourced from: https://github.com/PatrickJS/awesome-cursorrules/blob/main/rules/convex-cursorrules-prompt-file/.cursorrules\n", "_dot_env.local.append": "# Convex configuration, get this URL from your [Dashboard](dashboard.convex.dev)\nCONVEX_DEPLOYMENT=\nVITE_CONVEX_URL=\n", "convex/_generated/api.d.ts": "/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type {\n ApiFromModules,\n FilterApi,\n FunctionReference,\n} from \"convex/server\";\nimport type * as todos from \"../todos.js\";\n\n/**\n * A utility for referencing Convex functions in your app's API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\ndeclare const fullApi: ApiFromModules<{\n todos: typeof todos;\n}>;\nexport declare const api: FilterApi<\n typeof fullApi,\n FunctionReference\n>;\nexport declare const internal: FilterApi<\n typeof fullApi,\n FunctionReference\n>;\n", "convex/_generated/api.js": "/* eslint-disable */\n/**\n * Generated `api` utility.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport { anyApi } from \"convex/server\";\n\n/**\n * A utility for referencing Convex functions in your app's API.\n *\n * Usage:\n * ```js\n * const myFunctionReference = api.myModule.myFunction;\n * ```\n */\nexport const api = anyApi;\nexport const internal = anyApi;\n", "convex/_generated/dataModel.d.ts": "/* eslint-disable */\n/**\n * Generated data model types.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport type {\n DataModelFromSchemaDefinition,\n DocumentByName,\n TableNamesInDataModel,\n SystemTableNames,\n} from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\nimport schema from \"../schema.js\";\n\n/**\n * The names of all of your Convex tables.\n */\nexport type TableNames = TableNamesInDataModel;\n\n/**\n * The type of a document stored in Convex.\n *\n * @typeParam TableName - A string literal type of the table name (like \"users\").\n */\nexport type Doc = DocumentByName<\n DataModel,\n TableName\n>;\n\n/**\n * An identifier for a document in Convex.\n *\n * Convex documents are uniquely identified by their `Id`, which is accessible\n * on the `_id` field. To learn more, see [Document IDs](https://docs.convex.dev/using/document-ids).\n *\n * Documents can be loaded using `db.get(id)` in query and mutation functions.\n *\n * IDs are just strings at runtime, but this type can be used to distinguish them from other\n * strings when type checking.\n *\n * @typeParam TableName - A string literal type of the table name (like \"users\").\n */\nexport type Id =\n GenericId;\n\n/**\n * A type describing your Convex data model.\n *\n * This type includes information about what tables you have, the type of\n * documents stored in those tables, and the indexes defined on them.\n *\n * This type is used to parameterize methods like `queryGeneric` and\n * `mutationGeneric` to make them type-safe.\n */\nexport type DataModel = DataModelFromSchemaDefinition;\n", "convex/_generated/server.d.ts": "/* eslint-disable */\n/**\n * Generated utilities for implementing server-side Convex query and mutation functions.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport {\n ActionBuilder,\n HttpActionBuilder,\n MutationBuilder,\n QueryBuilder,\n GenericActionCtx,\n GenericMutationCtx,\n GenericQueryCtx,\n GenericDatabaseReader,\n GenericDatabaseWriter,\n} from \"convex/server\";\nimport type { DataModel } from \"./dataModel.js\";\n\n/**\n * Define a query in this Convex app's public API.\n *\n * This function will be allowed to read your Convex database and will be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport declare const query: QueryBuilder;\n\n/**\n * Define a query that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to read from your Convex database. It will not be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport declare const internalQuery: QueryBuilder;\n\n/**\n * Define a mutation in this Convex app's public API.\n *\n * This function will be allowed to modify your Convex database and will be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport declare const mutation: MutationBuilder;\n\n/**\n * Define a mutation that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to modify your Convex database. It will not be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport declare const internalMutation: MutationBuilder;\n\n/**\n * Define an action in this Convex app's public API.\n *\n * An action is a function which can execute any JavaScript code, including non-deterministic\n * code and code with side-effects, like calling third-party services.\n * They can be run in Convex's JavaScript environment or in Node.js using the \"use node\" directive.\n * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.\n *\n * @param func - The action. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped action. Include this as an `export` to name it and make it accessible.\n */\nexport declare const action: ActionBuilder;\n\n/**\n * Define an action that is only accessible from other Convex functions (but not from the client).\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped function. Include this as an `export` to name it and make it accessible.\n */\nexport declare const internalAction: ActionBuilder;\n\n/**\n * Define an HTTP action.\n *\n * This function will be used to respond to HTTP requests received by a Convex\n * deployment if the requests matches the path and method where this action\n * is routed. Be sure to route your action in `convex/http.js`.\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped function. Import this function from `convex/http.js` and route it to hook it up.\n */\nexport declare const httpAction: HttpActionBuilder;\n\n/**\n * A set of services for use within Convex query functions.\n *\n * The query context is passed as the first argument to any Convex query\n * function run on the server.\n *\n * This differs from the {@link MutationCtx} because all of the services are\n * read-only.\n */\nexport type QueryCtx = GenericQueryCtx;\n\n/**\n * A set of services for use within Convex mutation functions.\n *\n * The mutation context is passed as the first argument to any Convex mutation\n * function run on the server.\n */\nexport type MutationCtx = GenericMutationCtx;\n\n/**\n * A set of services for use within Convex action functions.\n *\n * The action context is passed as the first argument to any Convex action\n * function run on the server.\n */\nexport type ActionCtx = GenericActionCtx;\n\n/**\n * An interface to read from the database within Convex query functions.\n *\n * The two entry points are {@link DatabaseReader.get}, which fetches a single\n * document by its {@link Id}, or {@link DatabaseReader.query}, which starts\n * building a query.\n */\nexport type DatabaseReader = GenericDatabaseReader;\n\n/**\n * An interface to read from and write to the database within Convex mutation\n * functions.\n *\n * Convex guarantees that all writes within a single mutation are\n * executed atomically, so you never have to worry about partial writes leaving\n * your data in an inconsistent state. See [the Convex Guide](https://docs.convex.dev/understanding/convex-fundamentals/functions#atomicity-and-optimistic-concurrency-control)\n * for the guarantees Convex provides your functions.\n */\nexport type DatabaseWriter = GenericDatabaseWriter;\n", "convex/_generated/server.js": "/* eslint-disable */\n/**\n * Generated utilities for implementing server-side Convex query and mutation functions.\n *\n * THIS CODE IS AUTOMATICALLY GENERATED.\n *\n * To regenerate, run `npx convex dev`.\n * @module\n */\n\nimport {\n actionGeneric,\n httpActionGeneric,\n queryGeneric,\n mutationGeneric,\n internalActionGeneric,\n internalMutationGeneric,\n internalQueryGeneric,\n} from \"convex/server\";\n\n/**\n * Define a query in this Convex app's public API.\n *\n * This function will be allowed to read your Convex database and will be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport const query = queryGeneric;\n\n/**\n * Define a query that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to read from your Convex database. It will not be accessible from the client.\n *\n * @param func - The query function. It receives a {@link QueryCtx} as its first argument.\n * @returns The wrapped query. Include this as an `export` to name it and make it accessible.\n */\nexport const internalQuery = internalQueryGeneric;\n\n/**\n * Define a mutation in this Convex app's public API.\n *\n * This function will be allowed to modify your Convex database and will be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport const mutation = mutationGeneric;\n\n/**\n * Define a mutation that is only accessible from other Convex functions (but not from the client).\n *\n * This function will be allowed to modify your Convex database. It will not be accessible from the client.\n *\n * @param func - The mutation function. It receives a {@link MutationCtx} as its first argument.\n * @returns The wrapped mutation. Include this as an `export` to name it and make it accessible.\n */\nexport const internalMutation = internalMutationGeneric;\n\n/**\n * Define an action in this Convex app's public API.\n *\n * An action is a function which can execute any JavaScript code, including non-deterministic\n * code and code with side-effects, like calling third-party services.\n * They can be run in Convex's JavaScript environment or in Node.js using the \"use node\" directive.\n * They can interact with the database indirectly by calling queries and mutations using the {@link ActionCtx}.\n *\n * @param func - The action. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped action. Include this as an `export` to name it and make it accessible.\n */\nexport const action = actionGeneric;\n\n/**\n * Define an action that is only accessible from other Convex functions (but not from the client).\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument.\n * @returns The wrapped function. Include this as an `export` to name it and make it accessible.\n */\nexport const internalAction = internalActionGeneric;\n\n/**\n * Define a Convex HTTP action.\n *\n * @param func - The function. It receives an {@link ActionCtx} as its first argument, and a `Request` object\n * as its second.\n * @returns The wrapped endpoint function. Route a URL path to this function in `convex/http.js`.\n */\nexport const httpAction = httpActionGeneric;\n", "convex/schema.ts": "import { defineSchema, defineTable } from 'convex/server'\nimport { v } from 'convex/values'\n\nexport default defineSchema({\n products: defineTable({\n title: v.string(),\n imageId: v.string(),\n price: v.number(),\n }),\n todos: defineTable({\n text: v.string(),\n completed: v.boolean(),\n }),\n})\n", "convex/todos.ts": "import { mutation, query } from './_generated/server'\nimport { v } from 'convex/values'\n\nexport const list = query({\n args: {},\n handler: async (ctx) => {\n return await ctx.db\n .query('todos')\n .withIndex('by_creation_time')\n .order('desc')\n .collect()\n },\n})\n\nexport const add = mutation({\n args: { text: v.string() },\n handler: async (ctx, args) => {\n return await ctx.db.insert('todos', {\n text: args.text,\n completed: false,\n })\n },\n})\n\nexport const toggle = mutation({\n args: { id: v.id('todos') },\n handler: async (ctx, args) => {\n const todo = await ctx.db.get(args.id)\n if (!todo) {\n throw new Error('Todo not found')\n }\n return await ctx.db.patch(args.id, {\n completed: !todo.completed,\n })\n },\n})\n\nexport const remove = mutation({\n args: { id: v.id('todos') },\n handler: async (ctx, args) => {\n return await ctx.db.delete(args.id)\n },\n})\n", "convex/tsconfig.json": "{\n /* This TypeScript project config describes the environment that\n * Convex functions run in and is used to typecheck them.\n * You can modify it, but some settings are required to use Convex.\n */\n \"compilerOptions\": {\n /* These settings are not required by Convex and can be modified. */\n \"allowJs\": true,\n \"strict\": true,\n \"moduleResolution\": \"Bundler\",\n \"jsx\": \"react-jsx\",\n \"skipLibCheck\": true,\n \"allowSyntheticDefaultImports\": true,\n\n /* These compiler options are required by Convex */\n \"target\": \"ESNext\",\n \"lib\": [\"ES2021\", \"dom\"],\n \"forceConsistentCasingInFileNames\": true,\n \"module\": \"ESNext\",\n \"isolatedModules\": true,\n \"noEmit\": true\n },\n \"include\": [\"./**/*\"],\n \"exclude\": [\"./_generated\"]\n}\n", "src/integrations/convex/provider.tsx": "import { setupConvex, ConvexProvider } from 'convex-solidjs'\nimport type { JSXElement } from 'solid-js'\n\nconst CONVEX_URL = (import.meta as any).env.VITE_CONVEX_URL\nif (!CONVEX_URL) {\n console.error('missing envar CONVEX_URL')\n}\nconst client = setupConvex(CONVEX_URL)\n\nexport default function AppConvexProvider(props: { children: JSXElement }) {\n return {props.children}\n}\n", "src/routes/demo/convex.tsx": "import { createFileRoute } from '@tanstack/solid-router'\nimport { Trash2, Plus, Check, Circle } from 'lucide-solid'\n\nimport { api } from '../../../convex/_generated/api'\nimport type { Id } from '../../../convex/_generated/dataModel'\nimport { createSignal, For, Show } from 'solid-js'\nimport { useMutation, useQuery } from 'convex-solidjs'\n\nexport const Route = createFileRoute('/demo/convex')({\n ssr: false,\n component: ConvexTodos,\n})\n\nfunction ConvexTodos() {\n const todos = useQuery(api.todos.list, () => ({}))\n const addTodo = useMutation(api.todos.add)\n const toggleTodo = useMutation(api.todos.toggle)\n const removeTodo = useMutation(api.todos.remove)\n\n const [newTodo, setNewTodo] = createSignal('')\n\n const handleAddTodo = async () => {\n if (newTodo().trim()) {\n await addTodo.mutate({ text: newTodo().trim() })\n setNewTodo('')\n }\n }\n\n const handleToggleTodo = async (id: Id<'todos'>) => {\n await toggleTodo.mutate({ id })\n }\n const handleRemoveTodo = async (id: Id<'todos'>) => {\n await removeTodo.mutate({ id })\n }\n\n const completedCount = () =>\n todos?.data()?.filter((todo) => todo.completed).length || 0\n const totalCount = () => todos?.data()?.length || 0\n\n return (\n
\n
\n
\n
\n

Convex

\n

Todos

\n

Powered by real-time sync

\n 0}>\n
\n {completedCount()} completed\n \n {totalCount() - completedCount()} remaining\n \n
\n
\n
\n
\n\n
\n
\n setNewTodo(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n handleAddTodo()\n }\n }}\n placeholder=\"What needs to be done?\"\n class=\"demo-input min-w-0 flex-1\"\n />\n \n \n Add\n \n
\n
\n\n
\n \n
\n
\n

Loading todos...

\n
\n
\n \n \n

No todos yet

\n

\n Add your first todo above to get started!\n

\n
\n }\n >\n
\n \n {(todo, index) => (\n \n handleToggleTodo(todo._id)}\n class={`flex-shrink-0 w-6 h-6 rounded-full border-2 flex items-center justify-center transition-all duration-200 ${\n todo.completed\n ? 'border-[var(--lagoon-deep)] bg-[var(--lagoon)] text-[var(--sea-ink)]'\n : 'border-[var(--line)] text-transparent hover:border-[var(--lagoon-deep)] hover:text-[var(--lagoon-deep)]'\n }`}\n >\n \n \n\n \n {todo.text}\n \n\n handleRemoveTodo(todo._id)}\n class=\"demo-button demo-button-danger flex-shrink-0 p-2\"\n >\n \n \n
\n )}\n \n \n \n \n\n
\n

\n Built with Convex, real-time updates, and synced state.\n

\n
\n \n
\n )\n}\n" }, "deletedFiles": [], "smallLogo": "\n\n\n\n\n" }, { "name": "Form", "description": "TanStack Form", "phase": "add-on", "link": "https://tanstack.com/form/latest", "modes": [ "file-router", "code-router" ], "type": "add-on", "category": "tanstack", "color": "#EAB308", "routes": [ { "url": "/demo/form", "name": "Form", "path": "src/routes/demo.form.tsx", "jsName": "FormDemo" } ], "id": "form", "version": "0.0.0", "packageAdditions": { "dependencies": { "@tanstack/solid-form": "latest" } }, "readmeIsEjs": false, "files": { "src/routes/demo.form.tsx.ejs": "import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/solid-router'\nimport { createForm } from '@tanstack/solid-form'\n\nimport type { JSX } from 'solid-js/jsx-runtime'\nimport type { ValidationError } from '@tanstack/solid-form'\n\n<% if (codeRouter) { %>\nimport type { RootRoute } from '@tanstack/solid-router'\n<% } else { %>\nexport const Route = createFileRoute('/demo/form')({\n component: FormExample,\n})\n<% } %>\nfunction FieldWrapper(props: {\n children: JSX.Element\n errors: Array\n label: string\n}) {\n return (\n
\n \n {props.children}\n {props.errors.length ? (\n
{props.errors.join(', ')}
\n ) : null}\n
\n )\n}\n\nfunction FormExample() {\n const form = createForm(() => ({\n defaultValues: {\n fullName: '',\n email: '',\n address: {\n street: '',\n city: '',\n state: '',\n zipCode: '',\n country: '',\n },\n phone: '',\n },\n onSubmit: ({ value }) => {\n console.log(value)\n // Show success message\n alert('Form submitted successfully!')\n },\n }))\n\n return (\n
\n
\n
\n

TanStack Form

\n

Address Form

\n

\n Nested fields, field-level validation, and a select input.\n

\n
\n {\n e.preventDefault()\n e.stopPropagation()\n form.handleSubmit()\n }}\n class=\"space-y-6\"\n >\n {/* Full Name Field */}\n
\n {\n if (value.trim().length === 0) {\n return 'Full name is required'\n }\n if (value.length < 3) {\n return 'Name must be at least 3 characters'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* Email Field */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Email is required'\n }\n if (!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$/i.test(value)) {\n return 'Invalid email address'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* Street Address */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Street address is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* City, State, Zip in a row */}\n
\n {/* City */}\n {\n if (!value || value.trim().length === 0) {\n return 'City is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n\n {/* State */}\n {\n if (!value || value.trim().length === 0) {\n return 'State is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n\n {/* Zip Code */}\n {\n if (!value || value.trim().length === 0) {\n return 'Zip code is required'\n }\n if (!/^\\d{5}(-\\d{4})?$/.test(value)) {\n return 'Invalid zip code format'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n />\n \n )}\n />\n
\n\n {/* Country */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Country is required'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-select\"\n >\n \n \n \n \n \n \n \n \n \n \n )}\n />\n
\n\n {/* Phone Number */}\n
\n {\n if (!value || value.trim().length === 0) {\n return 'Phone number is required'\n }\n if (\n !/^(\\+\\d{1,3})?\\s?\\(?\\d{3}\\)?[\\s.-]?\\d{3}[\\s.-]?\\d{4}$/.test(\n value,\n )\n ) {\n return 'Invalid phone number format'\n }\n return undefined\n },\n }}\n children={(field) => (\n \n field().handleChange(e.target.value)}\n class=\"demo-input\"\n placeholder=\"(123) 456-7890\"\n />\n \n )}\n />\n
\n\n {/* Submit Button */}\n
\n \n {form.state.isSubmitting ? 'Submitting...' : 'Submit'}\n \n
\n \n
\n
\n )\n}\n\n<% if (codeRouter) { %>\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/form',\n component: FormExample,\n getParentRoute: () => parentRoute,\n})\n<% } %>\n" }, "deletedFiles": [], "smallLogo": "" }, { "name": "Sentry", "phase": "setup", "description": "Add Sentry for error monitoring and crash reporting (requires Start).", "link": "https://sentry.com/", "modes": [ "file-router" ], "type": "add-on", "category": "monitoring", "color": "#362D59", "partner": { "id": "sentry", "tier": "bronze" }, "routes": [ { "url": "/demo/sentry/bad-event-handler", "name": "Sentry", "path": "src/routes/demo.sentry.bad-event-handler.tsx", "jsName": "SentryBadEventHandlerDemo" } ], "id": "sentry", "version": "0.0.0", "packageAdditions": { "dependencies": { "@sentry/solid": "^10.67.0" } }, "readmeIsEjs": false, "files": { "_dot_cursorrules.append": "We use Sentry for watching for errors in our deployed application, as well as for instrumentation of our application.\n\n## Error collection\n\nError collection is automatic and configured in `src/router.tsx`.\n\n## Instrumentation\n\nWe want our server functions intstrumented. So if you see a function name like `createServerFn`, you can instrument it with Sentry. You'll need to import `Sentry`:\n\n```tsx\nimport * as Sentry from '@sentry/browser'\n```\n\nAnd then wrap the implementation of the server function with `Sentry.startSpan`, liks so:\n\n```tsx\nSentry.startSpan({ name: 'Requesting all the pokemon' }, async () => {\n // Some lengthy operation here\n await fetch('https://api.pokemon.com/data/')\n})\n```\n", "_dot_env.local.append": "# Your Sentry DSN (from your Sentry account)\nVITE_SENTRY_DSN=\n", "src/routes/demo.sentry.bad-event-handler.tsx": "import { createFileRoute } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/demo/sentry/bad-event-handler')({\n component: RouteComponent,\n})\n\nfunction RouteComponent() {\n return (\n
\n
\n

Sentry

\n

Error Handler Demo

\n {\n throw new Error('Sentry Frontend Error')\n }}\n class=\"demo-button\"\n >\n Throw error\n \n
\n
\n )\n}\n" }, "deletedFiles": [], "smallLogo": "" }, { "name": "Solid-UI", "description": "Copy-paste accessible UI components for Solid (Tailwind + Radix-style primitives).", "phase": "add-on", "link": "https://ui.shadcn.com/", "modes": [ "file-router", "code-router" ], "type": "add-on", "category": "styling", "tailwind": true, "color": "#000000", "id": "solid-ui", "version": "0.0.0", "packageAdditions": { "dependencies": { "@fontsource/inter": "^5.1.1", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "tailwind-merge": "^3.0.2", "tailwindcss-animate": "^1.0.7" } }, "readme": "## Solid-UI\n\nThis installation of Solid-UI follows the manual instructions but was modified to work with Tailwind V4.\n\nTo install the components, run the following command (this install button):\n\n```bash\n<%- getPackageManagerExecuteScript('solidui-cli@latest', ['add', 'button']) %>\n```\n", "readmeIsEjs": true, "files": { "src/lib/utils.ts": "import { type ClassValue, clsx } from \"clsx\"\nimport { twMerge } from \"tailwind-merge\"\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n", "src/styles.css": "@import 'tailwindcss';\n\n@plugin \"tailwindcss-animate\";\n\n@custom-variant dark (&:is(.dark *));\n\nbody {\n @apply m-0;\n font-family:\n -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu',\n 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;\n -webkit-font-smoothing: antialiased;\n -moz-osx-font-smoothing: grayscale;\n}\n\ncode {\n font-family:\n source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;\n}\n\n:root {\n --background: oklch(1 0 0);\n --foreground: oklch(0.141 0.005 285.823);\n --card: oklch(1 0 0);\n --card-foreground: oklch(0.141 0.005 285.823);\n --popover: oklch(1 0 0);\n --popover-foreground: oklch(0.141 0.005 285.823);\n --primary: oklch(0.21 0.006 285.885);\n --primary-foreground: oklch(0.985 0 0);\n --secondary: oklch(0.967 0.001 286.375);\n --secondary-foreground: oklch(0.21 0.006 285.885);\n --muted: oklch(0.967 0.001 286.375);\n --muted-foreground: oklch(0.552 0.016 285.938);\n --accent: oklch(0.967 0.001 286.375);\n --accent-foreground: oklch(0.21 0.006 285.885);\n --destructive: oklch(0.577 0.245 27.325);\n --destructive-foreground: oklch(0.577 0.245 27.325);\n --border: oklch(0.92 0.004 286.32);\n --input: oklch(0.92 0.004 286.32);\n --ring: oklch(0.871 0.006 286.286);\n --chart-1: oklch(0.646 0.222 41.116);\n --chart-2: oklch(0.6 0.118 184.704);\n --chart-3: oklch(0.398 0.07 227.392);\n --chart-4: oklch(0.828 0.189 84.429);\n --chart-5: oklch(0.769 0.188 70.08);\n --radius: 0.625rem;\n --sidebar: oklch(0.985 0 0);\n --sidebar-foreground: oklch(0.141 0.005 285.823);\n --sidebar-primary: oklch(0.21 0.006 285.885);\n --sidebar-primary-foreground: oklch(0.985 0 0);\n --sidebar-accent: oklch(0.967 0.001 286.375);\n --sidebar-accent-foreground: oklch(0.21 0.006 285.885);\n --sidebar-border: oklch(0.92 0.004 286.32);\n --sidebar-ring: oklch(0.871 0.006 286.286);\n}\n\n.dark {\n --background: oklch(0.141 0.005 285.823);\n --foreground: oklch(0.985 0 0);\n --card: oklch(0.141 0.005 285.823);\n --card-foreground: oklch(0.985 0 0);\n --popover: oklch(0.141 0.005 285.823);\n --popover-foreground: oklch(0.985 0 0);\n --primary: oklch(0.985 0 0);\n --primary-foreground: oklch(0.21 0.006 285.885);\n --secondary: oklch(0.274 0.006 286.033);\n --secondary-foreground: oklch(0.985 0 0);\n --muted: oklch(0.274 0.006 286.033);\n --muted-foreground: oklch(0.705 0.015 286.067);\n --accent: oklch(0.274 0.006 286.033);\n --accent-foreground: oklch(0.985 0 0);\n --destructive: oklch(0.396 0.141 25.723);\n --destructive-foreground: oklch(0.637 0.237 25.331);\n --border: oklch(0.274 0.006 286.033);\n --input: oklch(0.274 0.006 286.033);\n --ring: oklch(0.442 0.017 285.786);\n --chart-1: oklch(0.488 0.243 264.376);\n --chart-2: oklch(0.696 0.17 162.48);\n --chart-3: oklch(0.769 0.188 70.08);\n --chart-4: oklch(0.627 0.265 303.9);\n --chart-5: oklch(0.645 0.246 16.439);\n --sidebar: oklch(0.21 0.006 285.885);\n --sidebar-foreground: oklch(0.985 0 0);\n --sidebar-primary: oklch(0.488 0.243 264.376);\n --sidebar-primary-foreground: oklch(0.985 0 0);\n --sidebar-accent: oklch(0.274 0.006 286.033);\n --sidebar-accent-foreground: oklch(0.985 0 0);\n --sidebar-border: oklch(0.274 0.006 286.033);\n --sidebar-ring: oklch(0.442 0.017 285.786);\n}\n\n@theme inline {\n --color-background: var(--background);\n --color-foreground: var(--foreground);\n --color-card: var(--card);\n --color-card-foreground: var(--card-foreground);\n --color-popover: var(--popover);\n --color-popover-foreground: var(--popover-foreground);\n --color-primary: var(--primary);\n --color-primary-foreground: var(--primary-foreground);\n --color-secondary: var(--secondary);\n --color-secondary-foreground: var(--secondary-foreground);\n --color-muted: var(--muted);\n --color-muted-foreground: var(--muted-foreground);\n --color-accent: var(--accent);\n --color-accent-foreground: var(--accent-foreground);\n --color-destructive: var(--destructive);\n --color-destructive-foreground: var(--destructive-foreground);\n --color-border: var(--border);\n --color-input: var(--input);\n --color-ring: var(--ring);\n --color-chart-1: var(--chart-1);\n --color-chart-2: var(--chart-2);\n --color-chart-3: var(--chart-3);\n --color-chart-4: var(--chart-4);\n --color-chart-5: var(--chart-5);\n --radius-sm: calc(var(--radius) - 4px);\n --radius-md: calc(var(--radius) - 2px);\n --radius-lg: var(--radius);\n --radius-xl: calc(var(--radius) + 4px);\n --color-sidebar: var(--sidebar);\n --color-sidebar-foreground: var(--sidebar-foreground);\n --color-sidebar-primary: var(--sidebar-primary);\n --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);\n --color-sidebar-accent: var(--sidebar-accent);\n --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);\n --color-sidebar-border: var(--sidebar-border);\n --color-sidebar-ring: var(--sidebar-ring);\n}\n\n@layer base {\n * {\n @apply border-border outline-ring/50;\n }\n body {\n @apply bg-background text-foreground;\n }\n}\n", "ui.config.json": "{\n \"$schema\": \"https://solid-ui.com/schema.json\",\n \"tsx\": true,\n \"tailwind\": {\n \"css\": \"src/styles.css\",\n \"config\": \"tailwind.config.cjs\",\n \"prefix\": \"\"\n },\n \"aliases\": {\n \"components\": \"~/components/ui\",\n \"utils\": \"~/lib/utils\"\n }\n}" }, "deletedFiles": [], "smallLogo": "\n \n \n \n" }, { "name": "Store", "description": "Add TanStack Store to your application.", "phase": "add-on", "modes": [ "file-router", "code-router" ], "type": "add-on", "category": "tanstack", "color": "#B89A56", "link": "https://tanstack.com/store/latest", "routes": [ { "url": "/demo/store", "name": "Store", "path": "src/routes/demo.store.tsx", "jsName": "StoreDemo" } ], "id": "store", "version": "0.0.0", "packageAdditions": { "dependencies": { "@tanstack/solid-store": "latest", "@tanstack/store": "latest" } }, "readmeIsEjs": false, "files": { "src/lib/demo-store.ts": "import { Store } from '@tanstack/store'\n\nexport const store = new Store({\n firstName: 'Jane',\n lastName: 'Smith',\n})\n\nexport const fullName = new Store(`${store.state.firstName} ${store.state.lastName}`)\n\nstore.subscribe(() => {\n fullName.setState(() => `${store.state.firstName} ${store.state.lastName}`)\n})\n", "src/routes/demo.store.tsx.ejs": "import { <% if (fileRouter) { %>createFileRoute<% } else { %>createRoute<% } %> } from '@tanstack/solid-router'\nimport { useStore } from '@tanstack/solid-store'\n\nimport { fullName, store } from '../lib/demo-store'\n\n<% if (codeRouter) { %>\nimport type { RootRoute } from '@tanstack/solid-router'\n<% } else { %>\nexport const Route = createFileRoute('/demo/store')({\n component: DemoStore,\n})\n<% } %>\n\nfunction FirstName() {\n const firstName = useStore(store, (state) => state.firstName)\n return (\n \n store.setState((state) => ({ ...state, firstName: e.target.value }))\n }\n class=\"demo-input\"\n />\n )\n}\n\nfunction LastName() {\n const lastName = useStore(store, (state) => state.lastName)\n return (\n \n store.setState((state) => ({ ...state, lastName: e.target.value }))\n }\n class=\"demo-input\"\n />\n )\n}\n\nfunction FullName() {\n const fName = useStore(fullName)\n return (\n
\n {fName()}\n
\n )\n}\n\nfunction DemoStore() {\n return (\n
\n
\n

TanStack Store

\n

Store Example

\n \n \n \n
\n
\n )\n}\n\n<% if (codeRouter) { %>\nexport default (parentRoute: RootRoute) => createRoute({\n path: '/demo/store',\n component: DemoStore,\n getParentRoute: () => parentRoute,\n})\n<% } %>\n \n" }, "deletedFiles": [], "smallLogo": "" }, { "name": "Strapi", "description": "Headless CMS with admin UI (self-hosted, content models in TypeScript).", "link": "https://strapi.io/", "phase": "add-on", "type": "add-on", "category": "cms", "color": "#4945FF", "modes": [ "file-router" ], "routes": [ { "url": "/demo/strapi", "name": "Strapi", "path": "src/routes/demo.strapi.tsx", "jsName": "StrapiDemo" } ], "id": "strapi", "version": "0.0.0", "packageAdditions": { "dependencies": { "@strapi/client": "^1.6.1" } }, "readme": "## Setting up Strapi\n\nThe current setup shows an example of how to use Strapi with an articles collection which is part of the example structure & data.\n\n- Create a local running copy of the strapi admin\n\n```bash\npnpm dlx create-strapi@latest my-strapi-project\ncd my-strapi-project\npnpm dev\n```\n\n- Login and publish the example articles to see them on the strapi demo page.\n- Set the `VITE_STRAPI_URL` environment variable in your `.env.local`. (For local it should be http://localhost:1337/api)\n", "readmeIsEjs": false, "files": { "_dot_env.local.append": "# Strapi configuration\nVITE_STRAPI_URL=\"http://localhost:1337/api\"", "src/lib/strapiClient.ts": "import { strapi } from \"@strapi/client\";\n\nexport const strapiClient = strapi({\n baseURL: import.meta.env.VITE_STRAPI_URL,\n});\n\nexport const articles = strapiClient.collection(\"articles\");\n", "src/routes/demo/strapi.tsx": "import { articles } from '../../lib/strapiClient'\nimport { createFileRoute, Link } from '@tanstack/solid-router'\nimport { Show, For } from 'solid-js'\n\nexport const Route = createFileRoute('/demo/strapi')({\n component: RouteComponent,\n loader: async () => {\n const { data: strapiArticles } = await articles.find()\n return strapiArticles\n },\n})\n\nfunction RouteComponent() {\n const strapiArticles = Route.useLoaderData()\n\n return (\n
\n
\n

CMS

\n

Strapi Articles

\n\n 0}\n fallback={

No articles found.

}\n >\n
\n \n {(article) => (\n \n
\n

\n {article.title || 'Untitled'}\n

\n\n {article.description && (\n

\n {article.description}\n

\n )}\n\n {article.content && (\n

\n {article.content}\n

\n )}\n\n {article.createdAt && (\n

\n {new Date(article.createdAt).toLocaleDateString()}\n

\n )}\n
\n \n )}\n
\n
\n \n
\n
\n )\n}\n", "src/routes/demo/strapi_.$articleId.tsx": "import { articles } from '../../lib/strapiClient'\nimport { createFileRoute, Link } from '@tanstack/solid-router'\n\nexport const Route = createFileRoute('/demo/strapi_/$articleId')({\n component: RouteComponent,\n loader: async ({ params }) => {\n const { data: article } = await articles.findOne(params.articleId)\n return article\n },\n})\n\nfunction RouteComponent() {\n const article = Route.useLoaderData()\n\n return (\n
\n
\n \n \n \n \n Back to Articles\n \n\n
\n

{article()?.title || 'Untitled'}

\n\n {article()?.createdAt && (\n

\n Published on{' '}\n {new Date(\n article()?.createdAt || article()?.attributes?.createdAt,\n ).toLocaleDateString('en-US', {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })}\n

\n )}\n\n {article()?.description && (\n
\n

Description

\n

\n {article()?.description || article()?.attributes?.description}\n

\n
\n )}\n\n {article()?.content && (\n
\n

Content

\n
\n {article()?.content}\n
\n
\n )}\n
\n
\n
\n )\n}\n" }, "deletedFiles": [], "smallLogo": "\n\n\n\n\n\n\n\n" }, { "name": "T3Env", "description": "Validate process.env at build time (catch missing/wrong env vars before runtime).", "phase": "add-on", "link": "https://github.com/t3-oss/t3-env", "type": "add-on", "category": "tooling", "color": "#6366F1", "modes": [ "file-router", "code-router" ], "id": "t3env", "version": "0.0.0", "packageAdditions": { "dependencies": { "@t3-oss/env-core": "^0.13.10", "zod": "^4.3.6" } }, "readme": "## T3Env\n\n- You can use T3Env to add type safety to your environment variables.\n- Add Environment variables to the `src/env.mjs` file.\n- Use the environment variables in your code.\n\n### Usage\n\n```ts\nimport { env } from \"@/env\";\n\nconsole.log(env.VITE_APP_TITLE);\n```\n\n\n\n", "readmeIsEjs": false, "files": { "src/env.ts": "import { createEnv } from \"@t3-oss/env-core\";\nimport { z } from \"zod\";\n \nexport const env = createEnv({\n server: {\n SERVER_URL: z.url().optional(),\n },\n \n /**\n * The prefix that client-side variables must have. This is enforced both at\n * a type-level and at runtime.\n */\n clientPrefix: \"VITE_\",\n \n client: {\n VITE_APP_TITLE: z.string().min(1).optional(),\n },\n \n /**\n * What object holds the environment variables at runtime. This is usually\n * `process.env` or `import.meta.env`.\n */\n runtimeEnv: import.meta.env,\n \n /**\n * By default, this library will feed the environment variables directly to\n * the Zod validator.\n *\n * This means that if you have an empty string for a value that is supposed\n * to be a number (e.g. `PORT=` in a \".env\" file), Zod will incorrectly flag\n * it as a type mismatch violation. Additionally, if you have an empty string\n * for a value that is supposed to be a string with a default value (e.g.\n * `DOMAIN=` in an \".env\" file), the default value will never be applied.\n *\n * In order to solve these issues, we recommend that all new projects\n * explicitly specify this option as true.\n */\n emptyStringAsUndefined: true,\n});\n" }, "deletedFiles": [], "smallLogo": "\n\n\n\n\n\n" }, { "name": "TanStack Query", "description": "Integrate TanStack Query into your application.", "phase": "add-on", "modes": [ "file-router" ], "type": "add-on", "category": "tanstack", "color": "#EF4444", "link": "https://tanstack.com/query/latest", "routes": [ { "url": "/demo/tanstack-query", "name": "TanStack Query", "path": "src/routes/demo.tanstack-query.tsx", "jsName": "TanStackQueryDemo" } ], "integrations": [ { "type": "provider", "path": "src/integrations/tanstack-query/provider.tsx", "jsName": "TanStackQueryProvider" }, { "type": "header-user", "path": "src/integrations/tanstack-query/header-user.tsx", "jsName": "TanStackQueryHeaderUser" } ], "id": "tanstack-query", "version": "0.0.0", "packageAdditions": { "dependencies": { "@tanstack/solid-query": "latest", "@tanstack/solid-query-devtools": "latest" } }, "readmeIsEjs": false, "files": { "src/integrations/tanstack-query/header-user.tsx": "import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'\n\nexport default function AppTanstackQueryHeaderUser() {\n return \n}\n", "src/integrations/tanstack-query/provider.tsx": "import { QueryClient } from '@tanstack/solid-query'\n\nexport function getContext() {\n const queryClient = new QueryClient();\n return {\n queryClient\n };\n}", "src/routes/demo.tanstack-query.tsx": "import { createFileRoute } from '@tanstack/solid-router'\nimport { createQuery } from '@tanstack/solid-query'\n\nexport const Route = createFileRoute('/demo/tanstack-query')({\n component: App,\n})\n\nfunction App() {\n const peopleQuery = createQuery(() => ({\n queryKey: ['people'],\n queryFn: () =>\n Promise.resolve([{ name: 'John Doe' }, { name: 'Jane Doe' }]),\n initialData: [],\n }))\n\n return (\n
\n

People list from Swapi

\n
    {peopleQuery.data?.map((person) =>
  • {person.name}
  • )}
\n
\n )\n}\n\nexport default App\n" }, "deletedFiles": [], "smallLogo": "" }, { "name": "Biome", "description": "Biome toolchain support.", "phase": "setup", "type": "toolchain", "category": "tooling", "exclusive": [ "linter" ], "color": "#60A5FA", "modes": [ "code-router", "file-router" ], "link": "https://biomejs.dev", "id": "biome", "version": "0.0.0", "packageAdditions": { "scripts": { "format": "biome format", "lint": "biome lint", "check": "biome check" }, "devDependencies": { "@biomejs/biome": "2.4.5" } }, "readmeIsEjs": false, "files": { "biome.json.ejs": "{\n \"$schema\": \"https://biomejs.dev/schemas/1.9.4/schema.json\",\n \"vcs\": {\n \"enabled\": false,\n \"clientKind\": \"git\",\n \"useIgnoreFile\": false\n },\n \"files\": {\n \"ignoreUnknown\": false,\n \"ignore\": [\"src/routeTree.gen.ts\"],\n \"include\": [\"src/*\", \".vscode/*\", \"index.html\", \"vite.config.ts\"]\n },\n \"formatter\": {\n \"enabled\": true,\n \"indentStyle\": \"tab\"\n },\n \"organizeImports\": {\n \"enabled\": true\n },\n \"linter\": {\n \"enabled\": true,\n \"rules\": {\n \"recommended\": true\n }\n },\n \"javascript\": {\n \"formatter\": {\n \"quoteStyle\": \"double\"\n }\n }\n}\n" }, "deletedFiles": [], "smallLogo": "\n\n \n \n\n" }, { "name": "ESLint", "description": "ESLint + Prettier toolchain support.", "phase": "setup", "type": "toolchain", "category": "tooling", "exclusive": [ "linter" ], "color": "#4B32C3", "modes": [ "code-router", "file-router" ], "link": "https://eslint.org", "id": "eslint", "version": "0.0.0", "packageAdditions": { "scripts": { "lint": "eslint", "format": "prettier --write . && eslint --fix", "check": "prettier --check ." }, "devDependencies": { "@tanstack/eslint-config": "latest", "eslint": "^9.20.0", "prettier": "^3.8.1" } }, "readmeIsEjs": false, "files": { "_dot_prettierignore": "package-lock.json\npnpm-lock.yaml\nyarn.lock", "eslint.config.js": "// @ts-check\n\nimport { tanstackConfig } from \"@tanstack/eslint-config\";\n\nexport default [\n ...tanstackConfig,\n {\n rules: {\n \"import/no-cycle\": \"off\",\n \"import/order\": \"off\",\n \"sort-imports\": \"off\",\n \"@typescript-eslint/array-type\": \"off\",\n \"@typescript-eslint/require-await\": \"off\",\n \"pnpm/json-enforce-catalog\": \"off\",\n },\n },\n {\n ignores: [\"eslint.config.js\", \"prettier.config.js\"],\n },\n];\n", "prettier.config.js": "// @ts-check\n\n/** @type {import('prettier').Config} */\nconst config = {\n semi: false,\n singleQuote: true,\n trailingComma: \"all\",\n};\n\nexport default config;\n" }, "deletedFiles": [], "smallLogo": "\n\n \n \n \n \n\n" }, { "name": "TanStack Chat", "description": "A chat example that uses TanStack Start and TanStack Store. Features chat with Anthropic Sonnet, chat history and custom prompts.", "phase": "example", "type": "example", "category": "other", "color": "#00D1B2", "modes": [ "file-router" ], "link": "", "routes": [ { "url": "/example/chat", "name": "Chat" } ], "dependsOn": [ "solid-ui", "store" ], "id": "tanchat", "version": "0.0.0", "packageAdditions": { "dependencies": { "highlight.js": "^11.11.1", "lucide-solid": "^0.577.0", "markdown-it": "^14.1.0" } }, "readme": "# TanStack Chat Application\n\nAm example chat application built with TanStack Start, TanStack Store, and Claude AI.\n\n## Sidecar service\n\nThis applicaton requires a sidecar microservice to be running. The server is located in the `ai-streaming-service` directory.\n\nIn that directory you should edit the `.env.local` file to add your Anthropic API key:\n\n```env\nANTHROPIC_API_KEY=your_anthropic_api_key\n```\n\nThen run the server:\n\n```bash\ncd ai-streaming-service\nnpm install\nnpm run dev\n```\n\n## ✨ Features\n\n### AI Capabilities\n\n- 🤖 Powered by Claude 3.5 Sonnet\n- 📝 Rich markdown formatting with syntax highlighting\n- 🎯 Customizable system prompts for tailored AI behavior\n- 🔄 Real-time message updates and streaming responses (coming soon)\n\n### User Experience\n\n- 🎨 Modern UI with Tailwind CSS and Lucide icons\n- 🔍 Conversation management and history\n- 🔐 Secure API key management\n- 📋 Markdown rendering with code highlighting\n\n### Technical Features\n\n- 📦 Centralized state management with TanStack Store\n- 🔌 Extensible architecture for multiple AI providers\n- 🛠️ TypeScript for type safety\n\n## Architecture\n\n### Tech Stack\n\n- **Routing**: TanStack Router\n- **State Management**: TanStack Store\n- **Styling**: Tailwind CSS\n- **AI Integration**: Anthropic's Claude API\n", "readmeIsEjs": false, "files": { "ai-streaming-server/README.md": "# AI Streaming Server\n\nAn Express server with TypeScript that provides a streaming API endpoint for Anthropic's Claude AI model.\n\n## Setup\n\n1. Clone the repository\n2. Install dependencies:\n ```bash\n npm install\n ```\n3. Copy `.env.example` to `.env` and add your Anthropic API key:\n ```bash\n cp .env.example .env\n ```\n4. Edit `.env` and replace `your_api_key_here` with your actual Anthropic API key\n\n## Development\n\nRun in development mode with auto-reload:\n\n```bash\nnpm run dev\n```\n\nType checking:\n\n```bash\nnpm run typecheck\n```\n\n## Production\n\nBuild the TypeScript code:\n\n```bash\nnpm run build\n```\n\nRun in production mode:\n\n```bash\nnpm start\n```\n\n## API Usage\n\n### POST /api/chat\n\nEndpoint that accepts chat messages and returns a streaming response from Claude.\n\n#### Request Body\n\n```typescript\ninterface ChatMessage {\n role: \"user\" | \"assistant\";\n content: string;\n}\n\ninterface ChatRequest {\n messages: ChatMessage[];\n}\n```\n\nExample request:\n\n```json\n{\n \"messages\": [{ \"role\": \"user\", \"content\": \"Hello, how are you?\" }]\n}\n```\n\n#### Response\n\nThe endpoint returns a Server-Sent Events (SSE) stream. Each event contains a chunk of the response from Claude.\n\nExample usage with JavaScript/TypeScript:\n\n```typescript\nconst response = await fetch(\"http://localhost:3000/api/chat\", {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n messages: [{ role: \"user\", content: \"Hello, how are you?\" }],\n }),\n});\n\nconst reader = response.body!.getReader();\nconst decoder = new TextDecoder();\n\nwhile (true) {\n const { value, done } = await reader.read();\n if (done) break;\n\n const chunk = decoder.decode(value);\n const lines = chunk.split(\"\\n\");\n\n for (const line of lines) {\n if (line.startsWith(\"data: \")) {\n const data = JSON.parse(line.slice(6));\n if (data === \"[DONE]\") break;\n\n // Handle the streaming response chunk\n console.log(data);\n }\n }\n}\n```\n", "ai-streaming-server/_dot_env.example": "ANTHROPIC_API_KEY=your_api_key_here ", "ai-streaming-server/package.json": "{\n \"name\": \"ai-streaming-server\",\n \"version\": \"1.0.0\",\n \"description\": \"Express server for streaming Anthropic AI chat responses\",\n \"main\": \"dist/index.js\",\n \"type\": \"module\",\n \"scripts\": {\n \"build\": \"tsc\",\n \"start\": \"node dist/index.js\",\n \"dev\": \"tsx watch src/index.ts\",\n \"typecheck\": \"tsc --noEmit\"\n },\n \"dependencies\": {\n \"@anthropic-ai/sdk\": \"^0.65.0\",\n \"cors\": \"^2.8.5\",\n \"dotenv\": \"^17.3.1\",\n \"express\": \"^5.1.0\"\n },\n \"devDependencies\": {\n \"@types/cors\": \"^2.8.17\",\n \"@types/express\": \"^5.0.3\",\n \"@types/node\": \"^24.6.0\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^6.0.2\"\n }\n}\n", "ai-streaming-server/src/index.ts": "import express, { Request, Response } from 'express'\nimport cors from 'cors'\nimport dotenv from 'dotenv'\nimport Anthropic from '@anthropic-ai/sdk'\n\n// Load environment variables\ndotenv.config()\n\nconst app = express()\nconst port = process.env.PORT || 8080\n\n// Middleware\napp.use(cors())\napp.use(express.json())\n\n// Initialize Anthropic client\nconst anthropic = new Anthropic({\n apiKey: process.env.ANTHROPIC_API_KEY,\n})\n\n// Define types for the request body\ninterface ChatMessage {\n role: 'user' | 'assistant'\n content: string\n}\n\ninterface ChatRequest {\n messages: ChatMessage[]\n}\n\n// Streaming chat endpoint\napp.post(\n '/api/chat',\n async (req: Request<{}, {}, ChatRequest>, res: Response) => {\n try {\n const { messages } = req.body\n\n if (!messages || !Array.isArray(messages)) {\n return res.status(400).json({ error: 'Messages array is required' })\n }\n\n // Validate message format\n const isValidMessages = messages.every(\n (msg): msg is ChatMessage =>\n typeof msg === 'object' &&\n (msg.role === 'user' || msg.role === 'assistant') &&\n typeof msg.content === 'string',\n )\n\n if (!isValidMessages) {\n return res.status(400).json({\n error:\n \"Invalid message format. Each message must have 'role' and 'content'\",\n })\n }\n\n // Set up SSE headers\n res.setHeader('Content-Type', 'text/event-stream')\n res.setHeader('Cache-Control', 'no-cache')\n res.setHeader('Connection', 'keep-alive')\n\n // Create the message stream\n const stream = await anthropic.messages.create({\n messages: messages.map((msg) => ({\n role: msg.role,\n content: msg.content,\n })),\n model: 'claude-3-opus-20240229',\n max_tokens: 4096,\n stream: true,\n })\n\n // Stream the response\n for await (const chunk of stream) {\n if (chunk.type === 'content_block_delta') {\n res.write(`data: ${JSON.stringify(chunk)}\\n\\n`)\n }\n }\n\n // End the stream\n res.write('data: [DONE]\\n\\n')\n res.end()\n } catch (error) {\n console.error('Error:', error)\n // If headers haven't been sent yet, send error response\n if (!res.headersSent) {\n res.status(500).json({ error: 'Internal server error' })\n } else {\n // If streaming has started, send error event\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error'\n res.write(`data: ${JSON.stringify({ error: errorMessage })}\\n\\n`)\n res.end()\n }\n }\n },\n)\n\n// Start the server\napp.listen(port, () => {\n console.log(`Server is running on port ${port}`)\n})\n", "ai-streaming-server/tsconfig.json": "{\n \"compilerOptions\": {\n \"target\": \"ES2022\",\n \"module\": \"NodeNext\",\n \"moduleResolution\": \"NodeNext\",\n \"esModuleInterop\": true,\n \"strict\": true,\n \"skipLibCheck\": true,\n \"outDir\": \"dist\",\n \"rootDir\": \"src\",\n \"sourceMap\": true\n },\n \"include\": [\"src/**/*\"],\n \"exclude\": [\"node_modules\", \"dist\"]\n}\n", "src/components/demo.SettingsDialog.tsx": "import { PlusCircle, Trash2 } from 'lucide-solid'\nimport { useAppActions, useAppState } from '../store/demo.hooks'\nimport { createSignal } from 'solid-js'\n\ninterface SettingsDialogProps {\n isOpen: boolean\n onClose: () => void\n}\n\nexport function SettingsDialog(props: SettingsDialogProps) {\n const [promptForm, setPromptForm] = createSignal({ name: '', content: '' })\n const [isAddingPrompt, setIsAddingPrompt] = createSignal(false)\n const state = useAppState()\n const actions = useAppActions();\n\n const handleAddPrompt = () => {\n if (!promptForm.name.trim() || !promptForm().content.trim()) return\n actions.createPrompt(promptForm().name, promptForm().content)\n setPromptForm({ name: '', content: '' })\n setIsAddingPrompt(false)\n }\n\n const handleClose = () => {\n props.onClose()\n setIsAddingPrompt(false)\n setPromptForm({ name: '', content: '' })\n }\n\n if (!props.isOpen) return null\n\n return (\n
{\n if (e.target === e.currentTarget) handleClose()\n }}>\n
e.stopPropagation()}>\n
\n
\n

Settings

\n \n \n \n \n \n
\n\n
\n {/* Prompts Management */}\n
\n
\n \n setIsAddingPrompt(true)}\n class=\"flex items-center gap-2 px-3 py-1.5 text-sm font-medium text-white bg-gradient-to-r from-orange-500 to-red-600 rounded-lg hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-orange-500\"\n >\n \n Add Prompt\n \n
\n\n {isAddingPrompt() && (\n
\n setPromptForm(prev => ({ ...prev, name: e.target.value }))}\n placeholder=\"Prompt name...\"\n class=\"w-full px-3 py-2 text-sm text-white bg-gray-700 rounded-lg border border-gray-600 focus:border-orange-500 focus:ring-1 focus:ring-orange-500\"\n />\n setPromptForm(prev => ({ ...prev, content: e.target.value }))}\n placeholder=\"Enter prompt content...\"\n class=\"w-full h-32 px-3 py-2 text-sm text-white bg-gray-700 rounded-lg border border-gray-600 focus:border-orange-500 focus:ring-1 focus:ring-orange-500\"\n />\n
\n setIsAddingPrompt(false)}\n class=\"px-3 py-1.5 text-sm font-medium text-gray-300 hover:text-white focus:outline-none\"\n >\n Cancel\n \n \n Save Prompt\n \n
\n
\n )}\n\n
\n {state().prompts.map((prompt) => (\n
\n
\n

{prompt.name}

\n

{prompt.content}

\n
\n
\n \n actions.deletePrompt(prompt.id)}\n class=\"p-1 text-gray-400 hover:text-red-500\"\n >\n \n \n
\n
\n ))}\n
\n

\n Create and manage custom system prompts. Only one prompt can be active at a time.\n

\n
\n\n
\n\n
\n \n Cancel\n \n \n Close\n \n
\n
\n
\n
\n )\n}\n", "src/demo.index.css": "@import \"tailwindcss\";\n@import \"highlight.js/styles/github-dark.css\";\n\n/* Custom scrollbar styles */\n::-webkit-scrollbar {\n width: 8px;\n}\n\n::-webkit-scrollbar-track {\n background: transparent;\n}\n\n::-webkit-scrollbar-thumb {\n background-color: rgba(156, 163, 175, 0.5);\n border-radius: 4px;\n}\n\n::-webkit-scrollbar-thumb:hover {\n background-color: rgba(156, 163, 175, 0.7);\n}\n\n/* Smooth transitions for dark mode */\nhtml {\n transition: background-color 0.3s ease;\n}\n\n/* Markdown content styles */\n.prose {\n max-width: none;\n color: #e5e7eb; /* text-gray-200 */\n}\n\n/* .prose p {\n margin-top: 1.25em;\n margin-bottom: 1.25em;\n} */\n\n.prose code {\n color: #e5e7eb;\n background-color: rgba(31, 41, 55, 0.5);\n padding: 0.2em 0.4em;\n border-radius: 0.375rem;\n font-size: 0.875em;\n}\n\n.prose pre {\n background-color: rgba(31, 41, 55, 0.5);\n border-radius: 0.5rem;\n padding: 1rem;\n margin: 1.25em 0;\n overflow-x: auto;\n}\n\n.prose pre code {\n background-color: transparent;\n padding: 0;\n border-radius: 0;\n color: inherit;\n}\n\n.prose h1,\n.prose h2,\n.prose h3,\n.prose h4 {\n color: #f9fafb; /* text-gray-50 */\n /* margin-top: 2em; */\n /* margin-bottom: 1em; */\n}\n\n.prose ul,\n.prose ol {\n margin-top: 1.25em;\n margin-bottom: 1.25em;\n padding-left: 1.625em;\n}\n\n.prose li {\n margin-top: 0.5em;\n margin-bottom: 0.5em;\n}\n\n.prose blockquote {\n border-left-color: #f97316; /* orange-500 */\n background-color: rgba(249, 115, 22, 0.1);\n padding: 1em;\n margin: 1.25em 0;\n border-radius: 0.5rem;\n}\n\n.prose hr {\n border-color: rgba(249, 115, 22, 0.2);\n margin: 2em 0;\n}\n\n.prose a {\n color: #f97316; /* orange-500 */\n text-decoration: underline;\n text-decoration-thickness: 0.1em;\n text-underline-offset: 0.2em;\n}\n\n.prose a:hover {\n color: #fb923c; /* orange-400 */\n}\n\n.prose table {\n width: 100%;\n border-collapse: collapse;\n margin: 1.25em 0;\n}\n\n.prose th,\n.prose td {\n padding: 0.75em;\n border: 1px solid rgba(249, 115, 22, 0.2);\n}\n\n.prose th {\n background-color: rgba(249, 115, 22, 0.1);\n font-weight: 600;\n}\n\n/* Message transition animations */\n.message-enter {\n opacity: 0;\n transform: translateY(10px);\n}\n\n.message-enter-active {\n opacity: 1;\n transform: translateY(0);\n transition:\n opacity 300ms,\n transform 300ms;\n}\n\n.message-exit {\n opacity: 1;\n}\n\n.message-exit-active {\n opacity: 0;\n transition: opacity 300ms;\n}\n\n/* Add/update these styles to match AI formatting capabilities */\n.prose h1 {\n font-size: 2em;\n /* margin-top: 1em; */\n margin-bottom: 0.5em;\n}\n\n.prose h2 {\n font-size: 1.5em;\n margin-top: 1em;\n margin-bottom: 0.5em;\n}\n\n.prose h3 {\n font-size: 1.25em;\n margin-top: 1em;\n margin-bottom: 0.5em;\n}\n\n.prose ul {\n list-style-type: disc;\n padding-left: 1.5em;\n}\n\n.prose ol {\n list-style-type: decimal;\n padding-left: 1.5em;\n}\n\n.prose table {\n width: 100%;\n border-collapse: collapse;\n margin: 1em 0;\n}\n\n.prose th,\n.prose td {\n border: 1px solid rgba(249, 115, 22, 0.2);\n padding: 0.5em;\n}\n\n.prose th {\n background-color: rgba(249, 115, 22, 0.1);\n}\n\n.prose strong {\n color: #f9fafb; /* text-gray-50 */\n font-weight: 600;\n}\n\n.prose em {\n font-style: italic;\n}\n\n.prose blockquote {\n border-left: 4px solid #f97316; /* orange-500 */\n padding-left: 1em;\n margin: 1em 0;\n color: #d1d5db; /* text-gray-300 */\n}\n\n/* Ensure code blocks match the AI's formatting */\n.prose code {\n color: #e5e7eb;\n background-color: rgba(31, 41, 55, 0.5);\n padding: 0.2em 0.4em;\n border-radius: 0.375rem;\n font-size: 0.875em;\n}\n\n.prose pre {\n background-color: rgba(31, 41, 55, 0.5);\n border-radius: 0.5rem;\n padding: 1rem;\n margin: 1em 0;\n}\n\n.prose pre code {\n background-color: transparent;\n padding: 0;\n border-radius: 0;\n}\n", "src/lib/demo-store.ts": "import { Store } from '@tanstack/store'\n\nexport const store = new Store({\n firstName: 'Jane',\n lastName: 'Smith',\n})\n\nexport const fullName = new Store(`${store.state.firstName} ${store.state.lastName}`)\n\nstore.subscribe(() => {\n fullName.setState(() => `${store.state.firstName} ${store.state.lastName}`)\n})\n", "src/routes/example.chat.tsx": "import { createEffect, createSignal, Show } from 'solid-js'\nimport { createFileRoute } from '@tanstack/solid-router'\nimport {\n PlusCircle,\n MessageCircle,\n Trash2,\n Send,\n Settings,\n Edit2,\n} from 'lucide-solid'\nimport MarkdownIt from 'markdown-it'\nimport { SettingsDialog } from '../components/demo.SettingsDialog'\nimport {\n useAppActions,\n useAppSelectors,\n useAppState,\n} from '../store/demo.hooks'\nimport { store } from '../store/demo.store'\n\nimport '../demo.index.css'\n\ntype Message = {\n id: string\n role: 'user' | 'assistant'\n content: string\n}\n\nconst md = new MarkdownIt()\n\nfunction Home() {\n const state = useAppState()\n const actions = useAppActions()\n const selectors = useAppSelectors()\n\n const currentConversation = () =>\n state().conversations.find((c) => c.id === state().currentConversationId)\n const messages = () => currentConversation()?.messages || []\n\n // Local state\n const [input, setInput] = createSignal('')\n const [editingChatId, setEditingChatId] = createSignal(null)\n const [isSettingsOpen, setIsSettingsOpen] = createSignal(false)\n let messagesContainerRef: HTMLDivElement | null\n const [pendingMessage, setPendingMessage] = createSignal(null)\n\n const scrollToBottom = () => {\n if (messagesContainerRef) {\n messagesContainerRef.scrollTop = messagesContainerRef.scrollHeight\n }\n }\n\n // Scroll to bottom when messages change or loading state changes\n createEffect(() => {\n state().isLoading\n messages()\n scrollToBottom()\n })\n\n const handleSubmit = async (e: any) => {\n e.preventDefault()\n if (!input().trim() || state().isLoading) return\n\n const currentInput = input()\n setInput('') // Clear input early for better UX\n actions.setLoading(true)\n\n try {\n let conversationId = state().currentConversationId\n\n // If no current conversation, create one\n if (!conversationId) {\n conversationId = Date.now().toString()\n const newConversation = {\n id: conversationId,\n title: currentInput.trim().slice(0, 30),\n messages: [],\n }\n actions.addConversation(newConversation)\n }\n\n const userMessage: Message = {\n id: Date.now().toString(),\n role: 'user' as const,\n content: currentInput.trim(),\n }\n\n // Add user message\n actions.addMessage(conversationId, userMessage)\n\n // Get active prompt\n const activePrompt = selectors.getActivePrompt(store.state)\n let systemPrompt\n if (activePrompt) {\n systemPrompt = {\n value: activePrompt.content,\n enabled: true,\n }\n }\n\n const response = await fetch('http://localhost:8080/api/chat', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n messages: [...messages(), userMessage],\n systemPrompt,\n }),\n })\n\n const reader = response.body?.getReader()\n if (!reader) {\n throw new Error('No reader found in response')\n }\n\n const decoder = new TextDecoder()\n\n let done = false\n let newMessage = {\n id: (Date.now() + 1).toString(),\n role: 'assistant' as const,\n content: '',\n }\n while (!done) {\n const out = await reader.read()\n done = out.done\n if (!done) {\n try {\n const jsonTxt = decoder.decode(out.value).replace(/^data:\\s+/, '')\n console.log(jsonTxt)\n const json = JSON.parse(jsonTxt)\n if (json.type === 'content_block_delta') {\n newMessage = {\n ...newMessage,\n content: newMessage.content + json.delta.text,\n }\n setPendingMessage(newMessage)\n }\n } catch (e) {\n console.error(e)\n }\n }\n }\n\n setPendingMessage(null)\n if (newMessage.content.trim()) {\n actions.addMessage(conversationId, newMessage)\n }\n } catch (error) {\n console.error('Error:', error)\n const errorMessage: Message = {\n id: (Date.now() + 1).toString(),\n role: 'assistant' as const,\n content: 'Sorry, I encountered an error processing your request.',\n }\n if (state().currentConversationId) {\n actions.addMessage(state().currentConversationId!, errorMessage)\n }\n } finally {\n actions.setLoading(false)\n }\n }\n\n const handleNewChat = () => {\n const newConversation = {\n id: Date.now().toString(),\n title: 'New Chat',\n messages: [],\n }\n actions.addConversation(newConversation)\n }\n\n const handleDeleteChat = (id: string) => {\n actions.deleteConversation(id)\n }\n\n const handleUpdateChatTitle = (id: string, title: string) => {\n actions.updateConversationTitle(id, title)\n setEditingChatId(null)\n }\n\n // Handle input change\n const handleInputChange = (e: any) => {\n setInput(e.target.value)\n }\n\n return (\n
\n {/* Settings Button */}\n
\n setIsSettingsOpen(true)}\n class=\"w-10 h-10 rounded-full bg-gradient-to-r from-orange-500 to-red-600 flex items-center justify-center text-white hover:opacity-90 transition-opacity focus:outline-none focus:ring-2 focus:ring-orange-500\"\n >\n \n \n
\n\n {/* Sidebar */}\n
\n
\n \n \n New Chat\n \n
\n\n {/* Chat List */}\n
\n {state().conversations.map((chat) => (\n actions.setCurrentConversationId(chat.id)}\n >\n \n {editingChatId() === chat.id ? (\n \n handleUpdateChatTitle(chat.id, e.target.value)\n }\n onBlur={() => setEditingChatId(null)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n handleUpdateChatTitle(chat.id, chat.title)\n }\n }}\n class=\"flex-1 bg-transparent text-sm text-white focus:outline-none\"\n autofocus\n />\n ) : (\n \n {chat.title}\n \n )}\n
\n {\n e.stopPropagation()\n setEditingChatId(chat.id)\n }}\n class=\"p-1 text-gray-400 hover:text-white\"\n >\n \n \n {\n e.stopPropagation()\n handleDeleteChat(chat.id)\n }}\n class=\"p-1 text-gray-400 hover:text-red-500\"\n >\n \n \n
\n
\n ))}\n
\n
\n\n {/* Main Content */}\n
\n {state().currentConversationId ? (\n <>\n {/* Messages */}\n \n
\n {[...messages(), pendingMessage()]\n .filter((v) => v)\n .map((message) => (\n \n
\n {message!.role === 'assistant' ? (\n
\n AI\n
\n ) : (\n
\n Y\n
\n )}\n
\n
\n
\n ))}\n {state().isLoading && (\n
\n
\n
\n
\n
\n
\n
\n \n AI\n \n
\n
\n
\n
\n
\n Thinking\n
\n
\n
\n
\n
\n
\n \n \n \n )}\n \n \n\n {/* Input */}\n
\n
\n
\n
\n {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n handleSubmit(e)\n }\n }}\n placeholder=\"Type something clever...\"\n class=\"w-full rounded-lg border border-orange-500/20 bg-gray-800/50 pl-4 pr-12 py-3 text-sm text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-orange-500/50 focus:border-transparent resize-none overflow-hidden shadow-lg\"\n rows={1}\n style={{ 'min-height': '44px', 'max-height': '200px' }}\n onInput={(e) => {\n const target = e.target as HTMLTextAreaElement\n target.style.height = 'auto'\n target.style.height =\n Math.min(target.scrollHeight, 200) + 'px'\n handleInputChange(e)\n }}\n />\n \n \n \n
\n
\n
\n
\n \n ) : (\n
\n
\n

\n TanStack Chat\n

\n

\n You can ask me about anything, I might or might not have a good\n answer, but you can still ask.\n

\n
\n
\n {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n handleSubmit(e)\n }\n }}\n placeholder=\"Type something clever...\"\n class=\"w-full rounded-lg border border-orange-500/20 bg-gray-800/50 pl-4 pr-12 py-3 text-sm text-white placeholder-gray-400 focus:outline-none focus:ring-2 focus:ring-orange-500/50 focus:border-transparent resize-none overflow-hidden\"\n rows={1}\n style={{ 'min-height': '88px' }}\n />\n \n \n \n
\n
\n
\n
\n )}\n \n\n {/* Settings Dialog */}\n \n setIsSettingsOpen(false)}\n />\n \n \n )\n}\n\nexport const Route = createFileRoute('/example/chat')({\n component: Home,\n})\n", "src/store/demo.hooks.ts": "import { useStore } from '@tanstack/solid-store'\nimport { store, actions, selectors } from './demo.store'\n\nexport type { State, Prompt, Conversation } from './demo.store'\n\nexport function useAppState() {\n const state = useStore(store)\n return state;\n}\n\nexport function useAppActions() {\n return actions\n}\n\nexport function useAppSelectors() {\n return selectors\n} ", "src/store/demo.store.ts": "import { Store } from '@tanstack/store'\nimport type { Message } from '../utils/demo.ai'\n\n// Types\nexport interface Prompt {\n id: string\n name: string\n content: string\n is_active: boolean\n created_at: number\n}\n\nexport interface Conversation {\n id: string\n title: string\n messages: Message[]\n}\n\nexport interface State {\n prompts: Prompt[]\n conversations: Conversation[]\n currentConversationId: string | null\n isLoading: boolean\n}\n\nconst initialState: State = {\n prompts: [],\n conversations: [],\n currentConversationId: null,\n isLoading: false\n}\n\nexport const store = new Store(initialState)\n\nexport const actions = {\n // Prompt actions\n createPrompt: (name: string, content: string) => {\n const id = Date.now().toString()\n store.setState(state => {\n const updatedPrompts = state.prompts.map(p => ({ ...p, is_active: false }))\n return {\n ...state,\n prompts: [\n ...updatedPrompts,\n {\n id,\n name,\n content,\n is_active: true,\n created_at: Date.now()\n }\n ]\n }\n })\n },\n\n deletePrompt: (id: string) => {\n store.setState(state => ({\n ...state,\n prompts: state.prompts.filter(p => p.id !== id)\n }))\n },\n\n setPromptActive: (id: string, shouldActivate: boolean) => {\n store.setState(state => ({\n ...state,\n prompts: state.prompts.map(p => ({\n ...p,\n is_active: p.id === id ? shouldActivate : false\n }))\n }))\n },\n\n // Chat actions\n setConversations: (conversations: Conversation[]) => {\n store.setState(state => ({ ...state, conversations }))\n },\n\n setCurrentConversationId: (id: string | null) => {\n store.setState(state => ({ ...state, currentConversationId: id }))\n },\n\n addConversation: (conversation: Conversation) => {\n store.setState(state => ({\n ...state,\n conversations: [...state.conversations, conversation],\n currentConversationId: conversation.id\n }))\n },\n\n updateConversationTitle: (id: string, title: string) => {\n store.setState(state => ({\n ...state,\n conversations: state.conversations.map(conv =>\n conv.id === id ? { ...conv, title } : conv\n )\n }))\n },\n\n deleteConversation: (id: string) => {\n store.setState(state => ({\n ...state,\n conversations: state.conversations.filter(conv => conv.id !== id),\n currentConversationId: state.currentConversationId === id ? null : state.currentConversationId\n }))\n },\n\n addMessage: (conversationId: string, message: Message) => {\n store.setState(state => ({\n ...state,\n conversations: state.conversations.map(conv =>\n conv.id === conversationId\n ? { ...conv, messages: [...conv.messages, message] }\n : conv\n )\n }))\n },\n\n setLoading: (isLoading: boolean) => {\n store.setState(state => ({ ...state, isLoading }))\n }\n}\n\n// Selectors\nexport const selectors = {\n getActivePrompt: (state: State) => state.prompts.find(p => p.is_active),\n getCurrentConversation: (state: State) => \n state.conversations.find(c => c.id === state.currentConversationId),\n getPrompts: (state: State) => state.prompts,\n getConversations: (state: State) => state.conversations,\n getCurrentConversationId: (state: State) => state.currentConversationId,\n getIsLoading: (state: State) => state.isLoading\n} " }, "deletedFiles": [] }, { "name": "Cloudflare", "description": "Deploy to Cloudflare Workers (edge runtime, KV/D1/R2 bindings).", "link": "https://developers.cloudflare.com/workers/vite-plugin/", "phase": "add-on", "modes": [ "file-router", "code-router" ], "type": "deployment", "category": "deploy", "exclusive": [ "deploy" ], "color": "#F38020", "priority": 200, "partner": { "id": "cloudflare", "tier": "gold" }, "integrations": [ { "type": "vite-plugin", "import": "import { cloudflare } from '@cloudflare/vite-plugin'", "code": "cloudflare({ viteEnvironment: { name: 'ssr' } })" } ], "id": "cloudflare", "version": "0.0.0", "packageAdditions": {}, "packageTemplate": "{\n \"scripts\": {\n \"deploy\": \"<%= packageManager %> run build && wrangler deploy\"\n },\n \"dependencies\": {\n \"@cloudflare/vite-plugin\": \"^1.26.0\"\n },\n \"devDependencies\": {\n \"wrangler\": \"^4.70.0\"\n },\n \"pnpm\": {\n \"onlyBuiltDependencies\": [\"sharp\", \"workerd\"]\n }\n}\n", "readme": "## Deploy to Cloudflare Workers\n\nThis project uses the Cloudflare Vite plugin (configured in `vite.config.ts`) and `wrangler.jsonc`:\n\n1. Install Wrangler: `npm install -g wrangler`\n2. Authenticate: `wrangler login`\n3. Deploy: `npx wrangler deploy`\n\nFor production env vars, run `wrangler secret put MY_VAR` for each secret listed in `.env.example`. Public (non-secret) vars go in `wrangler.jsonc` under `vars`.\n\nKV, D1, R2, and Durable Object bindings are configured in `wrangler.jsonc` — see https://developers.cloudflare.com/workers/wrangler/configuration/.\n", "readmeIsEjs": false, "files": { "wrangler.jsonc": "{\n \"$schema\": \"node_modules/wrangler/config-schema.json\",\n \"name\": \"tanstack-start-app\",\n \"compatibility_date\": \"2025-09-02\",\n \"compatibility_flags\": [\n \"nodejs_compat\"\n ],\n \"main\": \"@tanstack/solid-start/server-entry\"\n}" }, "deletedFiles": [], "smallLogo": "" }, { "name": "Netlify", "description": "Deploy to Netlify (Functions and Edge Functions, GitHub-driven).", "link": "https://docs.netlify.com", "phase": "add-on", "modes": [ "file-router", "code-router" ], "type": "deployment", "category": "deploy", "exclusive": [ "deploy" ], "color": "#00C7B7", "priority": 180, "partner": { "id": "netlify", "tier": "gold" }, "integrations": [ { "type": "vite-plugin", "import": "import netlify from '@netlify/vite-plugin-tanstack-start'", "code": "netlify()" } ], "id": "netlify", "version": "0.0.0", "packageAdditions": { "devDependencies": { "@netlify/vite-plugin-tanstack-start": "^1.2.15" }, "pnpm": { "onlyBuiltDependencies": [ "sharp" ] } }, "readme": "## Deploy to Netlify\n\nThis project ships with `netlify.toml` configured for a Netlify site:\n\n1. Push this repo to GitHub\n2. Visit https://app.netlify.com/start and import the repo\n3. Netlify auto-detects the build (`vite build` → `dist/client`)\n4. Open **Site settings → Environment variables** and add anything from `.env.example` that needs a real value in production\n5. Trigger the first deploy\n\nServer functions and API routes run on Netlify Functions. For lower-latency request handling, see Netlify Edge Functions: https://docs.netlify.com/edge-functions/overview.\n", "readmeIsEjs": false, "files": { "netlify.toml": "[build]\n command = \"vite build\"\n publish = \"dist/client\"\n[dev]\n command = \"npm run dev\"\n targetPort = 3000\n port = 8888\n" }, "deletedFiles": [], "smallLogo": "" }, { "name": "Nitro (agnostic)", "description": "Generic Nitro adapter (deploy to any Node-compatible host).", "link": "https://v3.nitro.build/", "phase": "add-on", "modes": [ "file-router", "code-router" ], "type": "deployment", "category": "deploy", "exclusive": [ "deploy" ], "color": "#ECDC5A", "integrations": [ { "type": "vite-plugin", "import": "import { nitro } from 'nitro/vite'", "code": "nitro()" } ], "default": true, "id": "nitro", "version": "0.0.0", "packageAdditions": { "dependencies": { "nitro": "3.0.260610-beta" } }, "readme": "## Deploy with Nitro\n\nThis project uses Nitro as a generic server adapter, so it can run on any Node-compatible host.\n\n```bash\nnpm run build\nnode dist/server/index.mjs\n```\n\nThe build output is a self-contained Node server. To deploy, push the `dist/` directory to your host (Render, Fly.io, your own VPS, etc.) and run the server command above.\n\nFor host-specific presets (Vercel, Netlify, Cloudflare, AWS Lambda, etc.) and tuning, see https://v3.nitro.build/deploy.\n", "readmeIsEjs": false, "files": {}, "deletedFiles": [], "smallLogo": "\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n\n" }, { "name": "Railway", "description": "Deploy to Railway (full-stack PaaS for Node + databases).", "link": "https://railway.com/", "phase": "add-on", "modes": [ "file-router", "code-router" ], "type": "deployment", "category": "deploy", "exclusive": [ "deploy" ], "color": "#9B4DCA", "priority": 160, "partner": { "id": "railway", "tier": "gold" }, "deletedFiles": [ "nixpacks.toml" ], "integrations": [ { "type": "vite-plugin", "import": "import { nitro } from 'nitro/vite'", "code": "nitro()" } ], "id": "railway", "version": "0.0.0", "packageAdditions": { "scripts": { "start": "node .output/server/index.mjs" }, "dependencies": { "nitro": "3.0.260610-beta" } }, "readme": "## Deploy to Railway\n\nRailway's Railpack builder detects the project's package manager and package\nscripts automatically.\n\n1. Push this repo to GitHub\n2. Visit https://railway.com/new and create a project from your repo\n3. In the **Variables** tab, add the entries from `.env.example` with their production values\n4. Deploy, then open **Networking** and select **Generate Domain**\n\nRailpack runs the project's build script and starts the generated Nitro server\nwith `node .output/server/index.mjs`. The server handles SSR, server functions,\nAPI routes, and static assets.\n\nNeed a database? Add one from the Railway project canvas, then connect it to the\napp with a Railway reference variable. The variable name and value depend on the\ndatabase service you choose.\n", "readmeIsEjs": false, "files": {} }, { "name": "Render", "description": "Deploy to Render (full-stack cloud platform with GitHub integration).", "link": "https://render.com/", "phase": "add-on", "modes": [ "file-router" ], "type": "deployment", "supportedPackageManagers": [ "npm", "yarn", "pnpm", "bun" ], "category": "deploy", "exclusive": [ "deploy" ], "color": "#46E3B7", "priority": 150, "partner": { "id": "render", "tier": "gold" }, "integrations": [ { "type": "vite-plugin", "import": "import { nitro } from 'nitro/vite'", "code": "nitro()" } ], "id": "render", "version": "0.0.0", "packageAdditions": { "scripts": { "start": "node .output/server/index.mjs" }, "dependencies": { "nitro": "3.0.260610-beta" } }, "readme": "## Deploy to Render\n\nThis project includes a Render Blueprint:\n\n1. Push this repo to GitHub, GitLab, or Bitbucket\n2. In Render, choose **New > Blueprint** and connect this repo\n3. Render reads `render.yaml` and creates the web service\n4. In the Render Dashboard, add any secrets from `.env.example` under **Environment**\n5. Deploy\n\nThe Nitro server handles SSR, server functions, API routes, and static assets.\nRender provides `PORT` automatically; Nitro reads it at runtime.\n\nNeed a database? Add one from the Render Dashboard, then connect it via\nenvironment variables. See https://render.com/docs/databases for options.\n", "readmeIsEjs": false, "files": { "render.yaml.ejs": "services:\n - type: web\n runtime: node\n name: tanstack-start-app\n buildCommand: <%= packageManager %> install && <%= getPackageManagerRunScript('build') %>\n startCommand: <%= getPackageManagerRunScript('start') %>\n envVars:\n<% if (packageManager === 'bun') { -%>\n - key: BUN_VERSION\n value: '1.3.14'\n<% } -%>\n - key: NITRO_PRESET\n value: render-com\n - key: HOST\n value: 0.0.0.0\n" }, "deletedFiles": [] }, { "name": "Vercel", "description": "Deploy to Vercel (Functions, static assets, and Git previews).", "link": "https://vercel.com/docs/frameworks/full-stack/tanstack-start", "phase": "add-on", "modes": [ "file-router" ], "type": "deployment", "supportedPackageManagers": [ "npm", "yarn", "pnpm", "bun" ], "category": "deploy", "exclusive": [ "deploy" ], "color": "#000000", "priority": 140, "partner": { "id": "vercel", "tier": "gold" }, "integrations": [ { "type": "vite-plugin", "import": "import { nitro } from 'nitro/vite'", "code": "nitro()" } ], "id": "vercel", "version": "0.0.0", "packageAdditions": { "dependencies": { "nitro": "3.0.260610-beta" } }, "readme": "## Deploy to Vercel\n\n1. Push this repo to GitHub, GitLab, or Bitbucket\n2. In Vercel, choose **Add New > Project** and import the repo\n3. Keep the detected TanStack Start framework settings\n4. Add production values from `.env.example` under **Settings > Environment Variables**\n5. Deploy\n\nVercel runs the build script and deploys Nitro's output as Vercel Functions and\nstatic assets. The included `vercel.json` makes framework detection explicit.\n\nVariables prefixed with `VITE_` are included in the browser bundle. Keep secrets\nunprefixed so they remain server-only.\n", "readmeIsEjs": false, "files": { "vercel.json": "{\n \"$schema\": \"https://openapi.vercel.sh/vercel.json\",\n \"framework\": \"tanstack-start\"\n}\n" }, "deletedFiles": [] } ] } ]