+
diff --git a/apps/ui-library/app/(app)/telemetry-wrapper.tsx b/apps/ui-library/app/(app)/telemetry-wrapper.tsx
new file mode 100644
index 00000000000..5460e46156b
--- /dev/null
+++ b/apps/ui-library/app/(app)/telemetry-wrapper.tsx
@@ -0,0 +1,16 @@
+'use client'
+
+import { API_URL } from '@/lib/constants'
+import { IS_PLATFORM, PageTelemetry } from 'common'
+import { useConsent } from 'ui-patterns/ConsentToast'
+
+export const TelemetryWrapper = () => {
+ const { hasAcceptedConsent } = useConsent()
+ return (
+
+ )
+}
diff --git a/apps/ui-library/app/Providers.tsx b/apps/ui-library/app/Providers.tsx
index 97e0ebdbc72..d6083701403 100644
--- a/apps/ui-library/app/Providers.tsx
+++ b/apps/ui-library/app/Providers.tsx
@@ -1,13 +1,14 @@
'use client'
-import { AuthProvider } from 'common'
import { Provider as JotaiProvider } from 'jotai'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { ThemeProviderProps } from 'next-themes/dist/types'
import { FrameworkProvider } from '@/context/framework-context'
import { MobileMenuProvider } from '@/context/mobile-menu-context'
+import { AuthProvider } from 'common'
import { TooltipProvider } from 'ui'
+import { useConsent } from 'ui-patterns/ConsentToast'
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return (
diff --git a/apps/ui-library/components/block-preview.tsx b/apps/ui-library/components/block-preview.tsx
index 77e3a9fc251..777d8c8ab8b 100644
--- a/apps/ui-library/components/block-preview.tsx
+++ b/apps/ui-library/components/block-preview.tsx
@@ -39,7 +39,7 @@ export function BlockPreview({ name, wide = false, isPair = false }: ComponentPr
diff --git a/apps/ui-library/components/command-copy-button.tsx b/apps/ui-library/components/command-copy-button.tsx
index d040a80a10d..a5fff1b356d 100644
--- a/apps/ui-library/components/command-copy-button.tsx
+++ b/apps/ui-library/components/command-copy-button.tsx
@@ -2,10 +2,13 @@
import { Check, Copy } from 'lucide-react'
import { useEffect, useState } from 'react'
+
+import { useSendTelemetryEvent } from 'lib/telemetry'
import { Button_Shadcn_ } from 'ui'
export function CommandCopyButton({ command }: { command: string }) {
const [copied, setCopied] = useState(false)
+ const sendTelemetryEvent = useSendTelemetryEvent()
useEffect(() => {
if (copied) {
@@ -14,13 +17,56 @@ export function CommandCopyButton({ command }: { command: string }) {
}
}, [copied])
+ const parseCommandForTelemetry = (cmd: string) => {
+ // Extract framework from URL (e.g., 'nextjs' from 'password-based-auth-nextjs.json')
+ const frameworkMatch = cmd.match(/\/ui\/r\/.*?-(\w+)\.json/)
+ const framework = frameworkMatch
+ ? (frameworkMatch[1] as 'nextjs' | 'react-router' | 'tanstack' | 'react')
+ : 'nextjs'
+
+ // Extract package manager from command prefix (npx, pnpm, yarn, bun)
+ const packageManager = cmd.startsWith('npx')
+ ? ('npm' as const)
+ : cmd.startsWith('pnpm')
+ ? ('pnpm' as const)
+ : cmd.startsWith('yarn')
+ ? ('yarn' as const)
+ : cmd.startsWith('bun')
+ ? ('bun' as const)
+ : ('npm' as const)
+
+ // Extract template title from URL (e.g., 'password-based-auth' from 'password-based-auth-nextjs.json')
+ const titleMatch = cmd.match(/\/ui\/r\/(.*?)-\w+\.json/)
+ const title = titleMatch ? titleMatch[1] : ''
+
+ return {
+ framework,
+ packageManager,
+ title,
+ }
+ }
+
return (
{
+ // Copy command to clipboard
navigator.clipboard.writeText(command)
setCopied(true)
+
+ // Parse command and send telemetry event
+ const { framework, packageManager, title } = parseCommandForTelemetry(command)
+
+ sendTelemetryEvent({
+ action: 'supabase_ui_command_copy_button_clicked',
+ properties: {
+ templateTitle: title,
+ command: command,
+ framework,
+ packageManager,
+ },
+ })
}}
>
{copied ? : }
diff --git a/apps/ui-library/components/mdx-components.tsx b/apps/ui-library/components/mdx-components.tsx
index ad7369663dd..68d470a4a95 100644
--- a/apps/ui-library/components/mdx-components.tsx
+++ b/apps/ui-library/components/mdx-components.tsx
@@ -31,6 +31,7 @@ import {
TabsList_Shadcn_ as TabsList,
TabsTrigger_Shadcn_ as TabsTrigger,
} from 'ui'
+import TanStackBeta from '@/components/tanstack-beta'
import { Admonition } from 'ui-patterns/admonition'
import { BlockItem } from './block-item'
import { BlockPreview } from './block-preview'
@@ -248,6 +249,7 @@ const components = {
{...props}
/>
),
+ TanStackBeta,
// FrameworkDocs: ({ className, ...props }: React.ComponentProps) => (
//
// ),
@@ -257,7 +259,7 @@ const components = {
LinkedCard: ({ className, ...props }: React.ComponentProps) => (
+
+
+
+
+ Heads up: TanStack Start is in beta.
+
+ We're excited to support TanStack Start in our UI library! But since it's still
+ in beta, things may change quickly — expect breaking changes and some rough edges.
+ We'll do our best to keep up and make integration with Supabase as smooth as
+ possible. If you run into issues, have a look at the TanStack docs.
+
+ TanStack Quickstart guide
+
+
+
+
+ )
+}
diff --git a/apps/ui-library/content/docs/getting-started/quickstart.mdx b/apps/ui-library/content/docs/getting-started/quickstart.mdx
index c82490b554a..ade51c07240 100644
--- a/apps/ui-library/content/docs/getting-started/quickstart.mdx
+++ b/apps/ui-library/content/docs/getting-started/quickstart.mdx
@@ -60,6 +60,8 @@ If you're already using a recent version of shadcn/ui in your existing project,
+
+
When you've finished setting up shadcn/ui, you can start exploring the components in the sidebar
## Troubleshooting
diff --git a/apps/ui-library/content/docs/tanstack/client.mdx b/apps/ui-library/content/docs/tanstack/client.mdx
index dc9477629f8..db80ba69be7 100644
--- a/apps/ui-library/content/docs/tanstack/client.mdx
+++ b/apps/ui-library/content/docs/tanstack/client.mdx
@@ -7,6 +7,8 @@ description: Supabase client for TanStack Start
+
+
## Folder structure
diff --git a/apps/ui-library/lib/constants.ts b/apps/ui-library/lib/constants.ts
new file mode 100644
index 00000000000..9b2dc13741b
--- /dev/null
+++ b/apps/ui-library/lib/constants.ts
@@ -0,0 +1 @@
+export const API_URL = process.env.NEXT_PUBLIC_API_URL!
diff --git a/apps/ui-library/lib/telemetry.ts b/apps/ui-library/lib/telemetry.ts
new file mode 100644
index 00000000000..804549688cc
--- /dev/null
+++ b/apps/ui-library/lib/telemetry.ts
@@ -0,0 +1,19 @@
+'use client'
+
+import { usePathname } from 'next/navigation'
+import { useCallback } from 'react'
+
+import { sendTelemetryEvent } from 'common'
+import { TelemetryEvent } from 'common/telemetry-constants'
+import { API_URL } from 'lib/constants'
+
+export function useSendTelemetryEvent() {
+ const pathname = usePathname()
+
+ return useCallback(
+ (event: TelemetryEvent) => {
+ return sendTelemetryEvent(API_URL, event, pathname)
+ },
+ [pathname]
+ )
+}
diff --git a/apps/ui-library/registry/default/examples/realtime-chat-demo.tsx b/apps/ui-library/registry/default/examples/realtime-chat-demo.tsx
index 6462b35da51..5339d7d9ad3 100644
--- a/apps/ui-library/registry/default/examples/realtime-chat-demo.tsx
+++ b/apps/ui-library/registry/default/examples/realtime-chat-demo.tsx
@@ -58,10 +58,10 @@ const RealtimeChatDemo = () => {
return (
-
+
setUsername(e.target.value)}
/>
diff --git a/apps/ui-library/registry/default/examples/realtime-cursor-demo.tsx b/apps/ui-library/registry/default/examples/realtime-cursor-demo.tsx
index 0232fd53bb6..c10ce85d41f 100644
--- a/apps/ui-library/registry/default/examples/realtime-cursor-demo.tsx
+++ b/apps/ui-library/registry/default/examples/realtime-cursor-demo.tsx
@@ -18,7 +18,7 @@ const RealtimeCursorDemo = () => {
return (
-
+
setUsername(e.target.value)} disabled />