/**
* 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 (
Something went wrong
{error.message}
)
},
})
// Sentry Logo Component
function SentryLogo({ size = 48 }: { size?: number }) {
return (
)
}
// 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
}) {
return (
)
}
// Feature Card Component
function FeatureCard({
icon,
title,
description,
}: {
icon: React.ReactNode
title: string
description: string
}) {
return (
)
}
// 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 (
{type === 'error' && (
Error captured and sent to Sentry
)}
{type === 'success' && (
Trace completed successfully
)}
)
}
// Progress Bar Component
function ProgressBar({ loading }: { loading: boolean }) {
return (
{loading ? 'Running...' : 'Complete'}
)
}
function RouteComponent() {
const [isLoading, setIsLoading] = useState>({})
const [results, setResults] = useState<
Record
>({})
const [sentryConfigured, setSentryConfigured] = useState(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 (
Sentry Demo
Error monitoring & performance tracing
Click the buttons below to trigger errors and traces, then view them
in your{' '}
Sentry dashboard
.
{/* Sentry Not Initialized Warning */}
{showWarning && (
Sentry is not initialized
Set the VITE_SENTRY_DSN environment variable to
enable error tracking and performance monitoring.
)}
{/* Features Grid */}
}
title="Error Monitoring"
description="Client & server error tracking"
/>
}
title="Performance"
description="Tracing and spans visualization"
/>
}
title="Session Replay"
description="Real user session playback"
/>
}
title="Real-time Alerts"
description="Instant issue notifications"
/>
{/* Testing Panels */}
{/* Client-Side Panel */}
Trigger Client Error
{isLoading.clientError && (
)}
{results.clientError && !isLoading.clientError && (
{}}
/>
)}
Test Client Trace
{isLoading.clientTrace && (
)}
{results.clientTrace && !isLoading.clientTrace && (
{}}
/>
)}
{/* Server-Side Panel */}
Trigger Server Error
{isLoading.serverError && (
)}
{results.serverError && !isLoading.serverError && (
{}}
/>
)}
Test Server Trace
{isLoading.serverTrace && (
)}
{results.serverTrace && !isLoading.serverTrace && (
{}}
/>
)}
{/* Footer Note */}
)
}