Build a Next.js Interactive Web-Based Task Management App

Written by

in

In today’s fast-paced world, staying organized is crucial. Whether you’re juggling personal errands, professional projects, or a mix of both, a well-structured task management system can be a lifesaver. While numerous apps exist, building your own offers a fantastic learning opportunity. This tutorial guides you through creating a simple yet effective task management application using Next.js, a powerful React framework, enabling you to learn and customize your own solution.

Why Build a Task Management App with Next.js?

Next.js provides several advantages for this project:

  • Server-Side Rendering (SSR) & Static Site Generation (SSG): Improves SEO and initial load times.
  • API Routes: Simplifies backend development within the same project.
  • React.js Ecosystem: Leverages the vast React library ecosystem for UI components and more.
  • Developer Experience: Excellent tooling, including hot reloading and built-in CSS support.

This project will not only teach you the fundamentals of Next.js but also introduce you to concepts like state management, form handling, and basic API interactions. The goal is to make a functional application that you can expand upon as your skills grow.

Project Setup: Getting Started

Let’s begin by setting up our Next.js project. Open your terminal and run the following command:

npx create-next-app task-manager-app

This command creates a new Next.js project named “task-manager-app”. Navigate into the project directory:

cd task-manager-app

Next, install any necessary dependencies. For this project, we will use a simple form library to handle form submissions and a library for local storage. Run:

npm install react-hook-form localforage

Now, let’s start the development server:

npm run dev

Your app should now be running locally, usually at http://localhost:3000. You can view the default Next.js welcome page.

Structuring the Application

Our task management app will have the following components:

  • Task Input Form: For adding new tasks.
  • Task List: Displays the tasks.
  • Task Item: Represents a single task.

We’ll organize our project with a “components” folder to hold these. Create the folder in the root directory.

Inside the “components” folder, create these files:

  • TaskInputForm.js
  • TaskList.js
  • TaskItem.js

Building the Task Input Form

Let’s start with the TaskInputForm.js component. This will allow users to enter new tasks.

Open components/TaskInputForm.js and add the following code:

import React from 'react';
import { useForm } from 'react-hook-form';

const TaskInputForm = ({ addTask }) => {
  const { register, handleSubmit, reset } = useForm();

  const onSubmit = (data) => {
    addTask(data.task);
    reset(); // Clear the form after submission
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="mb-4">
      <input
        type="text"
        {...register("task", { required: true })}
        placeholder="Add a task..."
        className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
      />
      <button
        type="submit"
        className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline mt-2"
      >
        Add Task
      </button>
    </form>
  );
};

export default TaskInputForm;

Explanation:

  • We import useForm from the react-hook-form library for form handling.
  • The addTask prop is passed down from the parent component (we’ll create this later) to handle adding the task.
  • register is used to register form fields.
  • handleSubmit handles form submission.
  • reset clears the form after submission.
  • We use Tailwind CSS classes for basic styling; you can customize these or use your preferred CSS method.

Creating the Task List

Now, let’s create the TaskList.js component to display the tasks. This component will receive an array of tasks and render each one.

Open components/TaskList.js and add the following code:

import React from 'react';
import TaskItem from './TaskItem';

const TaskList = ({ tasks, deleteTask, toggleTaskComplete }) => {
  return (
    <ul className="list-none p-0">
      {tasks.map((task) => (
        <TaskItem
          key={task.id}
          task={task}
          deleteTask={deleteTask}
          toggleTaskComplete={toggleTaskComplete}
        />
      ))}
    </ul>
  );
};

export default TaskList;

Explanation:

  • The component receives tasks, deleteTask, and toggleTaskComplete as props.
  • It maps through the tasks array and renders a TaskItem component for each task.

Building the Task Item Component

Next, create the TaskItem.js component, which represents a single task in the list.

Open components/TaskItem.js and add the following code:

import React from 'react';

const TaskItem = ({ task, deleteTask, toggleTaskComplete }) => {
  return (
    <li className="flex items-center justify-between py-2 border-b border-gray-200 last:border-none">
      <div className="flex items-center">
        <input
          type="checkbox"
          checked={task.completed}
          onChange={() => toggleTaskComplete(task.id)}
          className="mr-2"
        />
        <span className={task.completed ? "line-through text-gray-400" : ""}>
          {task.text}
        </span>
      </div>
      <button
        onClick={() => deleteTask(task.id)}
        className="bg-red-500 hover:bg-red-700 text-white font-bold py-1 px-2 rounded"
      >
        Delete
      </button>
    </li>
  );
};

export default TaskItem;

Explanation:

  • It receives a task object as a prop.
  • Displays the task text and a checkbox to mark the task as complete.
  • Includes a delete button.
  • Uses conditional styling (line-through) to indicate completed tasks.

Putting It All Together: The Index Page

Now, let’s integrate these components into our main page (pages/index.js).

Open pages/index.js and replace its content with the following code:

import React, { useState, useEffect } from 'react';
import TaskInputForm from '../components/TaskInputForm';
import TaskList from '../components/TaskList';
import localforage from 'localforage';

