React News Dashboard Tutorial 2026: SSE + TypeScript

Kent Hudson

Kent Hudson

·

21 minuten Lesen

React News Dashboard Tutorial 2026: SSE + TypeScript

Erstellen Sie ein Echtzeit-Nachrichten-Dashboard mit React und APITube

Echtzeit-Nachrichten-Dashboards sehen einfach aus, bis Sie eines liefern. Sie beginnen mit einer 30-Sekunden-Polling-Schleife, stoßen am ersten Freitagmorgen auf ein 429, fügen einen Wiederholungsversuch hinzu, verdoppeln das Intervall, und jetzt ist das Dashboard weder Echtzeit noch zuverlässig.

Dieses Tutorial führt durch ein React Nachrichten-Dashboard, das Ratenbegrenzungen bewältigt, ohne stillschweigend zu brechen, und die sentiment- und category-Filter freilegt, die einen Nachrichtenfeed nützlich und nicht überladen machen.

Sie werden TypeScript, React 18 Hooks und die APITube News API verwenden (Offenlegung: Ich arbeite an APITube). Der Endzustand ist ein Dashboard mit einem Live-aktualisierenden Feed, Filtern und einem funktionierenden Deploy auf Vercel. Der vollständige Quellcode ist auf GitHub – Link am Ende.

Dieser Artikel richtet sich an React-Entwickler, die Hooks und TypeScript-Grundlagen kennen und eine produktionsreife Vorlage wünschen, kein Spielzeug-Demo.

Wichtige Erkenntnisse

  • Verwenden Sie Server-Sent Events für Nachrichten-Feeds, die bis zu 10 Mal pro Sekunde aktualisiert werden – WebSocket ist übertrieben und benötigt Backend-Infrastruktur, die Sie nicht benötigen.
  • Behandeln Sie 429 Too Many Requests als UX-Zustand: Zeigen Sie ein Banner „Verbindung wird in Xs wiederhergestellt“ mit exponentiellem Backoff (2 → 4 → 8 → 16 → 32 → 60 Sekunden, die gleiche Kurve, die EventSource verwendet).
  • Halten Sie einen In-Memory-Puffer-Cap (100 Artikel) ein, um über lange Sitzungen hinweg keinen Speicher zu verlieren.
  • Die kostenlose Stufe von APITube (100 Anfragen pro Tag + SSE Zugriff) reicht für Produktions-Experimente aus.
  • Full Stack: React 18, TypeScript, Vite, Vercel – kein Node-Server, kein Pub/Sub-Broker, kein kostenpflichtiges SaaS.

1. Was Sie bauen werden

Ein Echtzeit-Nachrichten-Dashboard ist eine Single-Page-Web-App, die Artikel aus einer Nachrichten API streamt, wenn sie veröffentlicht werden, sie clientseitig filtert und ohne Seitenaktualisierung aktualisiert. Dieses Tutorial erstellt eines mit drei Panels: einen Live-Feed der neuesten Artikel, eine Kategoriefilter-Seitenleiste (Technologie, Wirtschaft, Sport…) und einen Sentiment-Chip auf jeder Karte (grün / neutral / rot). Artikel werden mit Server-Sent Events gestreamt — keine Seitenaktualisierung, keine Polling-Schleife, kein Backend-Broker.

Die Architektur ist minimal: kein Node-Server, kein WebSocket-Broker, kein Pusher-Konto. Die React-App spricht direkt mit APITube über den REST-Endpunkt für das erste Rendering und den Server-Sent Events-Endpunkt für Live-Updates.

Stack:

  • React 18 + TypeScript (Vite)
  • Native fetch und EventSource — keine Client-Bibliothek
  • Zwei benutzerdefinierte Hooks: useRateLimitedFetch, useNewsStream
  • Vercel für Deployment

2. Voraussetzungen

Sie benötigen Node 20+, fundierte Kenntnisse von React Hooks (useState, useEffect, Cleanup-Funktionen) und einen kostenlosen APITube API Schlüssel. Die Anmeldung ist nur per E-Mail möglich, keine Karte erforderlich. Die kostenlose Stufe gewährt Ihnen 100 Anfragen pro Tag plus SSE Zugriff – genug für diesen Build und für Produktions-Experimente.

