---
title: "Best News API for React 2026 (RSC, SWR, TanStack Query)"
description: "Best news API for React in 2026 — 6 picks, Next.js 15 RSC recipe, useInfiniteQuery pagination, SSE live feed, and how NOT to leak your API key."
source: https://apitube.io/blog/post/best-news-api-react-2026
---
# Best News API for React Applications in 2026 (RSC, TanStack Query, SWR, and SSE)

Pick the wrong news API for your React app and you get the same failure modes every time: CORS errors in production, an API key shipped to `main.js`, a Next.js build that crashes because the vendor SDK imports `fs`, or a toy `useEffect` + `fetch` that re-fires on every render. The news-API question in 2026 is not really "which vendor?" — it's "which vendor plays well with the way React actually renders today."

**Disclosure.** APITube publishes this blog. APITube is one of the APIs listed below. Every React snippet runs as written.


## TL;DR — the best news API for React in 2026

**The best news API for React in 2026 is APITube, NewsData.io, or Perigon**, because each one uses header-based auth (`X-API-Key`), returns clean JSON, and works identically in a Next.js 15 Server Component, a Route Handler, or a client-side `useQuery`. NewsAPI.org disqualifies itself for real React apps by blocking non-localhost browser origins and using `?apiKey=` query-string auth.

| API | Best React pattern | Browser-safe? | Auth |
|------|--------------------|---------------|------|
| **APITube** | RSC or Route Handler proxy | via proxy | `X-API-Key` header |
| NewsData.io | RSC or Route Handler proxy | via proxy | header |
| Perigon | RSC or Route Handler proxy | via proxy | header |
| GNews | Route Handler proxy | via proxy only | query param |
| TheNewsAPI | Route Handler proxy | via proxy | header |
| NewsAPI.org | Route Handler proxy | **CORS-blocked** from browsers | query param |

The rest of this article is: why you must never put a news API key in a React component, three canonical React patterns (RSC, SWR, TanStack Query), `useInfiniteQuery` for paginated news, a live-news SSE hook, and production UX (Suspense + error boundary + skeleton).


## This article is for you if

You are a React developer shipping to production — CRA, Vite SPA, Next.js App Router, or Remix. You want patterns that don't leak keys, don't re-render like a hamster wheel, and don't break the moment you try to paginate or stream.


### Table of contents

