Daily World News - Next.js, React, TypeScript, GNews API, TailwindCSS, Framer Motion, TanStack React Query, Context API Fundamental Project 6
A modern, full-featured news application that delivers live world headlines from thousands of sources. Built with Next.js 16, React 19, TypeScript, and the GNews API, it demonstrates server-side rendering (SSR), client-side data fetching, React Query, Context API, bookmarks with localStorage, and responsive design—ideal for learning and teaching modern web development.
- Live Demo: https://daily-world-news.vercel.app/
- Introduction
- Features
- Technology Stack
- Project Structure
- Installation & Setup
- Environment Variables
- How to Run
- Routes & Pages
- API Endpoints
- Components & Architecture
- Hooks & Data Fetching
- Context & State Management
- Libraries & Dependencies
- Reusing Components
- Keywords
- Implementation highlights
- Conclusion
- License
- Happy Coding!
News World is an educational news application that fetches real-time headlines from the GNews API. It combines server-side rendering for fast initial load with client-side interactivity for category switching, keyword search, and bookmarking. The project showcases Next.js App Router, React Server Components, API routes as a backend proxy, TanStack React Query for caching, and a clean component-based architecture—making it a practical reference for beginners and intermediate developers learning full-stack React development.
| Feature | Description |
|---|---|
| Category Headlines | Browse news by category: General, World, Business, Technology, Entertainment, Sports, Science, Health, Nation |
| Hero & Headlines Reel | Hero banner (responsive height) and infinite auto-scrolling film reel of articles on the home page |
| Keyword Search | Search across 80,000+ sources with "Search in", date range (From/To), and pagination; centered layout |
| Bookmarks | Save articles; counter badge in navbar; full article data stored; same badges as Home; centered empty state |
| Country & Language Filters | Filter headlines and search by 20+ countries and 15+ languages (NewsContext, shared across Home & Search) |
| Article Modal | Shadcn Dialog (90vw×90vh), theme-aware; source/date/lang/country badges; single body; Share, Bookmark, Read More |
| Refresh | Manually refresh headlines with a loading indicator |
| Theme Toggle | Light/dark mode; consistent theme-aware UI; scrollbars and inputs adapt |
| Responsive Design | Mobile-first; Search/Bookmarks/About centered (max-w-7xl); Navbar border inset (mx-4) |
| Skeleton Loading | Animated skeletons; hero/reel use mounted guard to avoid hydration mismatch |
| SEO Metadata | Title, description, author, Open Graph, Twitter cards |
| Image Proxy | First-party /api/image; 4xx/5xx → placeholder; avoids ad blocker blocking |
| Category Icons | Lucide icons next to each category in the sidebar |
| Unified Data Flow | Same Article shape and NewsModal on Home, Search, Bookmarks; single hooks/context pattern |
| Technology | Purpose |
|---|---|
| Next.js 16 | React framework with App Router, API routes, SSR |
| React 19 | UI library with hooks and server components |
| TypeScript | Type safety and better DX |
| Tailwind CSS 4 | Utility-first styling |
| TanStack React Query | Server state, caching, and invalidation |
| Framer Motion | Animations and transitions |
| Lucide React | Icons |
| GNews API | News data provider (headlines + search) |
| ESLint | Code quality and linting |
news-world/
├── app/
│ ├── api/
│ │ ├── headlines/route.ts # GET /api/headlines - Top headlines proxy
│ │ ├── image/route.ts # GET /api/image - Image proxy (avoids ad blocker blocking)
│ │ └── search/route.ts # GET /api/search - Search proxy
│ ├── about/page.tsx # About page
│ ├── bookmarks/page.tsx # Bookmarks page
│ ├── search/page.tsx # Search page
│ ├── globals.css # Global styles
│ ├── layout.tsx # Root layout, metadata, providers
│ └── page.tsx # Home page (SSR initial fetch)
├── components/
│ ├── pages/
│ │ ├── HomePage.tsx # Home page content
│ │ ├── SearchPage.tsx # Search page (centered form, empty state with icons)
│ │ ├── BookmarksPage.tsx # Bookmarks page (centered, empty state with icons)
│ │ └── AboutPage.tsx # About page (Framer Motion staggered cards)
│ ├── ui/
│ │ ├── ArticleCard.tsx # Article card with badges, bookmark button
│ │ ├── ArticleCardSkeleton.tsx
│ │ ├── Navbar.tsx # Top navbar; border-b + mx-4 for inset line
│ │ ├── Footer.tsx # Reusable footer
│ │ ├── SearchBar.tsx # Theme-aware search input (Lucide Search)
│ │ ├── SearchResultsSkeleton.tsx
│ │ ├── NewsGridSkeleton.tsx
│ │ └── ... # Button, Badge, Skeleton, Dialog, etc.
│ ├── NewsSection.tsx # Home: header, sidebar, hero, reel, grid, footer
│ ├── NewsSidebar.tsx # Category sidebar with Lucide icons
│ ├── HeroBanner.tsx # Hero carousel (mounted guard for hydration)
│ ├── BannerSlider.tsx # Infinite film reel (mounted guard)
│ ├── NewsGrid.tsx # Article grid layout
│ ├── NewsModal.tsx # Shadcn Dialog article detail (90vw×90vh)
│ ├── ThemeToggle.tsx # Light/dark theme
│ └── providers/
│ ├── QueryProvider.tsx # React Query client
│ └── InvalidationProvider.tsx
├── context/
│ ├── NewsContext.tsx # Country/lang filters, query invalidation
│ └── BookmarkContext.tsx # Bookmark state, localStorage sync
├── hooks/
│ ├── useNews.ts # Headlines query
│ ├── useSearch.ts # Search query
│ ├── useRefreshNews.ts # Manual refresh
│ └── useBookmarks.ts # (optional) bookmark helpers
├── lib/
│ ├── gnews.ts # GNews API fetchers (server-side)
│ ├── api.ts # Client-side fetch wrappers
│ ├── imageProxy.ts # getProxiedImageUrl() - proxy external images
│ ├── queryKeys.ts # React Query key factory
│ ├── queryClient.ts # Query client config
│ ├── invalidateNews.ts # Invalidation helpers
│ └── utils.ts # cn(), etc.
├── data/
│ ├── categories.ts # GNews category list
│ ├── categoryIcons.tsx # Lucide icon map per category
│ ├── countries.ts # Country codes for filters
│ └── languages.ts # Language codes for filters
├── types/
│ └── news.ts # Article, GNewsResponse, params
├── public/
│ ├── favicon.ico
│ └── images/ # Placeholder images
├── .env.example # Environment variable template
├── next.config.ts
├── tailwind.config.ts
├── tsconfig.json
└── package.json- Node.js 20.x or later (nodejs.org)
- npm (comes with Node.js)
-
Clone the repository
git clone https://github.com/arnobt78/News--ReactVite.git cd news-world -
Install dependencies
npm install
-
Configure environment variables (see Environment Variables)
-
Run the development server
npm run dev
-
Open http://localhost:3000 in your browser.
You do not need any environment variables to run the app locally. The app will start and render, but news data will not load without a GNews API key.
To enable live news:
| Variable | Required | Description |
|---|---|---|
GNEWS_API_KEY |
Optional (for data) | Your GNews API key from gnews.io |
-
Go to gnews.io
-
Sign up for a free account
-
Copy your API key from the dashboard
-
Create a
.env.localfile in the project root:GNEWS_API_KEY=your_api_key_here
-
Restart the dev server (
npm run dev)
Note: The free tier allows ~100 requests per day. For production (e.g. Vercel), add GNEWS_API_KEY in your project's Environment Variables. If you see 403 from the API, check that the key is valid and not expired; 403 can also indicate rate limit or plan restrictions.
The project includes .env.example as a template:
# Get your GNews API key from https://gnews.io/docs/v4#authentication
GNEWS_API_KEY=your_gnews_api_key_hereCopy to .env.local and replace with your key.
| Command | Description |
|---|---|
npm run dev |
Start development server (Turbopack) at http://localhost:3000 |
npm run build |
Create production build |
npm run start |
Run production server (after npm run build) |
npm run lint |
Run ESLint |
npm run lint:fix |
Run ESLint with auto-fix |
| Route | Page | Description |
|---|---|---|
/ |
Home | Category headlines, sidebar, article grid |
/search |
Search | Keyword search with filters |
/bookmarks |
Bookmarks | Saved articles from localStorage |
/about |
About | Project info, tech stack, features |
All pages share the same header (Navbar), footer, and layout structure. The home page uses a sidebar for categories plus hero and reel; Search, Bookmarks, and About use a centered single-column layout (max-w-7xl) with consistent padding and empty states.
The app uses Next.js API routes as a backend proxy to the GNews API. This keeps the API key server-side and avoids CORS when fetching from the client.
Fetches top headlines from GNews.
Query parameters:
| Param | Type | Default | Description |
|---|---|---|---|
category |
string | general |
One of: general, world, business, technology, entertainment, sports, science, health, nation |
country |
string | - | Country code (e.g. us, gb, in) |
lang |
string | en |
Language code (e.g. en, es, fr) |
max |
number | 10 | Max articles to return |
page |
number | 1 | Page number |
nullable |
string | - | Optional GNews param |
truncate |
string | - | content to truncate article content |
Example:
GET /api/headlines?category=technology&country=us&max=10Searches news articles by keyword.
Query parameters:
| Param | Type | Required | Description |
|---|---|---|---|
q |
string | Yes | Search query |
country |
string | No | Country filter |
lang |
string | No | Language filter |
max |
number | No | Max results |
page |
number | No | Page number |
sortby |
string | No | publishedAt or relevance |
in |
string | No | Search in: title, description, content, or comma-separated |
from |
string | No | Start date (ISO) |
to |
string | No | End date (ISO) |
nullable |
string | No | Optional GNews param |
truncate |
string | No | content to truncate |
Example:
GET /api/search?q=technology&lang=en&page=1&in=title,descriptionFirst-party image proxy. Fetches external images server-side and streams to the client so ad blockers (which block third-party media) do not affect thumbnails. On upstream 4xx/5xx or invalid URL, redirects to /images/no-img.png so the UI shows a placeholder without console errors. SSRF-safe (only http/https, no localhost or private IPs). Configured in next.config.ts with Cache-Control headers.
Query parameters:
| Param | Type | Required | Description |
|---|---|---|---|
url |
string | Yes | Full URL of the image to fetch |
Example:
GET /api/image?url=https://media.cnn.com/.../image.jpg- HomePage – Renders
NewsSectionwith SSR initial articles - SearchPage – Centered search form (theme-aware SearchBar, Search in, From/To dates), "Browse headlines to bookmark" link, empty state with Lucide icons; results grid with pagination
- BookmarksPage – Centered layout; grid of bookmarked articles (same ArticleCard badges as Home); empty state with Lucide icons and "Browse headlines to bookmark"
- AboutPage – Client component; "Learn the basics" header + grid of feature cards with Lucide icons; Framer Motion staggered card animations; same max-w-7xl centered layout as Search/Bookmarks
- NewsSection – Home layout: header, NewsSidebar, scrollable main (HeroBanner, Headlines reel, NewsGrid), footer; scroll to top on category change
- Navbar – Top bar: logo, country/lang filters, refresh, theme toggle, nav links, bookmark count badge; border-b with mx-4 for inset bottom line
- NewsSidebar – Category list with Lucide icons per category
- HeroBanner – Hero carousel (responsive height); mounted guard for hydration
- BannerSlider – Infinite horizontal reel of article cards; mounted guard for hydration
- Footer – Copyright and links
- ArticleCard – Image, title, badges (source, date, lang, country), bookmark button; used on Home, Search, Bookmarks
- NewsGrid – Grid of ArticleCards
- NewsModal – Shadcn Dialog (90vw×90vh); theme-aware badges and text; single body (description or content); Share, Bookmark, Read More; same component on Home, Search, Bookmarks
- SearchBar – Theme-aware input with Lucide Search icon; debounced
- ThemeToggle – Light/dark theme switch
- Home:
app/page.tsx(server) fetches initial headlines →HomePage→NewsSectionusesuseNewsfor category switching; sameArticlepassed toNewsModal - Search:
SearchPageusesuseSearch(query, params)with NewsContext filters; "Search in", From/To; fullArticletoNewsModal - Bookmarks:
BookmarkContextstores full article data (including description, content);BookmarksPageconverts toArticleand uses sameNewsModal
Fetches headlines for a category. Uses React Query with optional SSR initial data for "general".
const { headline, news, loading, error, refetch } = useNews(
"technology",
undefined,
{ country: "us", lang: "en", max: 10 },
);Fetches search results. Enabled only when query is non-empty.
const { articles, totalArticles, loading, error, page, setPage } = useSearch(
"climate change",
{ country: "us", lang: "en" },
);Returns { refresh, isRefreshing } to manually refetch headlines and invalidate queries.
Returns { filters, setCountry, setLang } for country and language filters.
Returns { bookmarkedArticles, toggleBookmark, isBookmarked } for bookmark state.
- Purpose: Global country and language filters
- Values:
filters,setCountry,setLang - Behavior: Changing filters invalidates React Query cache for headlines and search
- Purpose: Persist bookmarked articles in
localStoragewith full article data for modal parity - Stored: url, title, image, publishedAt, source (name, country), lang, description, content
- Values:
bookmarkedArticles,toggleBookmark,isBookmarked - Storage key:
news-world-bookmarks
- next – React framework with App Router, API routes, SSR
- react / react-dom – UI library
- typescript – Type checking
- @tanstack/react-query – Server state, caching, background refetch
- Context API – Filters and bookmarks
- tailwindcss – Utility CSS
- framer-motion – Animations
- lucide-react – Icons
- class-variance-authority, clsx, tailwind-merge – Conditional styling
- lib/utils.ts –
cn()for merging class names
Use in any grid to display an article:
import ArticleCard from "@/components/ui/ArticleCard";
<ArticleCard
article={article}
onClick={() => handleClick(article)}
index={0}
/>;Drop-in top navbar for any page:
import Navbar from "@/components/ui/Navbar";
<header>
<Navbar />
</header>;Reusable footer:
import Footer from "@/components/ui/Footer";
<Footer />;Use in other projects by copying the hooks and ensuring /api/headlines and /api/search exist or are replaced with your own API.
Next.js 16, React 19, TypeScript, Tailwind CSS, GNews API, News App, SSR, API Routes, Image Proxy, React Query, TanStack Query, Context API, localStorage, Bookmarks, Shadcn UI, Lucide Icons, Hero Banner, Light/Dark Theme, Responsive Design, Framer Motion, Hydration-Safe, Educational Project
- Hero & reel: HeroBanner (responsive
h-64 sm:h-96 md:h-140) and BannerSlider infinite film reel; both use a mounted guard so SSR and client render the same placeholder, avoiding hydration mismatch. - Theme: Light/dark mode with theme-aware classes everywhere (no hardcoded dark colors); inline script in layout for instant theme apply; scrollbars and form inputs (SearchBar, date inputs with
color-scheme: darkin dark mode) adapt. - Navbar: Single place for the bottom border;
mx-4so the line is inset; same header wrapper on all pages (no border on page headers). - Search page: Centered search block; theme-aware SearchBar (Lucide Search icon); "Search in" and From/To date filters; pagination; "Browse headlines to bookmark" link; empty state with Lucide icons, x-y centered.
- Bookmarks page: Centered layout; bookmark counter in navbar; same badges on cards as Home (source, date, lang, country); full article data (description, content) stored so the modal shows the same content; empty state with Lucide icons.
- Article modal: Shadcn Dialog at 90vw×90vh; theme-aware text and badges; single body block (no duplicate description + content); GNews “[N chars]” suffix stripped; ring-0 for clean edges; same component and Article shape on Home, Search, Bookmarks.
- Image proxy: First-party only; 4xx/5xx redirect to placeholder (no 401/400 in console); browser-like User-Agent; documented in
next.config.ts. - About page: Client component with Framer Motion; staggered card animations; same centered layout (max-w-7xl) and hover styling as other pages.
News World is a practical example of a modern full-stack React application. It combines:
- Server-side rendering for fast initial load
- Client-side fetching for category switching and search
- API routes as a secure backend proxy (headlines, search, image proxy)
- React Query for caching and invalidation
- Context for filters and bookmarks
- localStorage for persistence
The codebase is structured for clarity and reuse. You can extend it with more categories, pagination, or different news APIs. Contributions and feedback are welcome.
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
This is an open-source project - feel free to use, enhance, and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://www.arnobmahmud.com.
Enjoy building and learning! 🚀
Thank you! 😊



