Vibe coding with AI course · Module 12: Medium Clone
Performance Monitoring - Sentry/Analytics
In this lesson2
Monitoring errors and performance in production.
Sentry Setup
npm install @sentry/nextjs// sentry.client.config.ts
import * as Sentry from "@sentry/nextjs";
Sentry.init({
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
environment: process.env.NODE_ENV
});Error Boundaries
'use client';
import { Component, ReactNode } from 'react';
import * as Sentry from '@sentry/nextjs';
class ErrorBoundary extends Component<
{ children: ReactNode },
{ hasError: boolean }
> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: any) {
Sentry.captureException(error, { contexts: { react: errorInfo } });
}
render() {
if (this.state.hasError) {
return <div>Something went wrong</div>;
}
return this.props.children;
}
}See you soon!
Code for this lesson: App.tsx
'use client';
import { useState } from 'react';
interface Response {
id: string;
author: {
name: string;
avatar: string;
};
content: string;
claps: number;
createdAt: Date;
replies: Response[];
}
const mockResponses: Response[] = [
{
id: '1',
author: {
name: 'Captain Redbeard',
avatar: 'https://i.pravatar.cc/150?img=12'
},
content: 'Great article! Really enjoyed reading about this topic. The examples were particularly helpful.',
claps: 24,
createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2),
replies: [
{
id: '2',
author: {
name: 'Sailor Sam',
avatar: 'https://i.pravatar.cc/150?img=33'
},
content: 'I agree! The practical examples made it easy to understand.',
claps: 8,
createdAt: new Date(Date.now() - 1000 * 60 * 30),
replies: []
}
]
},
{
id: '3',
author: {
name: 'Navigator Nancy',
avatar: 'https://i.pravatar.cc/150?img=45'
},
content: 'Would love to see more content like this. Thanks for sharing!',
claps: 15,
createdAt: new Date(Date.now() - 1000 * 60 * 60),
replies: []
}
];
function ResponseItem({ response }: { response: Response }) {
return (
<div className="border-b border-gray-200 pb-6">
<div className="flex items-start gap-3">
<img
src={response.author.avatar}
alt={response.author.name}
className="w-10 h-10 rounded-full"
/>
<div className="flex-1">
<div className="flex items-center gap-2 mb-2">
<span className="font-semibold">{response.author.name}</span>
<span className="text-sm text-gray-500">2h ago</span>
</div>
<p className="text-gray-800 leading-relaxed mb-3">
{response.content}
</p>
<div className="flex items-center gap-4 text-sm text-gray-500">
<button className="flex items-center gap-1 hover:text-green-600">
<span>Applause</span>
<span>{response.claps}</span>
</button>
<button className="hover:text-gray-700">Reply</button>
</div>
{response.replies.length > 0 && (
<div className="mt-4 pl-6 border-l-2 border-gray-100 space-y-4">
{response.replies.map(reply => (
<ResponseItem key={reply.id} response={reply} />
))}
</div>
)}
</div>
</div>
</div>
);
}
export default function ResponseSection() {
const [content, setContent] = useState('');
const [responses, setResponses] = useState(mockResponses);
const handleSubmit = () => {
if (!content.trim()) return;
alert(`Response posted: ${content}`);
setContent('');
};
return (
<div className="min-h-screen bg-white">
<div className="max-w-2xl mx-auto px-8 py-12">
<h2 className="text-2xl font-bold mb-6">
Responses ({responses.length})
</h2>
{/* Write Response */}
<div className="mb-8 border border-gray-200 rounded-lg p-4">
<textarea
value={content}
onChange={(e) => setContent(e.target.value)}
placeholder="What are your thoughts?"
className="w-full outline-none resize-none text-lg"
rows={3}
/>
<div className="flex justify-end mt-2">
<button
onClick={handleSubmit}
disabled={!content.trim()}
className="px-6 py-2 bg-green-600 text-white rounded-full font-semibold hover:bg-green-700 disabled:bg-gray-300 disabled:cursor-not-allowed"
>
Respond
</button>
</div>
</div>
{/* Responses */}
<div className="space-y-6">
{responses.map(response => (
<ResponseItem key={response.id} response={response} />
))}
</div>
</div>
</div>
);
}Spotted a mistake in this lesson?
Check yourself
Answer the questions from this lesson. Pick an answer to see right away whether it is correct.
1. What is Sentry used for in a Next.js app?
2. Which Error Boundary method is used to report the error to Sentry?
Hands-on tasks in the game
- Code editor
Error Boundary with Sentry reporting
- Vertical ordering
Arrange the steps of handling an error with an Error Boundary: