The events page (/events) displays Tech for Palestine events fetched from a Notion database with real-time polling and image caching.
Notion Database → API Route → Frontend Component
↓ ↓
/api/events Events.tsx
Entry point that fetches initial events data and renders the Events component.
---
import { fetchNotionEvents } from "../store/notionClient";
let events = await fetchNotionEvents();
---
<Events events={events} loading={loading} client:only="react" />React component that handles:
- Real-time Updates: Polls
/api/eventsevery 30 seconds - Event Display: Shows events in card format with images, dates, and status
- Error Handling: Graceful fallbacks for failed images
Key Features:
- Auto-refresh with intelligent change detection
- Loading states and error handling
- Responsive card layout with Material-UI
- Image fallback to T4P logo on error
Astro API endpoint that:
- Fetches events from Notion via
notionClient.ts - Returns fresh data without caching (
no-cacheheaders) - Handles errors gracefully
Core integration with Notion API:
fetchNotionEvents(): Gets all events from databasefetchNotionEventById(): Gets single event details- Image URL processing with proxy integration
interface EventItem {
id: string; // Notion page ID
title: string; // Event title
date: string; // ISO date string
status: string; // "Past" or "Upcoming"
location: string; // Event type/location
image: string; // Header image URL (direct Notion URL or default)
link: string; // Notion page URL
description?: string; // Event description
registerLink?: string; // Registration URL
recordingLink?: string; // Recording URL
}Notion images are served directly as Notion-hosted URLs. On error, the component falls back to the default image.
<img
src={event.image}
onError={(e) => {
if (target.src !== "/images/default.jpg") {
target.src = "/images/default.jpg";
}
}}
/># Required for Notion integration
NOTION_SECRET=secret_xxx
NOTION_DB_ID=database-idThe Events component automatically polls for updates:
useEffect(() => {
const interval = setInterval(pollForUpdates, 30000); // 30 seconds
return () => clearInterval(interval);
}, [events, isPolling]);Change Detection:
- Compares event arrays for additions, deletions, or modifications
- Only updates state when actual changes are detected
- Prevents unnecessary re-renders
Standard Astro build deployed to Cloudflare Pages:
pnpm buildsrc/
├── pages/
│ ├── events.astro # Events page entry point
│ ├── event-details.astro # Individual event details
│ └── api/events.ts # API endpoint
├── components/
│ ├── Events.tsx # Main events component
│ └── EventDetails.tsx # Event detail component
└── store/
└── notionClient.ts # Notion API integration
- Notion-hosted images may expire after ~1 hour — this is a Notion limitation
- The component falls back to
/images/default.jpgon error
- Check Notion API credentials
- Verify database permissions
- Check browser console for API errors
Returns array of event objects sorted by date (newest first).
Response:
[
{
"id": "notion-page-id",
"title": "Event Title",
"date": "2025-07-23",
"status": "Upcoming",
"location": "Virtual",
"image": "https://notion-hosted-url.com/...",
"description": "Event description",
"registerLink": "https://register.url",
"recordingLink": null
}
]pnpm dev # Start Astro dev server on :4321- Copy
.env.exampleto.env - Add Notion credentials