- [Never put your API key in a React component](#keys)
- [The 6 news APIs ranked for React](#apis)
- [Pattern 1: Next.js 15 App Router + async RSC](#rsc)
- [Pattern 2: useSWR for a client-side feed](#swr)
- [Pattern 3: TanStack Query + useInfiniteQuery for paginated news](#query)
- [Live news with SSE in a client component](#sse)
- [Production UX — Suspense, skeleton, error boundary](#ux)
- [useSWR vs TanStack Query for news feeds](#swr-vs-query)
- [What's changing for React in 2026-2027](#2026)
- [FAQ](#faq)
- [Verdict](#verdict)


## Never put your API key in a React component

Most "News API in React" tutorials start like this:

```tsx
// ❌ DON'T: key ships to browser, visible in DevTools → Network → main.js
function NewsFeed() {
  useEffect(() => {
    fetch(`https://api.example.com/news?apiKey=${process.env.NEXT_PUBLIC_NEWS_KEY}`)
      .then((r) => r.json()).then(setArticles);
  }, []);
}
```

Anything prefixed `NEXT_PUBLIC_*` (Next.js) or `REACT_APP_*` (Create React App) is **inlined into the client bundle at build time**. Open DevTools on any page using that component and the key is a few scroll-ticks away in a JS file. Most vendors will rotate or suspend a key the moment it shows up in a public GitHub search.

The fix is not a library — it's architecture. Put the key only in server-side code, then expose a thin proxy:

```tsx
// ✅ DO: app/api/news/route.ts (Next.js 15 Route Handler)
export const runtime = "nodejs"; // or "edge"
export async function GET(req: Request) {
  const { searchParams } = new URL(req.url);
  const q = searchParams.get("q") ?? "news";
  const res = await fetch(
    `https://api.apitube.io/v1/news/everything?title=${encodeURIComponent(q)}&per_page=20`,
    { headers: { "X-API-Key": process.env.APITUBE_KEY! } } // server-only env var
  );
  const data = await res.json();
  return Response.json(data.results);
}
```

Client code now hits `/api/news?q=openai` — key stays on the server. Same pattern in Remix (`loader`/`action`), SvelteKit, Astro server routes, or a tiny Express/Hono proxy for a pure SPA.

NewsAPI.org is a special case: its CORS policy **actively blocks non-localhost origins on the Developer plan**, which means even if you wanted to call it from a React component it wouldn't work in production. A proxy is the only option. Every other major vendor is proxyable by default; NewsAPI.org *requires* it for browser code.

See our [NewsAPI.org migration notes](https://apitube.io/blog/newsapi-org-vs-apitube-why-developers-are-switching) for the full story.


## The 6 news APIs ranked for React

Same template per vendor: recommended pattern, minimal example, one quirk.

### 1. APITube

**Recommended pattern:** Next.js 15 RSC or Route Handler. Native `fetch`, `X-API-Key` header. Supports SSE streaming for live feeds.

```tsx
// RSC
const res = await fetch(
  "https://api.apitube.io/v1/news/everything?title=openai&language.code=en",
  { headers: { "X-API-Key": process.env.APITUBE_KEY! }, next: { revalidate: 60 } }
);
```

**Quirk:** no official React SDK — that's a feature. Response includes `sentiment`, `entities`, `summary`. Works identically in Server Components, Route Handlers, and (via proxy) client hooks.

### 2. NewsData.io

**Recommended pattern:** Route Handler proxy + TanStack Query. Header auth supported.

```tsx
const res = await fetch(
  "https://newsdata.io/api/1/latest?q=openai&language=en",
  { headers: { "X-API-Key": process.env.NEWSDATA_KEY! } }
);
```

**Quirk:** pagination uses a `nextPage` cursor string — ideal for `useInfiniteQuery`.

### 3. Perigon

**Recommended pattern:** RSC. Perigon's event-clustered response maps well onto server-rendered feature cards.

```tsx
const res = await fetch(
  "https://api.goperigon.com/v1/all?q=openai&language=en",
  { headers: { "x-api-key": process.env.PERIGON_KEY! }, next: { revalidate: 120 } }
);
```

**Quirk:** richer schema (clusters, entities) — either render on the server or `zod`-parse to a slim client shape.

### 4. GNews

**Recommended pattern:** Route Handler proxy. Query-param auth means never touching this from a client component.

```tsx
const res = await fetch(
  `https://gnews.io/api/v4/search?q=openai&lang=en&apikey=${process.env.GNEWS_KEY}`
);
```

**Quirk:** lower rate limits; cache aggressively with `next: { revalidate: 300 }`.

### 5. TheNewsAPI

**Recommended pattern:** Route Handler proxy. Header auth.

```tsx
const res = await fetch(
  "https://api.thenewsapi.com/v1/news/all?search=openai&locale=us",
  { headers: { Authorization: `Bearer ${process.env.THENEWSAPI_KEY!}` } }
);
```

**Quirk:** tighter country coverage (~50 countries).

### 6. NewsAPI.org

**Recommended pattern:** Route Handler proxy, mandatory. Do NOT call from client.

```tsx
const url = new URL("https://newsapi.org/v2/everything");
url.searchParams.set("q", "openai");
url.searchParams.set("apiKey", process.env.NEWSAPI_KEY!);
const res = await fetch(url);
```

**Quirk:** CORS-blocked on non-localhost; production requires the $449/mo Business tier. Default is not a good default for shipping React apps.


## Pattern 1: Next.js 15 App Router + async RSC

Server Components let you `await fetch()` directly in a component. No `useState`, no `useEffect`, no client-side hydration of the news data. Key never touches the browser.

```tsx
// app/news/page.tsx
export const revalidate = 60; // ISR: regenerate every 60s

async function fetchNews() {
  const res = await fetch(
    "https://api.apitube.io/v1/news/everything?title=technology&per_page=12",
    { headers: { "X-API-Key": process.env.APITUBE_KEY! }, next: { revalidate: 60 } }
  );
  if (!res.ok) throw new Error(`News fetch failed: ${res.status}`);
  const data = await res.json();
  return data.results as Article[];
}

export default async function NewsPage() {
  const articles = await fetchNews();
  return (
    <main className="grid gap-4 p-6">
      {articles.map((a) => (
        <article key={a.href}>
          <h2>{a.title}</h2>
          <p>{a.summary}</p>
          <a href={a.href}>Read →</a>
        </article>
      ))}
    </main>
  );
}
```

Next.js extends `fetch()` with cache controls documented at [nextjs.org/docs/app/api-reference/functions/fetch](https://nextjs.org/docs/app/api-reference/functions/fetch). `next: { revalidate: 60 }` caches for 60 seconds; `cache: 'no-store'` disables caching for genuinely live data.

When the user clicks a filter, trigger a Server Action or navigate to `/news?topic=x` with `useRouter` — the RSC re-renders on the server with the new query, and only the HTML diff streams to the client.


## Pattern 2: useSWR for a client-side feed

Use SWR when the feed is simple: latest headlines, no pagination, no complex cache invalidation. SWR is **5.3 KB** gzipped and has sensible defaults — revalidate on focus, revalidate on reconnect, dedupe identical requests.

```tsx
"use client";
import useSWR from "swr";

const fetcher = (url: string) => fetch(url).then((r) => r.json());

export function NewsList({ query }: { query: string }) {
  const { data, error, isLoading } = useSWR(
    `/api/news?q=${encodeURIComponent(query)}`,   // your Route Handler, not the vendor
    fetcher,
    { refreshInterval: 60_000 }                    // poll every minute
  );

  if (isLoading) return <NewsSkeleton />;
  if (error) return <NewsError onRetry={() => location.reload()} />;
  if (!data?.length) return <EmptyFeed />;

  return (
    <ul>
      {data.map((a: Article) => (
        <li key={a.href}><a href={a.href}>{a.title}</a></li>
      ))}
    </ul>
  );
}
```

Note: the hook hits `/api/news`, not the vendor. The key is on the server.


## Pattern 3: TanStack Query + useInfiniteQuery for paginated news

TanStack Query is **16.2 KB** gzipped and earns its weight when you need pagination, mutations, and shared cache state across the tree. `useInfiniteQuery` is the canonical pattern for infinite-scroll news feeds. Docs: [tanstack.com/query](https://tanstack.com/query/latest/docs/framework/react/overview).

```tsx
"use client";
import { useInfiniteQuery } from "@tanstack/react-query";

type NewsPage = { articles: Article[]; nextCursor?: string };

async function fetchPage({ pageParam }: { pageParam?: string }): Promise<NewsPage> {
  const url = new URL("/api/news", location.origin);
  url.searchParams.set("q", "technology");
  if (pageParam) url.searchParams.set("cursor", pageParam);
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json();
}

export function InfiniteNews() {
  const {
    data, fetchNextPage, hasNextPage, isFetchingNextPage, status,
  } = useInfiniteQuery({
    queryKey: ["news", "technology"],
    queryFn: fetchPage,
    initialPageParam: undefined as string | undefined,
    getNextPageParam: (last) => last.nextCursor,
    staleTime: 30_000,
  });

  if (status === "pending") return <NewsSkeleton />;
  if (status === "error") return <NewsError />;

  return (
    <>
      {data.pages.flatMap((p) => p.articles).map((a) => (
        <article key={a.href}><h3>{a.title}</h3></article>
      ))}
      <button
        onClick={() => fetchNextPage()}
        disabled || isFetchingNextPage}
      >
        {isFetchingNextPage ? "Loading…" : hasNextPage ? "Load more" : "End of feed"}
      </button>
    </>
  );
}
```

Wire the button to an `IntersectionObserver` for true infinite scroll. The Route Handler returns `{ articles, nextCursor }` — cursor comes from the vendor's pagination (NewsData's `nextPage`, APITube's `next_page` / `has_next_pages`, etc.).


## Live news with SSE in a client component

When the product needs push updates, not polling, consume Server-Sent Events. APITube exposes `text/event-stream`. Wrap `EventSource` in a custom hook with reconnect:

```tsx
"use client";
import { useEffect, useState } from "react";

export function useNewsStream(endpoint: string) {
  const [articles, setArticles] = useState<Article[]>([]);
  const [connected, setConnected] = useState(false);

  useEffect(() => {
    const es = new EventSource(endpoint); // hits YOUR proxy, not the vendor
    es.onopen = () => setConnected(true);
    es.onerror = () => setConnected(false); // EventSource auto-reconnects
    es.onmessage = (evt) => {
      const article = JSON.parse(evt.data) as Article;
      setArticles((prev) => [article, ...prev].slice(0, 50)); // cap memory
    };
    return () => es.close();
  }, [endpoint]);

  return { articles, connected };
}

// Usage
function LiveFeed() {
  const { articles, connected } = useNewsStream("/api/news/stream");
  return (
    <>
      <div>Status: {connected ? "🟢 live" : "🔴 reconnecting"}</div>
      <ul>{articles.map((a) => <li key={a.href}>{a.title}</li>)}</ul>
    </>
  );
}
```

The `/api/news/stream` Route Handler opens a long-lived fetch to APITube's SSE endpoint and pipes bytes straight through with `ReadableStream`. No extra library, no polling.


## Production UX — Suspense, skeleton, error boundary

A three-div "Loading..." is not a UX. Compose Suspense + `<ErrorBoundary>` + skeleton:

```tsx
import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";

export function NewsSection() {
  return (
    <ErrorBoundary fallback={<NewsError />}>
      <Suspense fallback={<NewsSkeleton count={6} />}>
        <AsyncNewsList />
      </Suspense>
    </ErrorBoundary>
  );
}

function NewsSkeleton({ count = 6 }) {
  return (
    <ul className="grid gap-4">
      {Array.from({ length: count }).map((_, i) => (
        <li key={i} className="h-24 animate-pulse rounded bg-gray-200" />
      ))}
    </ul>
  );
}
```

Four states to handle — loading, error, empty, success. The empty state matters: a feed filter that returns zero articles should tell users why, not show a void.


## useSWR vs TanStack Query for news feeds

Decision rule by scenario, tuned to news:

| Scenario | Pick | Why |
|----------|------|-----|
| Latest-headlines widget, single query, no pagination | **SWR** | 5.3 KB, focus revalidation is ideal for "what's new?" |
| Search-as-you-type with debounce, 1-2 filters | **SWR** | Dedupe handles it; state is simple |
| Paginated / infinite-scroll news feed | **TanStack Query** | `useInfiniteQuery` is purpose-built |
| Saving articles (read-later), user mutations + cache sync | **TanStack Query** | `useMutation` + `queryClient.invalidateQueries` |
| Dashboard with 4+ news widgets sharing cached state | **TanStack Query** | Global cache, query keys, devtools |

Numeric rule: if the news UI involves one hook per page, SWR is enough. If it involves three or more coordinated queries, or any mutation, go TanStack Query. The 11 KB difference pays for itself the first time you `useInfiniteQuery`.


## What's changing for React in 2026-2027

Three shifts make the React-side news-API choice more consequential:

1. **RSC is now the default in Next.js and most new React frameworks.** News APIs that rely on a browser-only SDK are increasingly awkward; vendors with plain REST + header auth work in the server tree without ceremony. Unlike a browser-first SDK, a clean REST API works identically in Server Components, Route Handlers, and Edge functions, which means you pick one vendor and ship everywhere.

2. **`NEXT_PUBLIC_*` public env vars are under tighter CI scrutiny.** Security linters and deploy-time checks increasingly warn when public env vars are named like API keys. Teams shipping new codebases in 2026 will default to the proxy pattern; the old "just add `NEXT_PUBLIC_`" shortcut is becoming a CI failure.

3. **AI-native React apps retrieve news server-side.** Apps built on Vercel AI SDK, LangChain.js, or Mastra with React UIs fetch news in the server tree before passing context to the model. Browser-side news fetching for AI flows is an anti-pattern. The React news-API pick in 2026 is effectively the Node news-API pick of a server framework that renders React.


## Frequently asked questions

### How do I fetch news in React?

The recommended way to fetch news in React in 2026 is to call a server-side proxy (Next.js Route Handler, Remix loader, or Express endpoint) that holds the API key, because calling a news API directly from a React component leaks the key into the client bundle. For production, pair the proxy with `useSWR` (simple feeds) or TanStack Query `useQuery`/`useInfiniteQuery` (paginated or complex feeds).

### Is React Query better than SWR for news?

TanStack Query is better than SWR for news feeds that need pagination, mutations, or shared cache across components, because `useInfiniteQuery` and `useMutation` cover exactly those cases. SWR is better for a single latest-headlines hook because it is 5.3 KB versus TanStack Query's 16.2 KB. For a production news feed with infinite scroll, pick TanStack Query.

### How do I build a news feed in Next.js?

To build a news feed in Next.js 15, create an async Server Component that `await fetch()`s the news API server-side with your key in a server-only env var, then render the results as HTML. Add `next: { revalidate: 60 }` for ISR. For infinite scroll, expose a Route Handler at `/api/news` and consume it from a client component with TanStack Query's `useInfiniteQuery`.

### How do I hide my API key in React?

You hide your API key in React by keeping it in a server-only env var (no `NEXT_PUBLIC_` or `REACT_APP_` prefix) and calling the news API only from server code — a Next.js Route Handler, Remix loader, or a backend proxy. Anything prefixed `NEXT_PUBLIC_*` or `REACT_APP_*` gets inlined into the browser bundle and is visible in DevTools.

### Can React call News API directly?

No, React cannot safely call NewsAPI.org directly in 2026, because its CORS policy blocks non-localhost browser origins on the Developer plan, and the `?apiKey=` query-string auth would leak the key even if CORS allowed it. The correct pattern is a server-side proxy. Other vendors with header auth (APITube, NewsData.io, Perigon) are technically CORS-friendlier but should still be proxied in production for key security.


## Verdict

The best news API for React in 2026 is the one that plays well with React's default rendering model — server-first, proxy the key, let a hook library handle cache on the client. APITube, NewsData.io, and Perigon all qualify; NewsAPI.org doesn't without a paid tier and a proxy. Whatever you pick, the proxy pattern is non-negotiable and the hook choice (`useSWR` vs `useQuery`) follows from the feed complexity, not from which library is trendy this quarter.

APITube ships a clean REST API that works equally well in Server Components, Route Handlers, and browser fetches behind a proxy — `X-API-Key` header, SSE streaming, 30 req / 30 min on the free tier, commercial use allowed. apitube.io

Related reading: Best News API in 2026: Complete Comparison Guide · [Best News API for JavaScript & Node.js 2026](https://apitube.io/blog/best-news-api-javascript-nodejs-2026) · [Next.js `fetch` reference](https://nextjs.org/docs/app/api-reference/functions/fetch) · [TanStack Query docs](https://tanstack.com/query/latest/docs/framework/react/overview)


## Resources

- **APITube** — [apitube.io](https://apitube.io) — try it free, sentiment and entities included on every article
- **Documentation** — [docs.apitube.io](https://docs.apitube.io) — endpoints, parameters, response structure, integrations
- **Pricing** — [apitube.io/pricing](https://apitube.io/pricing) — all tiers
- **APITube blog** — [apitube.io/blog](https://apitube.io/blog) — more guides and comparisons

**Related guides:**
- [React News Dashboard Tutorial](https://apitube.io/blog/react-news-dashboard-tutorial)
- [Multilingual News App with Next.js](https://apitube.io/blog/nextjs-multilingual-news-app-tutorial)
- WebSocket News Alerts with Socket.IO
