first commit

This commit is contained in:
2026-03-02 21:16:26 +01:00
commit 841f43285e
179 changed files with 33193 additions and 0 deletions

78
src/routes/__root.tsx Normal file
View File

@@ -0,0 +1,78 @@
import { TanStackDevtools } from "@tanstack/react-devtools";
import type { QueryClient } from "@tanstack/react-query";
import {
createRootRouteWithContext,
HeadContent,
Scripts,
} from "@tanstack/react-router";
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
import { getLocale } from "@/paraglide/runtime";
import Header from "../components/Header";
import TanStackQueryDevtools from "../integrations/tanstack-query/devtools";
import TanStackQueryProvider from "../integrations/tanstack-query/root-provider";
import appCss from "../styles.css?url";
interface MyRouterContext {
queryClient: QueryClient;
}
export const Route = createRootRouteWithContext<MyRouterContext>()({
beforeLoad: async () => {
// Other redirect strategies are possible; see
// https://github.com/TanStack/router/tree/main/examples/react/i18n-paraglide#offline-redirect
if (typeof document !== "undefined") {
document.documentElement.setAttribute("lang", getLocale());
}
},
head: () => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: "TanStack Start Starter",
},
],
links: [
{
rel: "stylesheet",
href: appCss,
},
],
}),
shellComponent: RootDocument,
});
function RootDocument({ children }: { children: React.ReactNode }) {
return (
<html lang={getLocale()}>
<head>
<HeadContent />
</head>
<body>
<TanStackQueryProvider>
<Header />
{children}
<TanStackDevtools
config={{
position: "bottom-right",
}}
plugins={[
{
name: "Tanstack Router",
render: <TanStackRouterDevtoolsPanel />,
},
TanStackQueryDevtools,
]}
/>
</TanStackQueryProvider>
<Scripts />
</body>
</html>
);
}

28
src/routes/demo.i18n.tsx Normal file
View File

@@ -0,0 +1,28 @@
import { createFileRoute } from "@tanstack/react-router";
import { m } from "@/paraglide/messages";
import LocaleSwitcher from "../components/LocaleSwitcher";
export const Route = createFileRoute("/demo/i18n")({
component: App,
});
function App() {
return (
<div className="text-center">
<header className="min-h-screen flex flex-col items-center justify-center bg-[#282c34] text-white text-[calc(10px+2vmin)] gap-4">
<p>{m.example_message({ username: "TanStack Router" })}</p>
<a
className="text-[#61dafb] hover:underline"
href="https://inlang.com/m/gerre34r/library-inlang-paraglideJs"
target="_blank"
rel="noopener noreferrer"
>
{m.learn_router()}
</a>
<div className="mt-3">
<LocaleSwitcher />
</div>
</header>
</div>
);
}

View File

