Nextjs

Heroui image feature

HeroUI: The Modern React UI Framework You Need in 2025

In the ever-evolving React ecosystem, developers often struggle to choose the right UI library—one that is fast, accessible, modern, and developer-friendly. HeroUI, formerly known as NextUI, checks all those boxes and more. Officially rebranded in January 2025, HeroUI is quickly becoming the go-to choice for building beautiful and responsive interfaces with minimal effort. Backed by the power of Tailwind CSS and React Aria, HeroUI offers fully accessible and composable components built with performance in mind. Whether you’re building a dashboard, a marketing site, or a SaaS product, HeroUI’s modular design and rich theming options give you complete control. In this post, we’ll explore HeroUI’s key features, how to set it up, and why it might just be the best UI library for your next project. What is HeroUI? From NextUI to HeroUI HeroUI is a beautiful, fast, and modern React UI library designed to help developers build accessible and customizable web applications. It was formerly known as NextUI, and in January 2025, it underwent a strategic rebrand to better reflect its expanded capabilities and future direction. Built for the Future Key Features of HeroUI HeroUI vs Other UI Libraries HeroUI vs Material UI While Material UI offers a mature component ecosystem, it can feel rigid and comes with design opinions that are harder to override. HeroUI offers more flexibility through Tailwind, with less CSS bloat and faster customization. HeroUI vs Chakra UI Chakra UI emphasizes accessibility, like HeroUI. But HeroUI’s integration with Tailwind and its lightweight CLI tool gives it a performance edge—ideal for modern apps and frameworks like Next.js. Migrating from NextUI to HeroUI HeroUI is the spiritual successor to NextUI. Here’s what you need to know: Installing HeroUI Using the CLI The HeroUI CLI is the fastest way to get started. It handles everything from project scaffolding to component generation. Step 1: Install the CLI (Optional) You can install the CLI globally: npm install -g heroui Or use it directly via npx: npx heroui init Step 2: Initialize the Project Choose your preferred package manager: # pnpm pnpm dlx heroui init # npm npx heroui init # yarn yarn dlx heroui init # bun bunx heroui init You’ll be prompted to select: Step 3: Install Dependencies Once the setup is complete, install the dependencies: # pnpm pnpm install # npm npm install # yarn yarn install # bun bun install Step 4: Start the Development Server Run your project locally: # pnpm pnpm dev # npm npm run dev # yarn yarn dev # bun bun run dev Step 5: Add Components with the CLI Use the CLI to add components to your project: heroui add button Add multiple components: heroui add button card checkbox Or add all available components: heroui add –all If you omit the component name, the CLI launches an interactive menu: heroui add Example prompt: ? Which components would you like to add? ◯ accordion ◯ autocomplete ◯ avatar ◯ badge ◯ breadcrumbs ◉ button ◯ card ◯ checkbox ◯ chip ◯ code HeroUI in Action Here are just a few components you can start using immediately: Button Example import { Button } from “@heroui/react”; export default function Example() { return <Button color=”primary”>Click Me</Button>; } Modal Example import { Modal, useDisclosure } from “@heroui/react”; export default function ModalExample() { const { isOpen, onOpen, onClose } = useDisclosure(); return ( <> <Button onPress={onOpen}>Open Modal</Button> <Modal isOpen={isOpen} onClose={onClose}> <Modal.Content> <Modal.Header>Welcome</Modal.Header> <Modal.Body>Hello from HeroUI!</Modal.Body> </Modal.Content> </Modal> </> ); } Final Thoughts HeroUI brings together the best parts of Tailwind, accessibility, and developer-focused tooling in one elegant package. If you’re starting a new project in 2025 or looking to modernize an older one, HeroUI deserves a serious look. It’s ideal for: With its intuitive CLI, modular design, and commitment to best practices, HeroUI is ready for production and your next project.

HeroUI: The Modern React UI Framework You Need in 2025 Read More »

How to Create a Next.js MongoDB Todo Project: A Complete Guide

Introduction In today’s fast-paced development world, building a full-stack app doesn’t have to be complicated. With the power of Next.js and MongoDB, developers can quickly create scalable applications. In this tutorial, you will learn how to build a simple, full-stack To-Do App that leverages the App Router in Next.js and MongoDB for data persistence. Moreover, this app will be built using pure JavaScript (no TypeScript), making it perfect for beginners. By the end of this guide, you’ll understand how to set up a database, connect it using Mongoose, and build a frontend that interacts seamlessly with your backend. Prerequisites Before we begin, make sure you have: Project Overview We’ll build a simple but powerful To-Do App that includes: Choose: I have a Node.js version v22.17.0 npm Version 10.3.0 mongoose version v7.0.11 Step 1: Setting Up the Next.js Project First, let’s create a new Next.js project with JavaScript support: npx create-next-app@latest todo-project cd todo-project While running this command, it will ask you some questions about the dependency so that you can choose the option “No” for TypeScript and TailwindCSS Install additional dependencies we’ll need: Step 2: Setting Up MongoDB Connection Install MongoDB Community Edition locally and run the MongoDB service. Option A: MongoDB Atlas (Cloud) Option B: Local MongoDB Install MongoDB Community Edition locally and run the MongoDB service. Creating the Database Connection Utility Create a new file lib/mongodb.js: npm install mongoose I have created lib/mongob.js inside the src folder, and now paste this code import mongoose from ‘mongoose’ const MONGODB_URI = process.env.MONGODB_URI if (!MONGODB_URI) throw new Error(‘MONGODB_URI not defined in .env.local’) let cached = global.mongoose || { conn: null, promise: null } export async function connectDB() { if (cached.conn) return cached.conn if (!cached.promise) { cached.promise = mongoose.connect(MONGODB_URI, { dbName: ‘todo-app’, bufferCommands: false, }).then((mongoose) => { return mongoose }) } cached.conn = await cached.promise return cached.conn } Create a .env.local file in your src folder: MONGODB_URI=mongodb://<username>:<password>@localhost:27017/nextjsdb?authSource=admin Step 3: Creating the Todo Model Create a new directory models Inside the src and add Todo.js: import mongoose from ‘mongoose’ const TodoSchema = new mongoose.Schema({ text: { type: String, required: true }, completed: { type: Boolean, default: false } }, { timestamps: true }) export default mongoose.models.Todo || mongoose.model(‘Todo’, TodoSchema) Step 4: Creating API Routes For GET and POST requests, we use a single route.js file. For DELETE and PUT requests (which require dynamic parameters like an id We create a separate folder structure. This is because Next.js follows a file-based routing system, and each endpoint must have its own file or dynamic folder to handle different HTTP methods and routes correctly. GET & POST → /app/api/todos/route.js import { connectDB } from ‘@/lib/mongodb’ import Todo from ‘@/models/Todo’ export async function GET() { await connectDB() const todos = await Todo.find().sort({ createdAt: -1 }) return Response.json(todos) } export async function POST(req) { const { text } = await req.json() await connectDB() const newTodo = await Todo.create({ text }) return Response.json(newTodo) } Create the Folder path below. I have added PUT & DELETE → /app/api/todos/[id]/route.js Note: This would be a folder name [id] Do not confuse. import { connectDB } from ‘@/lib/mongodb’ import Todo from ‘@/models/Todo’ export async function PUT(req, { params }) { const { id } = params const { completed } = await req.json() await connectDB() const updated = await Todo.findByIdAndUpdate(id, { completed }, { new: true }) return Response.json(updated) } export async function DELETE(req, { params }) { const { id } = params await connectDB() await Todo.findByIdAndDelete(id) return Response.json({ message: ‘Deleted’ }) } Step 6: Build the UI — /app/page.js Now that our API is ready, let’s shift our focus to the front end. To begin with, we’ll build the user interface using React (via Next.js). This will include a task input field, a task list, and buttons to complete or delete a task. First, we define two state variables: task to hold the current input, and todos to store the fetched task list. After the component mounts, we use useEffect to fetch tasks from the API and display them on the screen. When a user adds a task, it is sent to the backend via a POST request. Then, the new task is added to the state and shown immediately in the list. In contrast, when a task is toggled or deleted, we use PUT and DELETE requests to update the backend accordingly. As a result, the interface remains synced with the database in real time. The file /app/page.js is the main UI page of your application — it’s where users: ‘use client’ What’s Happening in /app/page.js: This line tells Next.js that this file uses client-side rendering (since we use React hooks like useState and useEffect). React State const [task, setTask] = useState(”) const [todos, setTodos] = useState([]) Add a New Task const addTodo = async () => { if (!task.trim()) return const res = await fetch(‘/api/todos’, { method: ‘POST’, body: JSON.stringify({ text: task }), }) const newTodo = await res.json() setTodos([newTodo, …todos]) setTask(”) } Full code ‘use client’import { useEffect, useState } from ‘react’export default function Home() { const [task, setTask] = useState(”) const [todos, setTodos] = useState([]) useEffect(() => { fetchTodos() }, []) const fetchTodos = async () => { const res = await fetch(‘/api/todos’) const data = await res.json() setTodos(data) } const addTodo = async () => { if (!task.trim()) return const res = await fetch(‘/api/todos’, { method: ‘POST’, body: JSON.stringify({ text: task }), }) const newTodo = await res.json() setTodos([newTodo, …todos]) setTask(”) } const toggleComplete = async (id, completed) => { const res = await fetch(`/api/todos/${id}`, { method: ‘PUT’, body: JSON.stringify({ completed: !completed }) }) const updated = await res.json() setTodos(todos.map(todo => todo._id === id ? updated : todo)) } const deleteTodo = async (id) => { await fetch(`/api/todos/${id}`, { method: ‘DELETE’ }) setTodos(todos.filter(todo => todo._id !== id)) } return ( <main className=”min-h-screen p-6 bg-gray-100 flex flex-col items-center”> <h1 className=”text-2xl font-bold mb-4″>To-Do App</h1> <div className=”flex gap-2 mb-4″> <input type=”text” value={task} onChange={(e) => setTask(e.target.value)} placeholder=”Enter task…” className=”border p-2 rounded w-64″ /> <button onClick={addTodo} className=”bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600″ > Add </button> </div>

How to Create a Next.js MongoDB Todo Project: A Complete Guide Read More »

connecting nextjs project with mongodb blog post

How to Connect Next.js with MongoDB

MongoDB is a powerful NoSQL database that pairs perfectly with Next.js for full-stack applications. In this guide, you’ll learn how to connect Next.js to MongoDB (locally or with MongoDB Atlas) using Mongoose, and how to build simple API routes to insert and retrieve data. Prerequisites Before you begin, ensure you have the following installed: Create a Next.js app if needed: npx create-next-app@latest next-mongo-app cd next-mongo-app Although there is a small code change if you want to use TypeScript, I suggest using JavaScript for learning purposes. Step 1: Install Mongoose npm install mongoose Set the MongoDB URI in the .env.local file in your root directory MONGODB_URI=mongodb://<username>:<password>@localhost:27017/<databaseName>?authSource=admin //example MONGODB_URI=mongodb://admin:12345@localhost:27017/nextjsdb?authSource=admin Step 2: Set Up MongoDB Connection Helper Create a folder name lib and a file lib/mongodb.js: Make sure you are connected to the MongoDB database // lib/mongodb.js import mongoose from ‘mongoose’; const MONGODB_URI = process.env.MONGODB_URI; if (!MONGODB_URI) { throw new Error(‘Please define the MONGODB_URI environment variable’); } let cached = global.mongoose; if (!cached) { cached = global.mongoose = { conn: null, promise: null }; } export async function connectToDatabase() { if (cached.conn) return cached.conn; if (!cached.promise) { cached.promise = mongoose.connect(MONGODB_URI, { bufferCommands: false, useNewUrlParser: true, useUnifiedTopology: true, }).then((mongoose) => mongoose); } cached.conn = await cached.promise; return cached.conn; } Step 3: Define a Mongoose Model Create a folder models and a file models/Post.js // models/Post.js import mongoose from ‘mongoose’; const PostSchema = new mongoose.Schema({ title: String, content: String, }, { timestamps: true }); export default mongoose.models.Post || mongoose.model(‘Post’, PostSchema); Step 4: Create an API Route Create pages/api/posts.js: // pages/api/posts.js import { connectToDatabase } from ‘../../../lib/mongodb’; import Post from ‘../../../models/Post’; export default async function handler(req, res) { await connectToDatabase(); if (req.method === ‘GET’) { const posts = await Post.find({}); return res.status(200).json(posts); } if (req.method === ‘POST’) { const post = await Post.create(req.body); return res.status(201).json(post); } return res.status(405).json({ message: ‘Method not allowed’ }); } Step 5: Test with a Frontend Form Update pages/index.js With a simple form: This will show in the home URL / in the browser a simple form for inserting data into the database // pages/index.js or any component ‘use client’; // if using App Router import { useState } from ‘react’; export default function Home() { const [title, setTitle] = useState(”); const [content, setContent] = useState(”); async function handleSubmit(e) { e.preventDefault(); const res = await fetch(‘/api/posts’, { method: ‘POST’, headers: { ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ title, content }) }); const data = await res.json(); console.log(data); // Clear form after submit setTitle(”); setContent(”); } return ( <div style={{ maxWidth: 500, margin: ‘0 auto’ }}> <h1>Create Post</h1> <form onSubmit={handleSubmit}> <div> <label>Title:</label> <input type=”text” value={title} onChange={(e) => setTitle(e.target.value)} required style={{ width: ‘100%’, padding: ‘8px’, marginBottom: ’10px’ }} /> </div> <div> <label>Content:</label> <textarea value={content} onChange={(e) => setContent(e.target.value)} required rows={5} style={{ width: ‘100%’, padding: ‘8px’, marginBottom: ’10px’ }} ></textarea> </div> <button type=”submit”>Submit</button> </form> </div> ); } Folder Structure Overview Your folder structure should look the same. I have created: myproject/ ├── lib/ │ └── mongodb.js ├── models/ │ └── Post.js ├── pages/ │ ├── api/ │ │ └── posts.js │ └── index.js ├── .env.local └── … Api output should look like this : Comment below, let me know how you start your next JS journey

How to Connect Next.js with MongoDB Read More »

Scroll to Top