Grundlegende TypeScript wird angenommen (Schnittstellen, Generika, Union-Typen). Keine Backend-Arbeit erforderlich.

3. Polling vs SSE vs WebSocket: Auswahl des Echtzeit-Transports

Bevor Sie Code schreiben, wählen Sie den richtigen Mechanismus. Die meisten React-Dashboards verwenden standardmäßig WebSocket, da diese für die "Echtzeit"-Artikel auf Google am besten ranken, aber für Nachrichten-Daten ist WebSocket fast immer übertrieben.

Das Entscheidungsgerüst:

SzenarioAktualisierungsfrequenzEmpfohlener TransportWarum
Abfragen<1 Ereignis / 30 ssetInterval + AbrufGünstig, trivial, funktioniert über jeden Proxy
Server-Sent Events1-10 Ereignisse/Sek., nur Server→ClientEventSourceAutomatische Wiederverbindung, HTTP/2-freundlich, kein Broker
WebSocket>10 Ereignisse/Sek. oder bidirektionalws + ServerVoller Roundtrip, benötigt aber Infrastruktur

Nachrichtenereignisse treffen höchstens wenige pro Sekunde pro Filter. Das bringt sie direkt in den SSE-Bereich. WebSocket ist übertrieben – Ereignisse treffen weit unter 10 pro Sekunde, daher gewinnt SSE bei der Komplexität, ohne die Latenz zu beeinträchtigen.

Sie implementieren immer noch einen Polling-Fallback für das erste Rendern (damit der Feed nicht für 0.5 s leer ist, während SSE sich verbindet) und für Browser, die EventSource hinter aggressiven Proxys blockieren.

Was ist der beste Weg, um einen Echtzeit-Feed in React zu erstellen?

Der beste Weg, einen Echtzeit-Feed in React zu erstellen, sind Server-Sent Events für Feeds, die sich weniger als zehnmal pro Sekunde aktualisieren, kombiniert mit einem anfänglichen REST-Abruf für das erste Rendern. SSE stellt automatisch eine Verbindung wieder her, funktioniert über Proxys und benötigt keine Server-Infrastruktur, wenn Ihre Datenquelle bereits einen SSE-Endpunkt bereitstellt.

4. Projekteinrichtung

Erstellen Sie eine Vite + React + TypeScript-App:

npm create vite@latest news-dashboard -- --template react-ts
cd news-dashboard
npm install
npm install --save-dev @types/node

Erstellen Sie eine .env.local im Projektstammverzeichnis:

VITE_APITUBE_API_KEY=your_key_here

Projektstruktur (src/ Verzeichnis):

src/
  hooks/
    useRateLimitedFetch.ts
    useNewsStream.ts
  components/
    NewsCard.tsx
    CategoryFilter.tsx
    NewsDashboard.tsx
  types/
    news.ts
  lib/
    apitube.ts
  App.tsx
  main.tsx

Wie strukturiere ich ein React-Dashboard-Projekt?

Strukturieren Sie ein React Dashboard-Projekt um Datenanliegen, nicht um Seitenlayout. Platzieren Sie die Abruflogik in hooks/, Typen in types/, API-Clients in lib/ und Präsentationskomponenten in components/. Jede Komponente, die ein Abfrageergebnis rendert, sollte ihre Daten von einem einzigen typisierten Hook erhalten – rufen Sie niemals fetch inline auf. Dies hält Caching, Wiederholungsversuche und Rate-Limit-Logik an einem Ort.

5. Nachrichtenabruf mit dem APITube API

Beginnen Sie mit dem REST-Endpunkt. Definieren Sie den Antworttyp einmal und verwenden Sie ihn überall wieder.

src/types/news.ts:

export interface NewsArticle {
  id: string;
  title: string;
  description: string;
  href: string;
  published_at: string;
  language: { code: string };
  author?: { name: string };
  categories?: { id: string; name: string }[];
  sentiment?: {
    overall: { score: number; polarity: 'positive' | 'neutral' | 'negative' };
    title: { score: number };
    body: { score: number };
  };
  source?: { domain: string; country?: string };
}

export interface NewsResponse {
  status: string;
  results: NewsArticle[];
  has_next_pages: boolean;
  page: number;
  per_page: number;
}

src/lib/apitube.ts:

const BASE_URL = 'https://api.apitube.io';
const API_KEY = import.meta.env.VITE_APITUBE_API_KEY;