@@ -0,0 +1,621 @@
/**
* FILE OVERVIEW:
* Purpose: Interactive demo page showcasing Sentry's monitoring capabilities
* Key Concepts: Error tracking, Performance monitoring, Session replay
* Module Type: Route Component
* @ai_context: Demonstrates Sentry features through interactive examples with educational context
*/
import * as fs from 'node:fs/promises'
import { createFileRoute } from '@tanstack/react-router'
import { createServerFn } from '@tanstack/react-start'
import * as Sentry from '@sentry/tanstackstart-react'
import { useState, useEffect } from 'react'
export const Route = createFileRoute('/demo/sentry/testing')({
component: RouteComponent,
errorComponent: ({ error }) => {
useEffect(() => {
Sentry.captureException(error)
}, [error])
return (
<div className="min-h-screen flex items-center justify-center bg-[#181423]">
<div className="text-center p-8">
<SentryLogo />
<h1 className="text-2xl font-bold text-white mt-4 mb-2">
Something went wrong
</h1>
<p className="text-[#A49FB5]">{error.message}</p>
</div>
</div>
)
},
})
// Sentry Logo Component
function SentryLogo({ size = 48 }: { size?: number }) {
return (
<svg
height={size}
width={size}
fill="none"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 40 40"
>
<path
d="M21.85 2.995a3.698 3.698 0 0 1 1.353 1.354l16.303 28.278a3.703 3.703 0 0 1-1.354 5.053 3.694 3.694 0 0 1-1.848.496h-3.828a31.149 31.149 0 0 0 0-3.09h3.815a.61.61 0 0 0 .537-.917L20.523 5.893a.61.61 0 0 0-1.057 0l-3.739 6.494a28.948 28.948 0 0 1 9.63 10.453 28.988 28.988 0 0 1 3.499 13.78v1.542h-9.852v-1.544a19.106 19.106 0 0 0-2.182-8.85 19.08 19.08 0 0 0-6.032-6.829l-1.85 3.208a15.377 15.377 0 0 1 6.382 12.484v1.542H3.696A3.694 3.694 0 0 1 0 34.473c0-.648.17-1.286.494-1.849l2.33-4.074a8.562 8.562 0 0 1 2.689 1.536L3.158 34.17a.611.611 0 0 0 .538.917h8.448a12.481 12.481 0 0 0-6.037-9.09l-1.344-.772 4.908-8.545 1.344.77a22.16 22.16 0 0 1 7.705 7.444 22.193 22.193 0 0 1 3.316 10.193h3.699a25.892 25.892 0 0 0-3.811-12.033 25.856 25.856 0 0 0-9.046-8.796l-1.344-.772 5.269-9.136a3.698 3.698 0 0 1 3.2-1.849c.648 0 1.285.17 1.847.495Z"
fill="currentColor"
/>
</svg>
)
}
// Server function that will error
const badServerFunc = createServerFn({
method: 'GET',
}).handler(async () => {
return await Sentry.startSpan(
{
name: 'Reading non-existent file',
op: 'file.read',
},
async () => {
try {
await fs.readFile('./doesnt-exist', 'utf-8')
return true
} catch (error) {
Sentry.captureException(error)
throw error
}
},
)
})
// Server function that will succeed but be traced
const goodServerFunc = createServerFn({
method: 'GET',
}).handler(async () => {
return await Sentry.startSpan(
{
name: 'Successful server operation',
op: 'demo.success',
},
async () => {
await new Promise((resolve) => setTimeout(resolve, 500))
return { success: true }
},
)
})
// 3D Button Component inspired by Sentry wizard
function SentryButton({
children,
onClick,
variant = 'primary',
disabled = false,
loading = false,
}: {
children: React.ReactNode
onClick: () => void
variant?: 'primary' | 'error'
disabled?: boolean
loading?: boolean
}) {
const baseColor = variant === 'error' ? '#E50045' : '#553DB8'
const topColor = variant === 'error' ? '#FF1A5C' : '#7553FF'
return (
<button
type="button"
onClick={onClick}
disabled={disabled || loading}
className="group w-full rounded-lg text-white cursor-pointer border-none p-0 transition-all disabled:cursor-not-allowed disabled:opacity-60"
style={{ backgroundColor: baseColor }}
>
<span
className="flex items-center justify-center gap-3 px-6 py-4 rounded-lg text-lg font-semibold transition-transform group-hover:-translate-y-1 group-active:translate-y-0 group-disabled:translate-y-0"
style={{
backgroundColor: topColor,
border: `1px solid ${baseColor}`,
}}
>
{loading && (
<svg
className="animate-spin h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
)}
{children}
</span>
</button>
)
}
// Feature Card Component
function FeatureCard({
icon,
title,
description,
}: {
icon: React.ReactNode
title: string
description: string
}) {
return (
<div className="bg-[#1C1825] rounded-xl p-4 border border-[#2D2640] hover:border-[#7553FF]/50 transition-all group">
<div className="flex items-center gap-3 mb-2">
<div className="text-[#7553FF] group-hover:scale-110 transition-transform">
{icon}
</div>
<h3 className="font-semibold text-white">{title}</h3>
</div>
<p className="text-sm text-[#A49FB5] pl-9">{description}</p>
</div>
)
}
// Result Badge Component
function ResultBadge({
type,
spanOp,
onCopy,
}: {
type: 'success' | 'error'
spanOp: string
onCopy: () => void
}) {
const [copied, setCopied] = useState(false)
const handleCopy = () => {
navigator.clipboard.writeText(spanOp)
setCopied(true)
onCopy()
setTimeout(() => setCopied(false), 2000)
}
return (
<div className="mt-4 space-y-3">
{type === 'error' && (
<div className="flex items-center gap-2 bg-[#E50045]/10 border border-[#E50045]/30 rounded-lg px-4 py-3">
<svg
className="w-5 h-5 text-[#FF1A5C]"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<title>Error captured</title>
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<span className="text-[#FF1A5C] text-sm font-medium">
Error captured and sent to Sentry
</span>
</div>
)}
{type === 'success' && (
<div className="flex items-center gap-2 bg-[#00F261]/10 border border-[#00BF4D]/30 rounded-lg px-4 py-3">
<svg
className="w-5 h-5 text-[#00F261]"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<title>Trace complete</title>
<path d="M5 13l4 4L19 7" />
</svg>
<span className="text-[#00F261] text-sm font-medium">
Trace completed successfully
</span>
</div>
)}
<button
type="button"
onClick={handleCopy}
className="relative flex items-center gap-2 bg-[#7553FF]/10 hover:bg-[#7553FF]/20 border border-[#7553FF]/30 rounded-lg px-4 py-2 transition-all cursor-pointer w-full"
>
<span className="text-[#B3A1FF] text-sm">span.op:</span>
<code className="text-[#7553FF] font-mono text-sm">{spanOp}</code>
<svg
className="w-4 h-4 text-[#B3A1FF] ml-auto"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<title>Copy to clipboard</title>
<path d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" />
</svg>
{copied && (
<span className="absolute -top-8 left-1/2 -translate-x-1/2 bg-[#00F261] text-[#181423] text-xs font-medium px-2 py-1 rounded animate-pulse">
Copied!
</span>
)}
</button>
</div>
)
}
// Progress Bar Component
function ProgressBar({ loading }: { loading: boolean }) {
return (
<div className="mt-4 flex items-center gap-3">
<div
className={`w-3 h-3 rounded-full transition-all ${loading ? 'bg-[#7553FF] animate-pulse' : 'bg-[#00F261]'}`}
/>
<div className="flex-1 h-2 bg-[#2D2640] rounded-full overflow-hidden">
<div
className="h-full bg-gradient-to-r from-[#7553FF] to-[#B3A1FF] rounded-full transition-all duration-500"
style={{ width: loading ? '60%' : '100%' }}
/>
</div>
<span className="text-xs text-[#A49FB5] w-16 text-right">
{loading ? 'Running...' : 'Complete'}
</span>
</div>
)
}
function RouteComponent() {
const [isLoading, setIsLoading] = useState<Record<string, boolean>>({})
const [results, setResults] = useState<
Record<string, { type: 'success' | 'error'; spanOp: string }>
>({})
const [sentryConfigured, setSentryConfigured] = useState<boolean | null>(null)
useEffect(() => {
// Check if Sentry DSN environment variable is set
const hasDsn = !!import.meta.env.VITE_SENTRY_DSN
setSentryConfigured(hasDsn)
}, [])
// Don't show warning until we've checked on the client
const showWarning = sentryConfigured === false
const handleClientError = async () => {
setIsLoading((prev) => ({ ...prev, clientError: true }))
try {
await Sentry.startSpan(
{ name: 'Client Error Flow Demo', op: 'demo.client-error' },
async () => {
Sentry.setContext('demo', {
feature: 'client-error-demo',
triggered_at: new Date().toISOString(),
})
throw new Error('Client-side error demonstration')
},
)
} catch (error) {
Sentry.captureException(error)
setResults((prev) => ({
...prev,
clientError: { type: 'error', spanOp: 'demo.client-error' },
}))
} finally {
setIsLoading((prev) => ({ ...prev, clientError: false }))
}
}
const handleServerError = async () => {
setIsLoading((prev) => ({ ...prev, serverError: true }))
try {
await Sentry.startSpan(
{ name: 'Server Error Flow Demo', op: 'demo.server-error' },
async () => {
Sentry.setContext('demo', {
feature: 'server-error-demo',
triggered_at: new Date().toISOString(),
})
await badServerFunc()
},
)
} catch (error) {
Sentry.captureException(error)
setResults((prev) => ({
...prev,
serverError: { type: 'error', spanOp: 'demo.server-error' },
}))
} finally {
setIsLoading((prev) => ({ ...prev, serverError: false }))
}
}
const handleClientTrace = async () => {
setIsLoading((prev) => ({ ...prev, clientTrace: true }))
await Sentry.startSpan(
{ name: 'Client Operation', op: 'demo.client-trace' },
async () => {
await new Promise((resolve) => setTimeout(resolve, 1000))
},
)
setResults((prev) => ({
...prev,
clientTrace: { type: 'success', spanOp: 'demo.client-trace' },
}))
setIsLoading((prev) => ({ ...prev, clientTrace: false }))
}
const handleServerTrace = async () => {
setIsLoading((prev) => ({ ...prev, serverTrace: true }))
try {
await Sentry.startSpan(
{ name: 'Server Operation', op: 'demo.server-trace' },
async () => {
await goodServerFunc()
},
)
setResults((prev) => ({
...prev,
serverTrace: { type: 'success', spanOp: 'demo.server-trace' },
}))
} finally {
setIsLoading((prev) => ({ ...prev, serverTrace: false }))
}
}
return (
<div
className="min-h-screen text-white"
style={{
fontFamily:
'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif',
background:
'linear-gradient(180deg, #181423 0%, #1C1825 50%, #181423 100%)',
}}
>
<div className="max-w-5xl mx-auto px-6 py-16">
{/* Header */}
<div className="text-center mb-16">
<div className="inline-flex items-center gap-4 mb-8">
<div className="text-[#7553FF]">
<SentryLogo size={56} />
</div>
<div className="text-left">
<h1 className="text-3xl font-bold text-white tracking-tight">
Sentry Demo
</h1>
<p className="text-[#A49FB5] text-sm">
Error monitoring & performance tracing
</p>
</div>
</div>
<p className="text-lg text-[#A49FB5] max-w-xl mx-auto leading-relaxed">
Click the buttons below to trigger errors and traces, then view them
in your{' '}
<a
href="https://sentry.io"
target="_blank"
rel="noopener noreferrer"
className="text-[#7553FF] hover:text-[#B3A1FF] underline transition-colors"
>
Sentry dashboard
</a>
.
</p>
</div>
{/* Sentry Not Initialized Warning */}
{showWarning && (
<div className="mb-8 flex items-center gap-3 bg-[#E5A000]/10 border border-[#E5A000]/30 rounded-xl px-6 py-4">
<svg
className="w-6 h-6 text-[#E5A000] flex-shrink-0"
fill="none"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
viewBox="0 0 24 24"
stroke="currentColor"
>
<title>Warning</title>
<path d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
<div>
<p className="text-[#E5A000] font-medium">
Sentry is not initialized
</p>
<p className="text-[#A49FB5] text-sm mt-1">
Set the{' '}
<code className="bg-[#1C1825] px-1.5 py-0.5 rounded text-[#B3A1FF]">
VITE_SENTRY_DSN
</code>{' '}
environment variable to enable error tracking and performance
monitoring.
</p>
</div>
</div>
)}
{/* Features Grid */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-12">
<FeatureCard
icon={
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z" />
</svg>
}
title="Error Monitoring"
description="Client & server error tracking"
/>
<FeatureCard
icon={
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M13 3v18h-2V3h2zm6 6v12h-2V9h2zM7 14v7H5v-7h2z" />
</svg>
}
title="Performance"
description="Tracing and spans visualization"
/>
<FeatureCard
icon={
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M17 10.5V7c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v10c0 .55.45 1 1 1h12c.55 0 1-.45 1-1v-3.5l4 4v-11l-4 4z" />
</svg>
}
title="Session Replay"
description="Real user session playback"
/>
<FeatureCard
icon={
<svg className="w-6 h-6" fill="currentColor" viewBox="0 0 24 24">
<path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z" />
</svg>
}
title="Real-time Alerts"
description="Instant issue notifications"
/>
</div>
{/* Testing Panels */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* Client-Side Panel */}
<div className="bg-[#1C1825] rounded-2xl p-8 border border-[#2D2640]">
<div className="flex items-center gap-3 mb-6">
<div className="w-3 h-3 rounded-full bg-[#00F261]" />
<h2 className="text-xl font-semibold">Client-Side Testing</h2>
</div>
<div className="space-y-4">
<div>
<SentryButton
variant="error"
onClick={handleClientError}
loading={isLoading.clientError}
disabled={sentryConfigured === false}
>
Trigger Client Error
</SentryButton>
{isLoading.clientError && (
<ProgressBar loading={isLoading.clientError} />
)}
{results.clientError && !isLoading.clientError && (
<ResultBadge
type={results.clientError.type}
spanOp={results.clientError.spanOp}
onCopy={() => {}}
/>
)}
</div>
<div>
<SentryButton
variant="primary"
onClick={handleClientTrace}
loading={isLoading.clientTrace}
disabled={sentryConfigured === false}
>
Test Client Trace
</SentryButton>
{isLoading.clientTrace && (
<ProgressBar loading={isLoading.clientTrace} />
)}
{results.clientTrace && !isLoading.clientTrace && (
<ResultBadge
type={results.clientTrace.type}
spanOp={results.clientTrace.spanOp}
onCopy={() => {}}
/>
)}
</div>
</div>
</div>
{/* Server-Side Panel */}
<div className="bg-[#1C1825] rounded-2xl p-8 border border-[#2D2640]">
<div className="flex items-center gap-3 mb-6">
<div className="w-3 h-3 rounded-full bg-[#7553FF]" />
<h2 className="text-xl font-semibold">Server-Side Testing</h2>
</div>
<div className="space-y-4">
<div>
<SentryButton
variant="error"
onClick={handleServerError}
loading={isLoading.serverError}
disabled={sentryConfigured === false}
>
Trigger Server Error
</SentryButton>
{isLoading.serverError && (
<ProgressBar loading={isLoading.serverError} />
)}
{results.serverError && !isLoading.serverError && (
<ResultBadge
type={results.serverError.type}
spanOp={results.serverError.spanOp}
onCopy={() => {}}
/>
)}
</div>
<div>
<SentryButton
variant="primary"
onClick={handleServerTrace}
loading={isLoading.serverTrace}
disabled={sentryConfigured === false}
>
Test Server Trace
</SentryButton>
{isLoading.serverTrace && (
<ProgressBar loading={isLoading.serverTrace} />
)}
{results.serverTrace && !isLoading.serverTrace && (
<ResultBadge
type={results.serverTrace.type}
spanOp={results.serverTrace.spanOp}
onCopy={() => {}}
/>
)}
</div>
</div>
</div>
</div>
{/* Footer Note */}
<div className="mt-12 text-center">
<p className="text-sm text-[#6E6C75]">
This page uses{' '}
<code className="bg-[#1C1825] px-2 py-1 rounded text-[#B3A1FF]">
@sentry/tanstackstart-react
</code>{' '}
for full-stack error monitoring.
<br />
<a
href="https://docs.sentry.io/platforms/javascript/guides/tanstackstart-react/"
target="_blank"
rel="noopener noreferrer"
className="text-[#7553FF] hover:text-[#B3A1FF] underline transition-colors"
>
Read the documentation
</a>
</p>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,45 @@
import { createFileRoute } from '@tanstack/react-router'
import { useQuery } from '@tanstack/react-query'
export const Route = createFileRoute('/demo/tanstack-query')({
component: TanStackQueryDemo,
})
function TanStackQueryDemo() {
const { data } = useQuery({
queryKey: ['todos'],
queryFn: () =>
Promise.resolve([
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' },
{ id: 3, name: 'Charlie' },
]),
initialData: [],
})
return (
<div
className="flex items-center justify-center min-h-screen bg-gradient-to-br from-purple-100 to-blue-100 p-4 text-white"
style={{
backgroundImage:
'radial-gradient(50% 50% at 95% 5%, #f4a460 0%, #8b4513 70%, #1a0f0a 100%)',
}}
>
<div className="w-full max-w-2xl p-8 rounded-xl backdrop-blur-md bg-black/50 shadow-xl border-8 border-black/10">
<h1 className="text-2xl mb-4">
TanStack Query Simple Promise Handling
</h1>
<ul className="mb-4 space-y-2">
{data.map((todo) => (
<li
key={todo.id}
className="bg-white/10 border border-white/20 rounded-lg p-3 backdrop-blur-sm shadow-md"
>
<span className="text-lg text-white">{todo.name}</span>
</li>
))}
</ul>
</div>
</div>
)
}

120
src/routes/index.tsx Normal file
View File

@@ -0,0 +1,120 @@
import { Button } from "@heroui/react"
import { createFileRoute } from "@tanstack/react-router"
import {
Route as RouteIcon,
Server,
Shield,
Sparkles,
Waves,
Zap,
} from "lucide-react"
export const Route = createFileRoute("/")({ component: App })
function App() {
const features = [
{
icon: <Zap className="w-12 h-12 text-cyan-400" />,
title: "Powerful Server Functions",
description:
"Write server-side code that seamlessly integrates with your client components. Type-safe, secure, and simple.",
},
{
icon: <Server className="w-12 h-12 text-cyan-400" />,
title: "Flexible Server Side Rendering",
description:
"Full-document SSR, streaming, and progressive enhancement out of the box. Control exactly what renders where.",
},
{
icon: <RouteIcon className="w-12 h-12 text-cyan-400" />,
title: "API Routes",
description:
"Build type-safe API endpoints alongside your application. No separate backend needed.",
},
{
icon: <Shield className="w-12 h-12 text-cyan-400" />,
title: "Strongly Typed Everything",
description:
"End-to-end type safety from server to client. Catch errors before they reach production.",
},
{
icon: <Waves className="w-12 h-12 text-cyan-400" />,
title: "Full Streaming Support",
description:
"Stream data from server to client progressively. Perfect for AI applications and real-time updates.",
},
{
icon: <Sparkles className="w-12 h-12 text-cyan-400" />,
title: "Next Generation Ready",
description:
"Built from the ground up for modern web applications. Deploy anywhere JavaScript runs.",
},
]
return (
<div className="min-h-screen bg-gradient-to-b from-slate-900 via-slate-800 to-slate-900">
<Button> Hola</Button>
<section className="relative py-20 px-6 text-center overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-r from-cyan-500/10 via-blue-500/10 to-purple-500/10"></div>
<div className="relative max-w-5xl mx-auto">
<div className="flex items-center justify-center gap-6 mb-6">
<img
src="/tanstack-circle-logo.png"
alt="TanStack Logo"
className="w-24 h-24 md:w-32 md:h-32"
/>
<h1 className="text-6xl md:text-7xl font-black text-white [letter-spacing:-0.08em]">
<span className="text-gray-300">TANSTACK</span>{" "}
<span className="bg-gradient-to-r from-cyan-400 to-blue-400 bg-clip-text text-transparent">
START
</span>
</h1>
</div>
<p className="text-2xl md:text-3xl text-gray-300 mb-4 font-light">
The framework for next generation AI applications
</p>
<p className="text-lg text-gray-400 max-w-3xl mx-auto mb-8">
Full-stack framework powered by TanStack Router for React and Solid.
Build modern applications with server functions, streaming, and type
safety.
</p>
<div className="flex flex-col items-center gap-4">
<a
href="https://tanstack.com/start"
target="_blank"
rel="noopener noreferrer"
className="px-8 py-3 bg-cyan-500 hover:bg-cyan-600 text-white font-semibold rounded-lg transition-colors shadow-lg shadow-cyan-500/50"
>
Documentation
</a>
<p className="text-gray-400 text-sm mt-2">
Begin your TanStack Start journey by editing{" "}
<code className="px-2 py-1 bg-slate-700 rounded text-cyan-400">
/src/routes/index.tsx
</code>
</p>
</div>
</div>
</section>
<section className="py-16 px-6 max-w-7xl mx-auto">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{features.map((feature, index) => (
<div
key={index}
className="bg-slate-800/50 backdrop-blur-sm border border-slate-700 rounded-xl p-6 hover:border-cyan-500/50 transition-all duration-300 hover:shadow-lg hover:shadow-cyan-500/10"
>
<div className="mb-4">{feature.icon}</div>
<h3 className="text-xl font-semibold text-white mb-3">
{feature.title}
</h3>
<p className="text-gray-400 leading-relaxed">
{feature.description}
</p>
</div>
))}
</div>
</section>
</div>
)
}