Blog Template

A production-ready, full-stack blog platform built with Next.js, React 19, MongoDB, and Cloudinary. Ships with a complete admin dashboard, rich text editor, newsletter subscriptions, contact forms, and a fully customizable public blog interface.

Next.js 16.1.6 React 19 TypeScript MongoDB Tailwind CSS v4

Overview

Blog Template is a scalable, full-stack blog platform with a dual-interface architecture — a public-facing blog site and a private admin dashboard. It is built on the Next.js App Router with server components, server actions, and API routes. All data is stored in MongoDB, and media files are managed via Cloudinary CDN.

The template is designed to be purchased, configured, and deployed with minimal setup. The admin dashboard provides a complete content management system so non-technical users can manage all blog content without touching the codebase.

Features

Public Interface

🏠

Home Page

Hero banners, featured posts, editor picks, trending articles, category navigation

📄

Blog Posts

Full post pages with view tracking, estimated read time, related articles

🔍

Search

Full-text search across all published blog posts

📧

Newsletter

Email subscription modal with backend subscriber management

📬

Contact Form

Contact page with message submission and admin inbox

🌗

Dark / Light Theme

System-aware theme toggle with persistence

Admin Dashboard

📊

Dashboard Stats

Overview of blogs, categories, authors, subscribers, and messages

✍️

Blog Management

Create, edit, publish, draft, and delete posts with rich text editor

🗂️

Category Management

Drag-and-drop reordering, active/inactive toggle

👤

Author Management

Create and manage author profiles with avatar images

🖼️

Home Page Editor

Edit banners, taglines, images, and editor picks directly

⚙️

Global Settings

Company info, Cloudinary CDN, SEO metadata, terms & policy

Tech Stack

Category Technology Version
FrameworkNext.js (App Router)16.1.6
RuntimeReact19.1.0
LanguageTypeScript5.x
DatabaseMongoDB (Mongoose)8.19.3
AuthenticationNextAuth.js5.0.0-beta.30
Image CDNCloudinary2.8.0
StylingTailwind CSS4.x
UI PrimitivesShadcn/ui + Radix UILatest
State ManagementZustand5.0.8
FormsReact Hook Form7.62.0
ValidationZod4.1.5
Rich Text EditorSunEditor2.47.8
Drag and DropDnD Kit6.x / 10.x
TableTanStack Table8.21.3
IconsLucide React + React IconsLatest
NotificationsReact Hot Toast2.6.0
Date Utilitiesdate-fns, moment-timezoneLatest
Securitybcrypt, jsonwebtokenLatest

Requirements

Installation

  1. Extract or clone the project
    cd blog-template
  2. Install dependencies
    pnpm install
    # or
    npm install
  3. Create environment variables
    Create a .env.local file in the project root — see Environment Variables.
  4. Start development server
    pnpm dev
    Application runs at http://localhost:3000
  5. Login to admin dashboard
    Visit http://localhost:3000/admin/login with default credentials:
    Email: admin@example.com  |  Password: ChangeMe123!
  6. Configure Cloudinary
    Go to Settings → Cloudinary in the admin dashboard and enter your Cloudinary credentials.

Environment Variables

Create a .env.local file in the project root:

MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/<dbname>?retryWrites=true&w=majority
NEXTAUTH_SECRET=your_nextauth_secret_here
NEXTAUTH_URL=http://localhost:3000
JWT_SECRET=your_jwt_secret_here
MONGODB_URI
MongoDB connection string (Atlas or local). Required
NEXTAUTH_SECRET
Random secret for NextAuth.js session encryption. Generate with openssl rand -base64 32. Required
NEXTAUTH_URL
Full URL of the application. Use http://localhost:3000 for development. Required
JWT_SECRET
Secret for signing API JWT tokens. Generate with openssl rand -base64 32. Required
ℹ️
Note on Cloudinary

Cloudinary credentials are stored in MongoDB via the admin settings panel. They do not need to be added to .env.local.

Default Admin Credentials

A default admin user is automatically seeded into the database on first connection if no admin exists.

Email
admin@example.com
Password
ChangeMe123!
⚠️
Security Warning

Change the default admin password immediately after your first login. Navigate to Settings → Security in the admin dashboard.


Project Structure

blog-template/
├── app/                            # Next.js App Router root
│   ├── layout.tsx                  # Root layout
│   ├── globals.css                 # Global styles
│   ├── icon.tsx                    # App icon
│   ├── api/                        # API route handlers
│   │   ├── auth/[...nextauth]/     # NextAuth.js handler
│   │   ├── admin/                  # Protected admin APIs
│   │   │   ├── auth/               # Admin authentication
│   │   │   ├── blog/               # Blog CRUD
│   │   │   ├── category/           # Category CRUD
│   │   │   ├── author/             # Author CRUD
│   │   │   ├── contact-us/         # Contact messages
│   │   │   ├── feedback/           # Feedback management
│   │   │   ├── home-section/       # Home section editor
│   │   │   ├── settings/           # App settings
│   │   │   ├── subscribe/          # Subscribers list
│   │   │   └── dashboard/          # Dashboard stats
│   │   ├── blog/                   # Public blog APIs
│   │   ├── category/               # Public category API
│   │   ├── author/                 # Public author APIs
│   │   ├── banner/                 # Banner API
│   │   ├── contact-us/             # Contact form API
│   │   ├── feedback/               # Feedback API
│   │   ├── subscribe/              # Subscription API
│   │   ├── search/                 # Search API
│   │   ├── menu/                   # Navigation menu API
│   │   ├── home-section/           # Home section API
│   │   ├── settings/               # Public settings API
│   │   └── cache/                  # Cache invalidation
│   ├── (auth)/                     # Authentication pages (no layout)
│   │   └── admin/login/            # Admin login page
│   ├── (private)/                  # Protected admin interface
│   │   ├── layout.tsx              # Dashboard layout (auth guard)
│   │   └── admin/dashboard/        # Dashboard pages
│   │       ├── page.tsx            # Dashboard overview
│   │       ├── blogs/              # Blog management
│   │       ├── categories/         # Category management
│   │       ├── author/             # Author management
│   │       ├── settings/           # Global settings
│   │       ├── contact-us/         # Contact inbox
│   │       ├── feedback/           # Feedback list
│   │       ├── subscribe/          # Subscriber list
│   │       └── (showCase)/         # Homepage showcase editor
│   │           ├── banner-one/     # Banner 1 editor
│   │           ├── banner-two/     # Banner 2 editor
│   │           └── editor/         # Editor picks
│   ├── (public)/                   # Public-facing interface
│   │   ├── layout.tsx              # Public layout (header + footer)
│   │   ├── page.tsx                # Home page
│   │   ├── home2/                  # Alternative home
│   │   ├── about/                  # About page
│   │   ├── all-blogs/              # Blog listing
│   │   ├── blog-details/[slug]/    # Blog post detail
│   │   ├── lets-talk/              # Contact page
│   │   └── terms/                  # Terms & conditions
│   └── documentation/              # Documentation viewer route
│
├── actions/                        # Next.js Server Actions
│   ├── blog/                       # Blog mutations
│   ├── author/                     # Author mutations
│   ├── categories/                 # Category mutations
│   ├── settings/                   # Settings mutations
│   ├── subscribe/                  # Newsletter actions
│   ├── contactUs/                  # Contact form actions
│   ├── adminLogin/                 # Auth actions
│   └── profile/                    # Profile actions
│
├── components/                     # React components
│   ├── ui/                         # Shadcn/Radix UI primitives (30+ components)
│   ├── features/                   # Feature-specific components
│   │   └── landing/                # Public page sections
│   ├── form/                       # Form field components
│   │   ├── InputField.tsx
│   │   ├── PasswordField.tsx
│   │   ├── FileUploadComponent.tsx
│   │   └── PhoneNumber.tsx
│   ├── custom/                     # Custom business components
│   │   ├── FeedbackModal.tsx
│   │   ├── NewsletterModal.tsx
│   │   └── ContactUsForm.tsx
│   ├── shared/                     # Shared utility components
│   ├── hooks/                      # Component-level hooks
│   │   ├── useFilteredBlogPosts.tsx
│   │   └── useLoadMore.tsx
│   └── providers/                  # React providers
│       └── theme-provider.tsx
│
├── config/                         # Configuration modules
│   ├── database.ts                 # MongoDB connection + auto-seeding
│   ├── cloudinary.ts               # Cloudinary upload/delete utilities
│   ├── routes.ts                   # Route constants
│   ├── constant.ts                 # App constants (roles, cache keys)
│   └── cache.ts                    # In-memory cache config
│
├── hooks/                          # App-level custom hooks
│   └── use-mobile.ts
│
├── lib/                            # Utility functions
│   ├── utils.ts                    # General utilities (cn, etc.)
│   ├── api-client.ts               # HTTP client wrapper
│   ├── authenticate.ts             # Auth helpers
│   ├── validation-schema.ts        # Zod validation schemas
│   ├── types.ts                    # Shared TypeScript interfaces
│   ├── metadata.ts                 # SEO metadata builder
│   ├── date-format.ts              # Date formatting helpers
│   ├── mongo-adapter.ts            # MongoDB adapter
│   └── file-validator.ts           # File type/size validation
│
├── model/                          # Mongoose schemas & models
│   ├── Blog.ts                     # Blog schema
│   ├── Category.ts                 # Category schema
│   ├── User.ts                     # User/Author schema
│   ├── Settings.ts                 # App settings schema
│   ├── Contactus.ts                # Contact submissions schema
│   ├── Feedback.ts                 # Feedback schema
│   ├── Subscribe.ts                # Newsletter subscriber schema
│   └── HomeSection.ts              # Home section data schema
│
├── provider/                       # App-level providers
│   ├── AuthSessionProvider.tsx     # NextAuth session provider
│   ├── DashboardProvider.tsx       # Dashboard context
│   └── ThemeProvider.tsx           # Theme context
│
├── public/                         # Static assets
│   ├── fonts/                      # Custom web fonts
│   ├── images/                     # Static images
│   ├── sample-data/                # Sample data files
│   ├── documentation.html          # Standalone documentation
│   └── documentation.pdf           # PDF documentation
│
├── store/                          # Zustand state stores
│   ├── useBlogStore.ts
│   ├── useUserProfile.ts
│   ├── useLoadingStore.ts
│   └── useOutletStore.ts
│
├── types/                          # TypeScript type extensions
│   ├── next.d.ts
│   └── next-auth.d.ts
│
├── next.config.ts
├── tsconfig.json
├── postcss.config.mjs
├── components.json
└── package.json

Application Routes

Public Routes

RouteDescription
/Home page — hero banners, featured posts, categories
/home2Alternate home page layout
/aboutAbout the publication
/all-blogsPaginated blog listing with filters
/blog-details/[slug]Individual blog post with related posts
/lets-talkContact form page
/termsTerms & conditions and privacy policy
/documentationTemplate documentation viewer

Auth Routes

RouteDescription
/admin/loginAdmin login page

Admin Dashboard Routes (Protected)

RouteDescription
/admin/dashboardOverview — stats and quick links
/admin/dashboard/blogsBlog list with search and filters
/admin/dashboard/blogs/createCreate new blog post
/admin/dashboard/blogs/create/[id]Edit existing blog post
/admin/dashboard/categoriesCategory list with drag-and-drop sort
/admin/dashboard/authorAuthor management
/admin/dashboard/settingsGlobal settings
/admin/dashboard/contact-usInbox for contact form messages
/admin/dashboard/feedbackUser feedback submissions
/admin/dashboard/subscribeNewsletter subscriber list
/admin/dashboard/banner-oneHomepage hero banner 1 editor
/admin/dashboard/banner-twoHomepage hero banner 2 editor
/admin/dashboard/editorEditor's picks showcase editor

Authentication

The template uses NextAuth.js 5.x (beta) for session management alongside a custom JWT-based API authentication layer.