export async function fetchNews(params: URLSearchParams): Promise<NewsResponse> {
  const res = await fetch(`${BASE_URL}/v1/news/everything?${params}`, {
    headers: { 'X-API-Key': API_KEY },
  });
  if (!res.ok) throw { status: res.status, message: res.statusText };
  return res.json();
}

APITube authentifiziert sich mit dem X-API-Key-Header (kein Bearer-Token – ein häufiger Fehler bei der Migration von anderen Nachrichten-APIs). Die Antwortstruktur ist über /v1/news/everything und den SSE-Stream stabil, sodass derselbe NewsArticle-Typ beide abdeckt.

Wie rufe ich Nachrichten-Daten in einer React-App ab?

Nachrichten-Daten in einer React-App abrufen mit einem typisierten Wrapper um die Nachrichten-API plus einem benutzerdefinierten Hook, der den Lade- und Fehlerstatus verwaltet. Rendern Sie immer aus einem einzigen Hook pro Datenquelle — rufen Sie fetch niemals direkt in einer Komponente auf — sodass Ratenlimit-Handling, Caching und Authentifizierung an einem Ort leben. Übergeben Sie URLSearchParams in den Wrapper, um die Abfragekonstruktion typsicher zu halten.

6. Echtzeit-Updates mit Server-Sent Events

Der SSE-Endpunkt von APITube unter /v1/news/stream sendet neue Artikel, die mit denselben Filtern wie der REST-Endpunkt übereinstimmen. Der EventSource des Browsers verwaltet die Verbindung, Wiederverbindung und Backoff ohne zusätzliche Bibliothek.

src/hooks/useNewsStream.ts:

import { useEffect, useState } from 'react';
import type { NewsArticle } from '../types/news';

interface StreamOptions {
  category?: string;
  language?: string;
  enabled?: boolean;
}

export function useNewsStream(opts: StreamOptions) {
  const [articles, setArticles] = useState<NewsArticle[]>([]);
  const [status, setStatus] = useState<'connecting' | 'open' | 'closed'>('closed');

  useEffect(() => {
    if (!opts.enabled) return;

    const params = new URLSearchParams({
      'api_key': import.meta.env.VITE_APITUBE_API_KEY,
      'language.code': opts.language ?? 'en',
    });
    if (opts.category) params.set('category.id', opts.category);

    const url = `https://api.apitube.io/v1/news/stream?${params}`;
    const source = new EventSource(url);
    setStatus('connecting');

    source.onopen = () => setStatus('open');
    source.onmessage = (event) => {
      const article = JSON.parse(event.data) as NewsArticle;
      setArticles((prev) => [article, ...prev].slice(0, 100));
    };
    source.onerror = () => setStatus('closed');

    return () => {
      source.close();
      setStatus('closed');
    };
  }, [opts.category, opts.language, opts.enabled]);

  return { articles, status };
}

Drei Details, die in der Produktion wichtig sind:

  1. Der API Schlüssel befindet sich in der Query-String für SSE. Browser können keine benutzerdefinierten Header für EventSource-Anfragen setzen. APITube akzeptiert sowohl X-API-Key-Header (REST) als auch api_key-Query-Parameter (SSE).
  2. Begrenzen Sie den In-Memory-Puffer auf 100 Artikel. Ohne dies wird ein beschäftigter Filter wie language.code=en einen Tab über eine lange Sitzung hinweg aus dem Speicher laufen lassen.
  3. Eine Bereinigungsfunktion zurückgeben, die die EventSource schließt. React StrictMode wird in der Entwicklung gemountet-unmountet-remountet – ohne Bereinigung öffnen Sie zwei Verbindungen und verbrennen Ihr Rate Limit.

Wie verwende ich Server-Sent Events in React?

Verwenden Sie Server-Sent Events in React, indem Sie eine EventSource innerhalb einer useEffect erstellen, den Status im onmessage-Handler aktualisieren und eine Bereinigungsfunktion zurückgeben, die source.close() aufruft. Der Browser kümmert sich automatisch um die Wiederverbindung mit exponentiellem Backoff. Übergeben Sie Abfrageparameter, um den Stream serverseitig zu filtern, damit Sie keine Artikel übertragen, die die Benutzeroberfläche verwerfen würde.

