
Day 23 – Mini Blog Project in React (Part 1)
We’ve covered a lot of React concepts: props, hooks, forms, state management, routing, authentication. Now it’s time to bring them all together in a practical project.
👉 Over the next two blogs, we’ll build a Mini Blog Project in React.
- Part 1 (today): Setup, display blog list, show blog details, add blog, edit blog, delete blog.
- Part 2: Authentication (login/logout), protecting routes, and state management comparison (Context API, Redux Toolkit, Zustand).
We’ll use the modern Tailwind UI you’ve already seen (navbar, blog cards, footer) and turn it into a functional React project.
Step 1: Setup Project & Structure
We’ll use Vite for fast development.
npm create vite@latest mini-blog
This will ask you to choose many things like:
- Okay to Proceed (Y): Enter y
- Select a framework: Select React using Down and Up Arrow Key from the keyboard.
- After that you have to select a variant from many language, choose JavaScript.

Run below commands
cd mini-blog
npm install
npm install react-router-dom tailwindcss
npx tailwindcss init -p
Update vite.config.js
and index.css
with Tailwind setup.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [react(), tailwindcss()],
})
@import "tailwindcss";
👉 Remove other files and make Folder structure:
src/
├── components/
│ ├── Header.jsx
│ ├── Footer.jsx
│ ├── BlogCard.jsx
│
├── pages/
│ ├── Home.jsx
│ ├── BlogDetail.jsx
│ ├── AddBlog.jsx
│ ├── EditBlog.jsx
│
├── App.jsx
└── main.jsx
Step 2: Setup React Router
In App.jsx
:
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Header from "./components/Header";
import Footer from "./components/Footer";
import Home from "./pages/Home";
import BlogDetail from "./pages/BlogDetail";
import AddBlog from "./pages/AddBlog";
import EditBlog from "./pages/EditBlog";
function App() {
return (
<Router>
<Header />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/blog/:id" element={<BlogDetail />} />
<Route path="/add" element={<AddBlog />} />
<Route path="/edit/:id" element={<EditBlog />} />
</Routes>
<Footer />
</Router>
);
}
export default App;
👉 We now have routes for home, blog detail, add blog, and edit blog.
Now update your main.jsx
import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";
import "./index.css";
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Now make components files.
Start with Header.jsx
import React from "react";
import { Link } from "react-router-dom";
export default function Header() {
return (
<header className="bg-white shadow">
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
<Link to="/" className="flex items-center gap-3">
<div className="w-10 h-10 bg-gradient-to-br from-indigo-500 to-pink-500 rounded-full flex items-center justify-center text-white font-bold">
B
</div>
<span className="font-semibold text-lg">MiniBlog</span>
</Link>
<nav className="flex items-center gap-4">
<Link to="/" className="text-gray-700 hover:text-indigo-600">
Home
</Link>
<Link to="/add" className="text-gray-700 hover:text-indigo-600">
Add Blog
</Link>
{/* placeholder for future login / profile */}
<Link to="/login" className="px-3 py-1 rounded border border-gray-200 text-sm hover:bg-gray-50">
Sign In
</Link>
</nav>
</div>
</header>
);
}
Next create Footer.jsx
import React from "react";
export default function Footer() {
return (
<footer className="bg-gray-50 border-t mt-8">
<div className="container mx-auto px-4 py-6 text-center text-sm text-gray-600">
© {new Date().getFullYear()} MiniBlog • Built with React & Tailwind
</div>
</footer>
);
}
And next is BlogCard.jsx
component.
import React from "react";
export default function BlogCard({ id, title, body }) {
// small excerpt
const excerpt = body?.length > 120 ? `${body.slice(0, 120)}...` : body;
return (
<article className="bg-white rounded-lg shadow-sm overflow-hidden hover:shadow-md transition p-5 h-full flex flex-col">
<h3 className="text-lg font-semibold mb-2">{title}</h3>
<p className="text-gray-600 flex-1">{excerpt}</p>
<div className="mt-4 text-sm text-gray-400">Post ID: {id}</div>
</article>
);
}
Step 3: Blog List (Home Page)
We’ll fetch posts from JSONPlaceholder API.
pages/Home.jsx
:
import { useEffect, useState } from "react";
import { Link } from "react-router-dom";
import BlogCard from "../components/BlogCard";
function Home() {
const [blogs, setBlogs] = useState([]);
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts?_limit=6")
.then((res) => res.json())
.then((data) => setBlogs(data));
}, []);
return (
<div className="p-6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{blogs.map((blog) => (
<Link key={blog.id} to={`/blog/${blog.id}`}>
<BlogCard title={blog.title} body={blog.body} />
</Link>
))}
</div>
);
}
export default Home;
👉 BlogCard is just a styled component for displaying each blog.
Step 4: Blog Detail Page
pages/BlogDetail.jsx
:
import { useParams } from "react-router-dom";
import { useEffect, useState } from "react";
function BlogDetail() {
const { id } = useParams();
const [blog, setBlog] = useState(null);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/posts/${id}`)
.then((res) => res.json())
.then((data) => setBlog(data));
}, [id]);
if (!blog) return <p>Loading...</p>;
return (
<div className="p-6 max-w-2xl mx-auto">
<h1 className="text-2xl font-bold mb-4">{blog.title}</h1>
<p>{blog.body}</p>
</div>
);
}
export default BlogDetail;
Step 5: Add Blog (Form Handling)
pages/AddBlog.jsx
:
import { useState } from "react";
import { useNavigate } from "react-router-dom";
function AddBlog({ blogs, setBlogs }) {
const [title, setTitle] = useState("");
const [body, setBody] = useState("");
const navigate = useNavigate();
const handleSubmit = (e) => {
e.preventDefault();
const newBlog = { id: Date.now(), title, body };
setBlogs((prev) => [...prev, newBlog]);
navigate("/");
};
return (
<form onSubmit={handleSubmit} className="p-6 max-w-xl mx-auto space-y-4">
<input
type="text"
placeholder="Blog Title"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="w-full p-2 border rounded"
/>
<textarea
placeholder="Blog Content"
value={body}
onChange={(e) => setBody(e.target.value)}
className="w-full p-2 border rounded"
/>
<button type="submit" className="bg-blue-500 text-white px-4 py-2 rounded">
Add Blog
</button>
</form>
);
}
export default AddBlog;
👉 Controlled inputs with useState
update our blog list.
Step 6: Edit & Delete Blog
pages/EditBlog.jsx
:
import { useParams, useNavigate } from "react-router-dom";
import { useState } from "react";
function EditBlog({ blogs, setBlogs }) {
const { id } = useParams();
const navigate = useNavigate();
const blog = blogs.find((b) => b.id === Number(id));
const [title, setTitle] = useState(blog.title);
const [body, setBody] = useState(blog.body);
const handleSubmit = (e) => {
e.preventDefault();
setBlogs((prev) =>
prev.map((b) => (b.id === blog.id ? { ...b, title, body } : b))
);
navigate(`/blog/${id}`);
};
const handleDelete = () => {
setBlogs((prev) => prev.filter((b) => b.id !== blog.id));
navigate("/");
};
return (
<form onSubmit={handleSubmit} className="p-6 max-w-xl mx-auto space-y-4">
<input
type="text"
value={title}
onChange={(e) => setTitle(e.target.value)}
className="w-full p-2 border rounded"
/>
<textarea
value={body}
onChange={(e) => setBody(e.target.value)}
className="w-full p-2 border rounded"
/>
<button type="submit" className="bg-green-500 text-white px-4 py-2 rounded">
Save Changes
</button>
<button
type="button"
onClick={handleDelete}
className="bg-red-500 text-white px-4 py-2 rounded ml-2"
>
Delete
</button>
</form>
);
}
export default EditBlog;
Conclusion

In this blog, we built the foundation of our Mini Blog Project in React:
- Set up project with React Router + Tailwind.
- Display blog list from API.
- Blog detail page.
- Add, Edit, Delete blog (form handling & state updates).
👉 In Part 2 (Day 24), we’ll add:
- Authentication (Login/Logout flow)
- Protected Routes
- State management comparison (Context API, Redux Toolkit, Zustand).
That will complete our React 20-day learning journey with a real-world project. Click to find the Source code of Mini Blog Project in React.