Next.js Tutorial: Building **Tech3Space** *A modern tech news aggregator using Server Components, Client Components, and Backend API connections*
Next.js Tutorial: Building Tech3Space
A modern tech news aggregator using Server Components, Client Components, and Backend API connections
Next.js version: 16.2+ (App Router – the default and recommended way in 2026).
We’ll build Tech3Space – a homepage that fetches tech “posts” from a backend API, displays them with a Server Component, and adds interactivity (like button) using a Client Component. You’ll also learn how to create your own API route as a backend endpoint.
Why This Stack?
- Server Components (default): Run on the server → secure API keys, direct data fetching, zero client JS for static parts.
- Client Components: Handle interactivity (state, events, browser APIs).
- Backend API connection: Use
fetchin Server Components (best practice) or create API routes (/api/...) for your own backend logic.
Step 1: Create the Project
Open your terminal and run:
npx create-next-app@latest tech3space
Prompt answers (recommended):
- Project name →
tech3space - TypeScript → Yes
- ESLint → Yes
- Tailwind CSS → Yes
- App Router → Yes (this is the default)
- Customize import alias → No
Then start the dev server:
cd tech3space
npm run dev
Open http://localhost:3000 – you should see the default Next.js page.
Step 2: Project Structure (App Router)
tech3space/
├── app/
│ ├── globals.css
│ ├── layout.tsx ← Root layout (Server Component)
│ └── page.tsx ← Homepage (Server Component by default)
├── components/ ← We'll create this
├── public/
└── package.json
Key rule: Any file inside app/ is a Server Component unless you add 'use client'; at the top.
Step 3: Server Component – Fetch Data from a Backend API
We’ll use the public JSONPlaceholder API as our “backend” (https://jsonplaceholder.typicode.com/posts). In a real app you would replace this with your own backend (e.g., Supabase, your REST/GraphQL API, etc.).
Replace app/page.tsx with this:
// app/page.tsx
import { Suspense } from 'react';
async function getPosts() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts', {
cache: 'force-cache', // Change to 'no-store' for real-time data
next: { revalidate: 3600 }, // Optional: ISR – revalidate every hour
});
if (!res.ok) throw new Error('Failed to fetch posts');
return res.json();
}
export default async function Home() {
const posts = await getPosts();
return (
<main className="min-h-screen bg-gray-50 py-12">
<div className="max-w-4xl mx-auto px-6">
<h1 className="text-5xl font-bold text-center mb-4 bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
Tech3Space
</h1>
<p className="text-center text-xl text-gray-600 mb-12">
Latest tech news • Powered by Next.js Server Components
</p>
<Suspense fallback={<div className="text-center">Loading posts...</div>}>
<div className="grid gap-8">
{posts.slice(0, 8).map((post: any) => (
<div
key={post.id}
className="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 hover:shadow-md transition"
>
<h2 className="text-2xl font-semibold mb-3 line-clamp-2">{post.title}</h2>
<p className="text-gray-600 line-clamp-3">{post.body}</p>
{/* Client Component will go here in next step */}
<div id={`post-${post.id}`} className="mt-6"></div>
</div>
))}
</div>
</Suspense>
</div>
</main>
);
}
What just happened?
asyncfunction → Server Component.fetchruns on the server (API keys stay secret).Suspenseenables streaming – the rest of the page loads instantly.
Step 4: Client Component – Add Interactivity
Create a new folder components and file LikeButton.tsx:
// components/LikeButton.tsx
'use client'; // ← This makes it a Client Component
import { useState } from 'react';
export default function LikeButton({ postId }: { postId: number }) {
const [likes, setLikes] = useState(42); // Demo state
return (
<button
onClick={() => setLikes(likes + 1)}
className="flex items-center gap-2 px-5 py-2 bg-red-50 hover:bg-red-100 text-red-600 rounded-2xl font-medium transition"
>
❤️ <span>{likes}</span>
</button>
);
}
Now import and use it inside the Server Component (update the post card in app/page.tsx):
// Inside the map loop, replace the <div id={`post-${post.id}`} ...>
import LikeButton from '@/components/LikeButton';
{/* ... inside the post card ... */}
<div className="mt-6 flex justify-end">
<LikeButton postId={post.id} />
</div>
Server → Client communication:
- You can pass props (numbers, strings, objects) from Server to Client.
- The Client Component only contains the interactive part → minimal JS bundle.
Step 5: Create Your Own Backend API Route
Sometimes you need your own backend endpoint (e.g., to proxy an external API, add authentication, or transform data).
Create app/api/posts/route.ts:
// app/api/posts/route.ts
import { NextResponse } from 'next/server';
export async function GET() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts');
const posts = await res.json();
// You can add auth, filtering, or database logic here
return NextResponse.json({
success: true,
data: posts.slice(0, 10),
source: 'Tech3Space internal API',
});
}
Test it:
- Visit http://localhost:3000/api/posts
- You now have a full backend endpoint!
You can call this route from anywhere:
const res = await fetch('http://localhost:3000/api/posts', { cache: 'no-store' });
Step 6: Bonus – Real Backend Tips (Production Ready)
| Scenario | Recommended Approach | Why? |
|---|---|---|
| Fetching public data | Server Component + fetch | Fast, cached, no client JS |
| User-specific / dynamic data | Server Component + cache: 'no-store' | Always fresh |
| Interactivity + client caching | Client Component + SWR or React Query | Optimistic updates |
| Protected API (secrets) | API Route or Server Component | Keys never reach browser |
| Environment variables | .env.local → process.env.API_SECRET | Secure |
Example using your own backend (add to .env.local):
NEXT_PUBLIC_API_URL=https://your-backend.com/api
Step 7: Run, Build & Deploy
npm run dev # Development
npm run build # Production build
npm run start # Run production build locally
Deploy (free & instant):
- Push to GitHub.
- Go to vercel.com → Import your repo.
- Vercel auto-detects Next.js and deploys with zero config.
Final Result
You now have:
- A Server Page (
page.tsx) that fetches data securely. - A Client Component (
LikeButton) for interactivity. - A custom API route acting as your backend.
Next steps to level up Tech3Space:
- Add a real database (Supabase / Prisma).
- Implement Server Actions for forms.
- Add authentication with NextAuth.js.
- Use
loading.tsxanderror.tsxfiles for better UX.
You’ve mastered the core of modern Next.js: Server Components + Client Components + Backend API connections.
Happy coding! 🚀
If you want the full GitHub repo or the next feature (search bar, dark mode, etc.), just ask!