7. Umgang mit Ratenbegrenzungen: Backoff und veralteter Cache

Die kostenlose Stufe beträgt 100 Anfragen pro Tag. Der SSE-Stream zählt nicht dagegen – aber der REST-Fallback, der anfängliche Seitenaufruf und Filteränderungen schon. Wenn Sie das Limit erreichen, gibt APITube 429 Too Many Requests zurück.

Ratenbegrenzungen sind ein UX-Problem, kein Backend-Problem. Wenn ein Dashboard 429 Too Many Requests erreicht, muss der Benutzer wissen, dass der Feed eingefroren ist und wie lange – nicht nur auf stumm geschaltete, veraltete Daten starren. Zeigen Sie ein Banner an, zählen Sie herunter und bedienen Sie den zuletzt bekannten guten Cache.

Die meisten Tutorials versuchen stillschweigend, bei 429 erneut zu versuchen. Dieses Muster ist in Ordnung für einen Hintergrundjob. Für ein Live-Dashboard bricht es den Vertrag: Der Benutzer sieht "frische" Zeitstempel auf Daten, die Minuten alt sind.

src/hooks/useRateLimitedFetch.ts:

import { useEffect, useRef, useState } from 'react';
import { fetchNews } from '../lib/apitube';
import type { NewsResponse } from '../types/news';

interface State {
  data: NewsResponse | null;
  error: Error | null;
  retryIn: number;  // seconds until next attempt, 0 if idle
  isStale: boolean; // true when showing cached data during backoff
}

export function useRateLimitedFetch(params: URLSearchParams) {
  const [state, setState] = useState<State>({
    data: null, error: null, retryIn: 0, isStale: false,
  });
  const cacheRef = useRef<Map<string, NewsResponse>>(new Map());

  useEffect(() => {
    const key = params.toString();
    let cancelled = false;
    let attempt = 0;

    async function run() {
      // Serve stale cache immediately if we have it.
      const cached = cacheRef.current.get(key);
      if (cached) setState((s) => ({ ...s, data: cached, isStale: true }));

      while (!cancelled) {
        try {
          const data = await fetchNews(params);
          cacheRef.current.set(key, data);
          setState({ data, error: null, retryIn: 0, isStale: false });
          return;
        } catch (err: any) {
          if (err.status !== 429) {
            setState((s) => ({ ...s, error: err }));
            return;
          }
          // Exponential backoff: 2, 4, 8, 16, 32, 60 (capped) seconds.
          const delay = Math.min(2 ** (attempt + 1), 60);
          attempt += 1;

          for (let i = delay; i > 0 && !cancelled; i--) {
            setState((s) => ({ ...s, retryIn: i, isStale: true }));
            await new Promise((r) => setTimeout(r, 1000));
          }
        }
      }
    }

    run();
    return () => { cancelled = true; };
  }, [params.toString()]);

  return state;
}

Der Hook erledigt drei Dinge, die Sie von einem naiven fetch nicht bekommen:

  • Stale-while-revalidate: Wenn eine gecachte Antwort für dieselbe Abfrage existiert, wird sie sofort gerendert, während im Hintergrund ein frischer Abruf läuft.
  • Exponentielles Backoff begrenzt auf 60 s: 2 → 4 → 8 → 16 → 32 → 60. Dies ist dieselbe Kurve, die der native EventSource des Browsers verwendet.
  • Exponiert retryIn, sodass die UI ein Banner „Verbindung wird wiederhergestellt in 12s…“ rendern kann. Der Benutzer weiß, was passiert. Er schließt den Tab nicht.

Wie gehe ich mit API-Ratenlimits in einer React-App um?

API Ratenlimits in einer React-App mit exponentiellem Backoff bei 429-Antworten plus einem Stale-While-Revalidate-Cache behandeln. Cachen Sie die letzte erfolgreiche Antwort pro Abfrageschlüssel, rendern Sie sie sofort, wenn der Hook erneut ausgeführt wird, und stellen Sie einen retryIn-Zähler bereit, damit die UI ein Banner „Verbindung wird in Xs wiederhergestellt“ anzeigen kann, anstatt stillschweigend erneut zu versuchen. Behandeln Sie 429 als benutzerorientierten Zustand, nicht als Backend-Detail.

8. Filtern nach Kategorie, Sprache und Sentiment