const Home = () => {
  const [tasks, setTasks] = useState([]);

  useEffect(() => {
    const loadTasks = async () => {
      const storedTasks = await localforage.getItem('tasks') || [];
      setTasks(storedTasks);
    };
    loadTasks();
  }, []);

  useEffect(() => {
    localforage.setItem('tasks', tasks);
  }, [tasks]);

  const addTask = (taskText) => {
    const newTask = {
      id: Date.now(), // Simple unique ID
      text: taskText,
      completed: false,
    };
    setTasks([...tasks, newTask]);
  };

  const deleteTask = (taskId) => {
    setTasks(tasks.filter((task) => task.id !== taskId));
  };

  const toggleTaskComplete = (taskId) => {
    setTasks(
      tasks.map((task) =>
        task.id === taskId ? { ...task, completed: !task.completed } : task
      )
    );
  };

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-2xl font-bold mb-4">Task Manager</h1>
      <TaskInputForm addTask={addTask} />
      <TaskList
        tasks={tasks}
        deleteTask={deleteTask}
        toggleTaskComplete={toggleTaskComplete}
      />
    </div>
  );
};

export default Home;

Explanation:

  • We import the components we created.
  • We use the useState hook to manage the tasks state.
  • useEffect hooks are used to load tasks from local storage on component mount and save tasks to local storage whenever the tasks state changes.
  • addTask function adds a new task to the tasks state. It creates a unique ID using Date.now().
  • deleteTask function removes a task based on its ID.
  • toggleTaskComplete function toggles the completion status of a task.
  • The components are rendered within a container, and the necessary props are passed to them.

Styling Your App

In this example, we’ve used basic Tailwind CSS classes for styling. However, you can use any styling method you prefer, such as:

  • CSS Modules: For component-specific styles.
  • Styled Components: For writing CSS-in-JS.
  • CSS-in-JS Libraries: Like Emotion or Radix UI.
  • Custom CSS files: For global styles.

To use Tailwind CSS, make sure you have it set up in your Next.js project. You can follow the official documentation for setup. Once installed, you can modify the classes in the code above, or create your own custom styles.

Testing Your Application

At this point, you should have a functional task management app. Test it out by:

  • Adding tasks.
  • Marking tasks as complete.
  • Deleting tasks.
  • Refreshing the page to ensure tasks are saved in local storage.

If everything works as expected, congratulations! You’ve successfully built a basic task management app.

Common Mistakes and Troubleshooting

Here are some common mistakes and how to fix them:

  • Tasks Not Saving: Make sure you’ve correctly implemented the local storage functionality using useEffect and localforage. Double-check the dependencies in your useEffect hooks.
  • Form Not Submitting: Ensure the handleSubmit function from react-hook-form is correctly used and that the input field is properly registered.
  • Incorrect Data Display: Verify the data being passed to the TaskList and TaskItem components. Use console.log() to debug and inspect the data.
  • Styling Issues: Check your CSS classes and ensure they are applied correctly. Use your browser’s developer tools to inspect the elements and see if the styles are being applied.

Enhancements and Next Steps

This is a starting point. Here are some ideas for enhancements:

  • Adding Task Categories: Allow users to categorize tasks (e.g., “Work,” “Personal”).
  • Implementing Task Due Dates: Add a date picker for task deadlines.
  • Adding Task Prioritization: Allow users to set task priorities (e.g., “High,” “Medium,” “Low”).
  • Using a Database: Instead of local storage, connect to a database like MongoDB or PostgreSQL to store tasks.
  • Adding Authentication: Allow users to create accounts and log in to manage their tasks securely.
  • Implementing Drag and Drop: Enable users to reorder tasks using drag-and-drop functionality.
  • Adding a Dark Mode: Allow users to switch between light and dark themes.
  • Adding a Mobile-Responsive Design: Ensure the app looks good on different screen sizes.

Summary/Key Takeaways

This tutorial has walked you through building a fundamental task management application with Next.js. We covered:

  • Setting up a Next.js project.
  • Creating reusable components.
  • Handling form submissions.
  • Managing state.
  • Using local storage for data persistence.

This project provides a solid foundation for understanding Next.js and React. The code can be expanded upon and customized to fit your needs. Remember to experiment with new features and explore the vast ecosystem of React libraries to enhance your app.

FAQ

1. Why use Next.js for this project?

Next.js offers benefits like server-side rendering, static site generation, and excellent developer experience, making it a great choice for building web applications, including this task manager.

2. How can I store tasks in a database instead of local storage?

You would need to install a database client library (e.g., for MongoDB or PostgreSQL), set up API routes in your Next.js project to handle database interactions (CRUD operations), and replace the local storage logic with database calls.

3. How do I deploy this app?

You can deploy your Next.js app to platforms like Vercel (recommended, as it’s built by the Next.js team), Netlify, or other hosting providers that support Node.js applications.

4. How can I add user authentication?

You would need to integrate a user authentication library or service (e.g., Auth0, Firebase Authentication) into your app, create user registration and login forms, and secure your API routes accordingly.

5. Can I use this code for commercial projects?

Yes, the code is provided as a learning resource and can be used in commercial projects. However, it’s recommended to add appropriate licenses and follow best practices for code maintainability and scalability.

Building a task management application is an excellent way to grasp the fundamentals of Next.js and React. By understanding the core concepts and the flow of data, you can create more complex and customized applications. The flexibility of Next.js allows for extensive customization, empowering you to tailor the application to your specific requirements. Continue to explore, experiment, and refine your skills, and you’ll find yourself capable of developing increasingly sophisticated web applications. The journey of a thousand lines of code begins with a single task added.