How It Works

  1. Admin visits /admin/login and submits credentials.
  2. NextAuth validates credentials against the MongoDB users collection.
  3. A session is created server-side; a signed JWT is also issued for API requests.
  4. All /api/admin/* routes verify the JWT from the Authorization: Bearer <token> header.
  5. Protected Next.js pages under (private) redirect unauthenticated users to /admin/login.

Security Details

Generating Secrets

openssl rand -base64 32

Run this command twice — once for NEXTAUTH_SECRET and once for JWT_SECRET.

State Management

The app uses Zustand for global client-side state.

StoreFilePurpose
useBlogStorestore/useBlogStore.tsBlog list, filters, pagination state
useUserProfilestore/useUserProfile.tsCurrent authenticated admin profile
useLoadingStorestore/useLoadingStore.tsGlobal loading indicator state
useOutletStorestore/useOutletStore.tsOutlet / location data

Image Management

All images are managed through Cloudinary CDN. Credentials are stored in MongoDB — no .env changes are needed for Cloudinary configuration.

Setup Steps

  1. Create a Cloudinary account at cloudinary.com.
  2. Copy your Cloud Name, API Key, and API Secret from the dashboard.
  3. In the admin dashboard, navigate to Settings → Cloudinary and enter your credentials.
  4. Set a Folder Name (e.g., blog-uploads) for organized media storage.

Supported Operations

OperationDescription
UploadBlog cover images, banners, author avatars, logo, favicon
DeleteSingle asset by Cloudinary public ID
Bulk deleteMultiple assets at once
Folder deleteDelete all assets in a folder including the folder itself
ℹ️
File Size Limit

Server Actions accept file uploads up to 50 MB (configured in next.config.ts via serverActions.bodySizeLimit).


API — Authentication

MethodEndpointDescriptionAuth
POST/api/admin/auth/loginAdmin login — returns JWTNo
GET/api/admin/auth/meGet current admin profileYes
PATCH/api/admin/auth/passwordChange admin passwordYes
GET / POST/api/auth/[...nextauth]NextAuth.js session handler

Login Request

POST /api/admin/auth/login

{
  "email": "admin@example.com",
  "password": "ChangeMe123!"
}

Login Response

{
  "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "user": {
    "_id": "...",
    "name": "Admin",
    "email": "admin@example.com",
    "role": "admin"
  }
}

API — Blogs

Admin (Protected)

MethodEndpointDescription
GET/api/admin/blogList all blogs with pagination and filters
POST/api/admin/blogCreate a new blog post
PUT/api/admin/blog/[id]Update a blog post by ID
DELETE/api/admin/blog/[id]Delete a blog post by ID
PATCH/api/admin/blog/status/[id]Toggle published / draft status

Create Blog Request

POST /api/admin/blog

{
  "title": "My Blog Post Title",
  "slug": "my-blog-post-title",
  "shortDescription": "A brief summary of the post",
  "description": "<p>Full HTML content from the rich text editor</p>",
  "readTime": "5 min read",
  "image": "https://res.cloudinary.com/cloud/image/upload/v1/...",
  "category": "60d5f9b0c9e77c001f3c9d34",
  "createdBy": "60d5f9b0c9e77c001f3c9d35",
  "isPopular": false,
  "isFeature": true,
  "status": true
}

Public

MethodEndpointQuery ParamsDescription
GET/api/blogpage, limit, categoryPaginated published blogs
GET/api/blog/allAll published blogs
GET/api/blog/trendingTrending blogs sorted by views
GET/api/blog/morepage, limitLoad more blogs for infinite scroll
GET/api/blog/[slug]Single blog post by URL slug
PATCH/api/blog/[slug]/viewsIncrement view counter
GET/api/blog/[slug]/relatedRelated posts in the same category

API — Categories

Admin (Protected)

MethodEndpointDescription
GET/api/admin/categoryList all categories
POST/api/admin/categoryCreate a category
PUT/api/admin/category/[id]Update a category
DELETE/api/admin/category/[id]Delete a category
PATCH/api/admin/category/status/[id]Toggle active / inactive
POST/api/admin/category/sortReorder categories by drag-and-drop

Sort Request

POST /api/admin/category/sort

{ "orderedIds": ["id1", "id2", "id3"] }

Public

MethodEndpointDescription
GET/api/categoryAll active categories

API — Authors

Admin (Protected)

MethodEndpointDescription
GET/api/admin/authorList all authors
POST/api/admin/authorCreate an author
PUT/api/admin/author/[id]Update an author
DELETE/api/admin/author/[id]Delete an author
GET/api/admin/author/profileCurrent authenticated author profile

Public

MethodEndpointDescription
GET/api/authorAll authors
GET/api/author/[id]Author profile by ID
GET/api/author/[id]/most-read-blogAuthor's most-read blog posts

API — Settings

Admin (Protected)

MethodEndpointDescription
GET/api/admin/settingsGet all settings
POST/api/admin/settings/generalUpdate general info (name, logo, socials)
POST/api/admin/settings/cloudinaryUpdate Cloudinary credentials
POST/api/admin/settings/metadataUpdate SEO metadata
POST/api/admin/settings/page-bannerUpdate page banner images
POST/api/admin/settings/business-hourUpdate business hours
POST/api/admin/settings/termsUpdate terms & privacy policy

Cloudinary Request Body

{
  "cloudName": "your-cloud-name",
  "apiKey": "123456789012345",
  "apiSecret": "your-api-secret",
  "folder": "blog-uploads",
  "secureUrlBase": "https://res.cloudinary.com/your-cloud-name"
}

SEO Metadata Request Body

{
  "title": "My Blog — Latest Articles",
  "applicationName": "My Blog",
  "description": "Explore insightful articles and stories.",
  "keywords": ["blog", "articles", "news"],
  "openGraphImage": "https://res.cloudinary.com/..."
}

Public

MethodEndpointDescription
GET/api/settingsPublic settings (name, logo, social links)

API — Contact & Feedback

MethodEndpointDescription
POST/api/contact-usSubmit a contact form message
GET/api/admin/contact-usList all contact messages
DELETE/api/admin/contact-usDelete contact messages
PUT/api/admin/contact-us/[id]Update message (mark as read)
POST/api/feedbackSubmit user feedback
GET/api/admin/feedbackList all feedback entries
PATCH/api/admin/feedback/[id]Update feedback status
DELETE/api/admin/feedbackDelete feedback entries

API — Newsletter

MethodEndpointDescription
POST/api/subscribeSubscribe with an email address
GET/api/admin/subscribeList all subscribers

Subscribe Request

POST /api/subscribe

{ "email": "subscriber@example.com" }

API — Banners & Home Sections

MethodEndpointDescription
GET/api/banner/[slug]Get banner data by slug identifier
GET/api/home-section/name/[slug]Get home section by slug
GET/api/admin/home-sectionList all home sections
POST/api/admin/home-sectionCreate or update a home section
GET/api/admin/home-section/name/[slug]Admin: get home section by slug

API — Menu, Dashboard & Cache

MethodEndpointDescription
GET/api/menuFull navigation menu structure
GET/api/menu/[slug]Single menu item by slug
GET/api/admin/dashboardAggregate stats for dashboard
POST/api/cacheInvalidate in-memory cache

Database — Blog Model

Collection: blogs

Blog → collection: blogs
title
String
Required. Post title.
slug
String
Required, unique. URL-friendly identifier.
shortDescription
String
Brief excerpt shown in post cards.
description
String
Full HTML content from SunEditor rich text editor.
readTime
String
Required. E.g. "5 min read".
image
String
Cloudinary CDN URL for cover image.
category
ObjectId
Reference to Category model.
createdBy
ObjectId
Reference to User (author) model.
isPopular
Boolean
Default: false. Marks post as trending/popular.
isFeature
Boolean
Default: false. Marks post as featured on home page.
views
Number
Default: 0. Auto-incremented on each post visit.
status
Boolean
Default: true. true = published, false = draft.
createdAt
Date
Auto-generated timestamp.
updatedAt
Date
Auto-updated timestamp.

Indexes: status+slug, status+isPopular, status+isFeature, status+title, status+category

Database — Category Model

Category → collection: categories
name
String
Display name of the category.
slug
String
URL-friendly identifier.
count
Number
Number of blogs assigned to this category.
status
Boolean
Active / inactive toggle.
position
Number
Sort order — updated by drag-and-drop reordering.

Database — User Model

User → collection: users
name
String
Display name.
email
String
Unique. Used for login.
password
String
bcrypt hashed (10 salt rounds).
role
String
"admin" or "user". admin = full access; user = author.

Database — Settings Model

Settings → collection: settings
general.companyName
String
Site / company name.
general.logo
String
Cloudinary URL for logo image.
general.favicon
String
Cloudinary URL for favicon.
general.supportEmail
String
Primary contact email address.
general.facebook
String
Social media profile URL.
cloudinary.*
Object
cloudName, apiKey, apiSecret, folder, secureUrlBase.
metadata.*
Object
title, description, keywords[], openGraphImage.
pageBanner.*
Object
Image URLs for static page banners.
businessHours
Array
dayOfWeek (0–6), openTime, closeTime (minutes), isClosed.
termsPolicy.terms
String
HTML content for Terms & Conditions.
termsPolicy.policy
String
HTML content for Privacy Policy.

Database — Other Models

Contact Us

ContactUs→ collection: contactus
name
String
Sender's name.
email
String
Sender's email address.
message
String
Message body.
createdAt
Date
Submission timestamp.

Feedback

Feedback→ collection: feedbacks
message
String
Feedback text.
status
String
E.g. "pending", "reviewed".
createdAt
Date
Submission timestamp.

Subscribe (Newsletter)

Subscribe→ collection: subscribes
email
String
Unique subscriber email address.
createdAt
Date
Subscription timestamp.

HomeSection

HomeSection→ collection: homesections
name
String
Slug identifier — e.g. "banner-one", "editor-picks".
data
Object
Flexible JSON payload — structure varies per section type.

Admin — First Time Setup

  1. Login at /admin/login with default credentials
  2. Settings → General — set company name, logo, favicon, social links
  3. Settings → Cloudinary — configure Cloudinary CDN credentials
  4. Settings → SEO Metadata — set page title, description, Open Graph image
  5. Create at least one Category before creating blog posts
  6. Create at least one Author before creating blog posts
  7. Create your first Blog Post

Admin — Creating Blog Posts

  1. Navigate to Blogs → Create New
  2. Enter the Title — the slug auto-generates but can be customized
  3. Add a Short Description — this appears in blog listing cards
  4. Write full content in the SunEditor rich text editor
  5. Upload a Cover Image — uploaded directly to Cloudinary
  6. Select a Category and Author
  7. Set Read Time (e.g., "5 min read")
  8. Toggle isFeature to show on home page featured section
  9. Toggle isPopular to show in trending section
  10. Click Publish for live or save as Draft

Admin — Managing Categories

Admin — Home Page Customization

Use the Showcase section in the admin sidebar to edit home page content:

SectionRouteDescription
Banner One/admin/dashboard/banner-onePrimary hero banner — tagline, heading, description, image
Banner Two/admin/dashboard/banner-twoSecondary hero banner — alternate layout
Editor Picks/admin/dashboard/editorCurated posts for the editor's picks section on home

Admin — Settings Guide

TabDescription
GeneralCompany name, contact info, logo, favicon, social media links
CloudinaryCDN credentials — Cloud Name, API Key, API Secret, Folder
SEO MetadataPage title, meta description, keywords, Open Graph image
Page BannersHero images for static internal pages
Business HoursOperating hours per day of the week (0 = Sunday to 6 = Saturday)
Terms & PolicyRich-text HTML content for Terms of Service and Privacy Policy pages
SecurityChange admin account password

Build & Production

Build

pnpm build

Uses Turbopack for fast production bundling (configured in package.json).

Start Production Server

pnpm start

Pre-Deploy Checklist

Deployment

Vercel (Recommended)

  1. Push project to GitHub, GitLab, or Bitbucket.
  2. Import at vercel.com.
  3. Set all environment variables in the Vercel project settings.
  4. Deploy — Vercel handles builds and CDN automatically.

Self-Hosted (Node.js + PM2)

pnpm build
pm2 start pnpm --name "blog-template" -- start

Run behind a reverse proxy (Nginx or Caddy) for SSL termination.

Docker

FROM node:20-alpine
WORKDIR /app
COPY . .
RUN npm install -g pnpm && pnpm install && pnpm build
EXPOSE 3000
CMD ["pnpm", "start"]

Credits

ToolURL
Next.jshttps://nextjs.org
Reacthttps://react.dev
MongoDBhttps://mongodb.com
Mongoosehttps://mongoosejs.com
NextAuth.jshttps://next-auth.js.org
Cloudinaryhttps://cloudinary.com
Tailwind CSShttps://tailwindcss.com
Shadcn/uihttps://ui.shadcn.com
Radix UIhttps://radix-ui.com
Zustandhttps://zustand-demo.pmnd.rs
React Hook Formhttps://react-hook-form.com
Zodhttps://zod.dev
SunEditorhttps://github.com/JiHong88/SunEditor
DnD Kithttps://dndkit.com
TanStack Tablehttps://tanstack.com/table
Lucide Iconshttps://lucide.dev