Das Dashboard wäre kein Dashboard ohne Filter. APITube gibt strukturierte categories, language und – einzigartig – ein sentiment-Objekt mit overall, title und body-Scores für jeden Artikel zurück.

Sentiment-Filterung ist wichtiger, als es klingt. Ein Krypto-Dashboard, ein Markenüberwachungstool und eine Finanznachrichten-App müssen alle negative Signale schnell erkennen. Die meisten Nachrichten-APIs liefern Ihnen einen rohen Textblock und überlassen die Bewertung Ihnen.

src/components/NewsDashboard.tsx:

import { useMemo, useState } from 'react';
import { useRateLimitedFetch } from '../hooks/useRateLimitedFetch';
import { useNewsStream } from '../hooks/useNewsStream';
import { NewsCard } from './NewsCard';
import { CategoryFilter } from './CategoryFilter';

export function NewsDashboard() {
  const [category, setCategory] = useState<string | undefined>();
  const [sentimentFilter, setSentimentFilter] =
    useState<'all' | 'positive' | 'negative'>('all');

  const params = useMemo(() => {
    const p = new URLSearchParams({ 'language.code': 'en', per_page: '20' });
    if (category) p.set('category.id', category);
    return p;
  }, [category]);

  const initial = useRateLimitedFetch(params);
  const stream = useNewsStream({ category, language: 'en', enabled: true });

  const articles = useMemo(() => {
    const combined = [...stream.articles, ...(initial.data?.results ?? [])];
    if (sentimentFilter === 'all') return combined;
    return combined.filter(
      (a) => a.sentiment?.overall.polarity === sentimentFilter
    );
  }, [stream.articles, initial.data, sentimentFilter]);

  return (
    <div className="dashboard">
      <aside>
        <CategoryFilter value={category} onChange={setCategory} />
        <select
          value={sentimentFilter}
          onChange={(e) => setSentimentFilter(e.target.value as any)}
        >
          <option value="all">All sentiment</option>
          <option value="positive">Positive</option>
          <option value="negative">Negative</option>
        </select>
      </aside>

      <main>
        {initial.retryIn > 0 && (
          <div className="banner">
            Rate-limited — reconnecting in {initial.retryIn}s
            {initial.isStale && ' (showing cached results)'}
          </div>
        )}
        <div className="status">Stream: {stream.status}</div>
        {articles.map((a) => <NewsCard key={a.id} article={a} />)}
      </main>
    </div>
  );
}

NewsCard ist eine dünne, präsentationsorientierte Komponente – nur Titel, Quelle, Zeitstempel und ein Sentiment-Chip, der grün/grau/rot basierend auf article.sentiment?.overall.polarity gefärbt ist. Vollständiger Quellcode im Repository.

9. Bereitstellung für Vercel

Die gesamte App ist statisch + clientseitig – keine Serverlaufzeit, keine API-Routen. Vercel erledigt das mit einem einzigen Befehl.

npm install -g vercel
vercel

Einrichtung der Umgebungsvariablen:

Vite präfigiert clientseitig exponierte Variablen mit VITE_. Das bedeutet, Ihr API-Schlüssel wird im Bundle mitgeliefert und gegen APITube vom Browser des Benutzers ausgeführt. Für ein persönliches Dashboard oder ein authentifiziertes Admin-Panel ist das in Ordnung. Für eine öffentliche Marketing-Website proxyen Sie stattdessen über eine serverlose Funktion.

Fügen Sie den Schlüssel im Vercel-Dashboard unter Projekteinstellungen → Umgebungsvariablen hinzu:

VITE_APITUBE_API_KEY=<your_key>

CORS: APITube erlaubt jeden Ursprung auf beiden REST und SSE Endpunkten, wenn die Anfrage einen gültigen API Schlüssel enthält, daher ist keine CORS Konfiguration auf Ihrer Seite erforderlich. Wenn Sie zu einer Proxy-Architektur wechseln, setzen Sie Access-Control-Allow-Origin auf Ihrer Vercel-Funktion.

Bereitstellen und Sie haben ein funktionierendes React News Dashboard unter <project>.vercel.app in weniger als einer Minute.

10. Wohin als Nächstes

Der Build funktioniert. Erweiterungen:

  1. Tauschen Sie den In-Memory-Cache gegen localStorage aus, damit ein Tab-Neuladen keinen leeren Feed anzeigt, während die SSE-Verbindung aufgebaut wird.
  2. Fügen Sie useSWR hinzu, wenn Sie kostenlos komponentenübergreifendes Caching wünschen. Der hier gezeigte useRateLimitedFetch-Hook ist bewusst abhängigkeitsfrei, damit Sie den Mechanismus sehen können.
  3. Fügen Sie media[]-Rendering hinzu für Artikel-Thumbnails — APITube gibt sie in derselben Antwort zurück.
  4. Filtern nach topics[] und entities[] für eine NLP-gestützte Vertikale (Finanzen, Krypto, Sport). APITubes Entity-Extraktion ist integriert.
  5. Tauschen Sie EventSource gegen @microsoft/fetch-event-source aus, wenn Sie benutzerdefinierte Header für die Stream-Verbindung benötigen.

Vollständiger Quellcode: github.com/apitube/react-news-dashboard-tutorial

Häufig gestellte Fragen

Funktioniert dies mit React Server Components?

Teilweise. Der Fetch-Hook läuft problemlos auf dem Client. EventSource ist nur im Browser verfügbar — Sie können keinen Stream in einem RSC await. Rendern Sie das Dashboard als Client Component ('use client') und Sie sind bereit.

Kann ich dieses Muster mit einer anderen Nachrichten-API verwenden?

Ja. Tauschen Sie src/lib/apitube.ts gegen den Wrapper Ihres Anbieters aus und aktualisieren Sie NewsArticle, um seiner Antwortform zu entsprechen. Das Hook-Muster – useRateLimitedFetch + useNewsStream – ist anbieterunabhängig. Beachten Sie, dass die meisten Nachrichten-APIs keinen SSE-Endpunkt bereitstellen, sodass Sie auf das Abfragen alle 30-60 s zurückfallen.

Wie vermeide ich es, im Entwicklungsmodus die kostenlose Stufe zu überschreiten?

Zwischenspeichern Sie aggressiv während der Entwicklung — setzen Sie staleTime: Infinity in Ihrer Cache-Schicht und aktualisieren Sie manuell. Schützen Sie außerdem die SSE-Verbindung mit einem enabled-Flag, damit sie sich nicht bei jedem Hot Reload öffnet.

Funktioniert SSE in Safari?

Ja, seit Safari 5. Alle modernen Browser unterstützen EventSource. Die einzige Eigenart ist, dass Safari die Verbindung nach ca. 60 s Leerlaufzeit stillschweigend schließt – APITube sendet alle 30 s einen Keep-Alive-Kommentar, um dies zu verhindern.

APITube - News API

Verwandte Artikel

Next.js-Tutorial für mehrsprachige Apps 2026: UI Content i18n</h4><h2></h2>
Developer Guides

Next.js-Tutorial für mehrsprachige Apps 2026: UI Content i18n</h4><h2></h2>

Build a Next.js 16 news reader with translated interface text, articles in 60+ languages, and metadata for each language. Includes next-intl, hreflang, a sitemap, and RTL support.

Beste Nachrichten-API für React 2026 (RSC, SWR, TanStack Query)
Insights

Beste Nachrichten-API für React 2026 (RSC, SWR, TanStack Query)

Beste Nachrichten-API für React in 2026 — 6-Auswahl, Next.js 15 RSC-Rezept, useInfiniteQuery-Pagination, SSE-Live-Feed und wie man seinen API-Schlüssel nicht preisgibt.

Real-time news alerts with WebSockets (Socket.IO 2026)
Developer Guides

Real-time news alerts with WebSockets (Socket.IO 2026)

Build real-time news alerts with WebSockets in Node.js using Socket.IO 4.8 and APITube SSE. Room filters, reconnect, Redis adapter, full code.

How to Build a News Aggregator App in 30 Minutes (2026)
Developer Guides

How to Build a News Aggregator App in 30 Minutes (2026)

Learn how to build a news aggregator app in 30 minutes with vanilla JavaScript. Search, filters, rate-limit handling — full working code included.

Wir verwenden Cookies

Durch Klicken auf "Akzeptieren" stimmen Sie der Verwendung von Cookies auf Ihrem Gerät zu, damit die Website funktioniert und für Analysen.